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 = self.get_visible_things()
84 offset = self.get_surroundings_offset()
86 for t in visible_things:
87 if t.type_ == 'human':
88 target = (t.position[1][0] - offset[0],
89 t.position[1][1] - offset[1])
91 if target is not None:
93 offset_self_pos = (self.position[1][0] - offset[0],
94 self.position[1][1] - offset[1])
95 target_dir = self.move_on_dijkstra_map(offset_self_pos,
97 if target_dir is not None:
98 self.set_task('MOVE', (target_dir,))
104 def hunt_food_satisfaction(self):
105 for id_ in self.inventory:
106 t = self.world.get_thing(id_)
107 if t.type_ == 'food':
108 self.set_task('EAT', (id_,))
110 for id_ in self.get_pickable_items():
111 t = self.world.get_thing(id_)
112 if t.type_ == 'food':
113 self.set_task('PICKUP', (id_,))
115 visible_things = self.get_visible_things()
116 offset = self.get_surroundings_offset()
118 for t in visible_things:
119 if t.type_ == 'food':
120 food_targets += [(t.position[1][0] - offset[0],
121 t.position[1][1] - offset[1])]
122 offset_self_pos = (self.position[1][0] - offset[0],
123 self.position[1][1] - offset[1])
124 target_dir = self.move_on_dijkstra_map(offset_self_pos,
128 self.set_task('MOVE', (target_dir,))
134 def decide_task(self):
135 #if not self.hunt_player():
136 if not self.hunt_food_satisfaction():
137 self.set_task('WAIT')
139 def set_task(self, task_name, args=()):
140 task_class = self.world.game.tasks[task_name]
141 self.task = task_class(self, args)
142 self.task.check() # will throw GameError if necessary
144 def proceed(self, is_AI=True):
145 """Further the thing in its tasks, decrease its health.
147 First, ensures an empty map, decrements .health and kills
148 thing if crossing zero (removes from self.world.things for AI
149 thing, or unsets self.world.player_is_alive for player thing);
150 then checks that self.task is still possible and aborts if
151 otherwise (for AI things, decides a new task).
153 Then decrements .task.todo; if it thus falls to <= 0, enacts
154 method whose name is 'task_' + self.task.name and sets .task =
155 None. If is_AI, calls .decide_task to decide a self.task.
158 self.unset_surroundings()
161 if self is self.world.player:
162 self.world.player_is_alive = False
164 del self.world.things[self.world.things.index(self)]
168 except GameError as e:
170 self._last_task_result = e
175 self.set_task('WAIT')
178 if self.task.todo <= 0:
179 self._last_task_result = self.task.do()
181 if is_AI and self.task is None:
185 self.set_task('WAIT')
187 def unset_surroundings(self):
189 self._surrounding_map = None
190 self._surroundings_offset = None
192 def must_fix_indentation(self):
193 return self._radius % 2 != self.position[1][0] % 2
195 def get_surroundings_offset(self):
196 if self._surroundings_offset is not None:
197 return self._surroundings_offset
198 add_line = self.must_fix_indentation()
199 offset_y = self.position[1][0] - self._radius - int(add_line)
200 offset_x = self.position[1][1] - self._radius
201 self._surroundings_offset = (offset_y, offset_x)
202 return self._surroundings_offset
204 def get_surrounding_map(self):
205 if self._surrounding_map is not None:
206 return self._surrounding_map
208 def pan_and_scan(size_of_axis, pos, offset):
210 small_pos = pos + offset
213 small_pos = size_of_axis + small_pos
214 elif small_pos >= size_of_axis:
216 small_pos = small_pos - size_of_axis
217 return big_pos, small_pos
219 add_line = self.must_fix_indentation()
220 self._surrounding_map = self.world.game.\
221 map_type(size=(self._radius*2+1+int(add_line),
223 size = self.world.maps[(0,0)].size
224 offset = self.get_surroundings_offset()
225 for pos in self._surrounding_map:
226 big_y, small_y = pan_and_scan(size[0], pos[0], offset[0])
227 big_x, small_x = pan_and_scan(size[1], pos[1], offset[1])
228 big_yx = (big_y, big_x)
229 small_yx = (small_y, small_x)
230 self._surrounding_map[pos] = self.world.maps[big_yx][small_yx]
231 return self._surrounding_map
233 def get_stencil(self):
234 if self._stencil is not None:
236 surrounding_map = self.get_surrounding_map()
237 m = surrounding_map.new_from_shape(' ')
238 for pos in surrounding_map:
239 if surrounding_map[pos] in {'.', '~'}:
241 offset = self.get_surroundings_offset()
242 fov_center = (self.position[1][0] - offset[0],
243 self.position[1][1] - offset[1])
244 self._stencil = m.get_fov_map(fov_center)
247 def get_visible_map(self):
248 stencil = self.get_stencil()
249 m = self.get_surrounding_map().new_from_shape(' ')
251 if stencil[pos] == '.':
252 m[pos] = self._surrounding_map[pos]
255 def get_visible_things(self):
257 def calc_pos_in_fov(big_pos, small_pos, offset, size_of_axis):
258 pos = small_pos - offset
260 pos = small_pos - size_of_axis - offset
262 pos = small_pos + size_of_axis - offset
265 stencil = self.get_stencil()
266 offset = self.get_surroundings_offset()
268 size = self.world.maps[(0,0)].size
269 fov_size = self.get_surrounding_map().size
270 for thing in self.world.things:
271 big_pos = thing.position[0]
272 small_pos = thing.position[1]
273 pos_y = calc_pos_in_fov(big_pos[0], small_pos[0], offset[0], size[0])
274 pos_x = calc_pos_in_fov(big_pos[1], small_pos[1], offset[1], size[1])
275 if pos_y < 0 or pos_x < 0 or pos_y >= fov_size[0] or pos_x >= fov_size[1]:
277 if (not thing.in_inventory) and stencil[(pos_y, pos_x)] == '.':
278 visible_things += [thing]
279 return visible_things
281 def get_pickable_items(self):
283 visible_things = self.get_visible_things()
284 for t in [t for t in visible_things if
285 isinstance(t, ThingItem) and
286 (t.position == self.position or
288 self.world.maps[(0,0)].get_neighbors(self.position[1]).values())]:
289 pickable_ids += [t.id_]
294 class ThingHuman(ThingAnimate):
300 class ThingMonster(ThingAnimate):