1 from plomrogue.errors import GameError
8 def __init__(self, world, id_=None, position=(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
50 def move_towards_target(self, target):
51 dijkstra_map = type(self.world.map_)(self.world.map_.size)
53 dijkstra_map.terrain = [n_max for i in range(dijkstra_map.size_i)]
54 dijkstra_map[target] = 0
56 visible_map = self.get_visible_map()
59 for pos in dijkstra_map:
60 if visible_map[pos] != '.':
62 neighbors = dijkstra_map.get_neighbors(tuple(pos))
63 for direction in neighbors:
64 yx = neighbors[direction]
65 if yx is not None and dijkstra_map[yx] < dijkstra_map[pos] - 1:
66 dijkstra_map[pos] = dijkstra_map[yx] + 1
68 neighbors = dijkstra_map.get_neighbors(tuple(self.position))
70 target_direction = None
71 for direction in sorted(neighbors.keys()):
72 yx = neighbors[direction]
74 n_new = dijkstra_map[yx]
77 target_direction = direction
79 self.set_task('MOVE', (target_direction,))
81 def decide_task(self):
82 visible_things = self.get_visible_things()
84 for t in visible_things:
85 if t.type_ == 'human':
88 if target is not None:
90 self.move_towards_target(target)
96 def set_task(self, task_name, args=()):
97 task_class = self.world.game.tasks[task_name]
98 self.task = task_class(self, args)
99 self.task.check() # will throw GameError if necessary
101 def proceed(self, is_AI=True):
102 """Further the thing in its tasks, decrease its health.
104 First, ensures an empty map, decrements .health and kills
105 thing if crossing zero (removes from self.world.things for AI
106 thing, or unsets self.world.player_is_alive for player thing);
107 then checks that self.task is still possible and aborts if
108 otherwise (for AI things, decides a new task).
110 Then decrements .task.todo; if it thus falls to <= 0, enacts
111 method whose name is 'task_' + self.task.name and sets .task =
112 None. If is_AI, calls .decide_task to decide a self.task.
118 if self is self.world.player:
119 self.world.player_is_alive = False
121 del self.world.things[self.world.things.index(self)]
125 except GameError as e:
127 self._last_task_result = e
132 self.set_task('WAIT')
135 if self.task.todo <= 0:
136 self._last_task_result = self.task.do()
138 if is_AI and self.task is None:
142 self.set_task('WAIT')
144 def get_stencil(self):
145 if self._stencil is not None:
147 self._stencil = self.world.map_.get_fov_map(self.position)
150 def get_visible_map(self):
151 stencil = self.get_stencil()
152 m = self.world.map_.new_from_shape(' ')
154 if stencil[pos] == '.':
155 m[pos] = self.world.map_[pos]
158 def get_visible_things(self):
159 stencil = self.get_stencil()
161 for thing in self.world.things:
162 if (not thing.in_inventory) and stencil[thing.position] == '.':
163 visible_things += [thing]
164 return visible_things
166 def get_pickable_items(self):
168 for t in [t for t in self.get_visible_things() if
169 isinstance(t, ThingItem) and
170 (t.position == self.position or
172 self.world.map_.get_neighbors(self.position).values())]:
173 pickable_ids += [t.id_]
178 class ThingHuman(ThingAnimate):
184 class ThingMonster(ThingAnimate):