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
205 offset = self.get_surroundings_offset()
206 add_line = self.must_fix_indentation()
207 self._surrounding_map = self.world.game.\
208 map_type(size=(self._radius*2+1+int(add_line),
210 for pos in self._surrounding_map:
211 offset_pos = (pos[0] + offset[0], pos[1] + offset[1])
212 if offset_pos[0] >= 0 and \
213 offset_pos[0] < self.world.maps[(0,0)].size[0] and \
214 offset_pos[1] >= 0 and \
215 offset_pos[1] < self.world.maps[(0,0)].size[1]:
216 self._surrounding_map[pos] = self.world.maps[(0,0)][offset_pos]
217 return self._surrounding_map
219 def get_stencil(self):
220 if self._stencil is not None:
222 m = self.get_surrounding_map()
223 offset = self.get_surroundings_offset()
224 fov_center = (self.position[1][0] - offset[0],
225 self.position[1][1] - offset[1])
226 self._stencil = m.get_fov_map(fov_center)
229 def get_visible_map(self):
230 stencil = self.get_stencil()
231 m = self.get_surrounding_map().new_from_shape(' ')
233 if stencil[pos] == '.':
234 m[pos] = self._surrounding_map[pos]
237 def get_visible_things(self):
238 stencil = self.get_stencil()
239 offset = self.get_surroundings_offset()
241 for thing in self.world.things:
242 if abs(thing.position[1][0] - self.position[1][0]) > self._radius or\
243 abs(thing.position[1][1] - self.position[1][1]) > self._radius:
245 offset_pos = (thing.position[1][0] - offset[0],
246 thing.position[1][1] - offset[1])
247 if (not thing.in_inventory) and stencil[offset_pos] == '.':
248 visible_things += [thing]
249 return visible_things, offset
251 def get_pickable_items(self):
253 visible_things, _ = self.get_visible_things()
254 for t in [t for t in visible_things if
255 isinstance(t, ThingItem) and
256 (t.position == self.position or
258 self.world.maps[(0,0)].get_neighbors(self.position[1]).values())]:
259 pickable_ids += [t.id_]
264 class ThingHuman(ThingAnimate):
270 class ThingMonster(ThingAnimate):