home · contact · privacy
In examine cursor mode, show map cell contents in left bar.
[plomrogue2-experiments] / new / plomrogue / things.py
1 from plomrogue.errors import GameError
2
3
4
5 class ThingBase:
6     type_ = '?'
7
8     def __init__(self, world, id_=None, position=(0,0)):
9         self.world = world
10         self.position = position
11         if id_ is None:
12             self.id_ = self.world.new_thing_id()
13         else:
14             self.id_ = id_
15
16
17
18 class Thing(ThingBase):
19     blocking = False
20     in_inventory = False
21
22     def __init__(self, *args, **kwargs):
23         super().__init__(*args, **kwargs)
24         self.inventory = []
25
26     def proceed(self):
27         pass
28
29
30
31 class ThingItem(Thing):
32     type_ = 'item'
33
34
35
36 class ThingAnimate(Thing):
37     blocking = True
38
39     def __init__(self, *args, **kwargs):
40         super().__init__(*args, **kwargs)
41         self.set_task('WAIT')
42         self._last_task_result = None
43         self._stencil = None
44
45     def move_towards_target(self, target):
46         dijkstra_map = type(self.world.map_)(self.world.map_.size)
47         n_max = 256
48         dijkstra_map.terrain = [n_max for i in range(dijkstra_map.size_i)]
49         dijkstra_map[target] = 0
50         shrunk = True
51         visible_map = self.get_visible_map()
52         while shrunk:
53             shrunk = False
54             for pos in dijkstra_map:
55                 if visible_map[pos] != '.':
56                     continue
57                 neighbors = dijkstra_map.get_neighbors(tuple(pos))
58                 for direction in neighbors:
59                     yx = neighbors[direction]
60                     if yx is not None and dijkstra_map[yx] < dijkstra_map[pos] - 1:
61                         dijkstra_map[pos] = dijkstra_map[yx] + 1
62                         shrunk = True
63         neighbors = dijkstra_map.get_neighbors(tuple(self.position))
64         n = n_max
65         target_direction = None
66         for direction in sorted(neighbors.keys()):
67             yx = neighbors[direction]
68             if yx is not None:
69                 n_new = dijkstra_map[yx]
70                 if n_new < n:
71                     n = n_new
72                     target_direction = direction
73         if target_direction:
74             self.set_task('MOVE', (target_direction,))
75
76     def decide_task(self):
77         visible_things = self.get_visible_things()
78         target = None
79         for t in visible_things:
80             if t.type_ == 'human':
81                 target = t.position
82                 break
83         if target is not None:
84             try:
85                 self.move_towards_target(target)
86                 return
87             except GameError:
88                 pass
89         self.set_task('WAIT')
90
91     def set_task(self, task_name, args=()):
92         task_class = self.world.game.tasks[task_name]
93         self.task = task_class(self, args)
94         self.task.check()  # will throw GameError if necessary
95
96     def proceed(self, is_AI=True):
97         """Further the thing in its tasks.
98
99         Decrements .task.todo; if it thus falls to <= 0, enacts method
100         whose name is 'task_' + self.task.name and sets .task =
101         None. If is_AI, calls .decide_task to decide a self.task.
102
103         Before doing anything, ensures an empty map visibility stencil
104         and checks that task is still possible, and aborts it
105         otherwise (for AI things, decides a new task).
106
107         """
108         self._stencil = None
109         try:
110             self.task.check()
111         except GameError as e:
112             self.task = None
113             self._last_task_result = e
114             if is_AI:
115                 try:
116                     self.decide_task()
117                 except GameError:
118                     self.set_task('WAIT')
119             return
120         self.task.todo -= 1
121         if self.task.todo <= 0:
122             self._last_task_result = self.task.do()
123             self.task = None
124         if is_AI and self.task is None:
125             try:
126                 self.decide_task()
127             except GameError:
128                 self.set_task('WAIT')
129
130     def get_stencil(self):
131         if self._stencil is not None:
132             return self._stencil
133         self._stencil = self.world.map_.get_fov_map(self.position)
134         return self._stencil
135
136     def get_visible_map(self):
137         stencil = self.get_stencil()
138         m = self.world.map_.new_from_shape(' ')
139         for pos in m:
140             if stencil[pos] == '.':
141                 m[pos] = self.world.map_[pos]
142         return m
143
144     def get_visible_things(self):
145         stencil = self.get_stencil()
146         visible_things = []
147         for thing in self.world.things:
148             if (not thing.in_inventory) and stencil[thing.position] == '.':
149                 visible_things += [thing]
150         return visible_things
151
152     def get_pickable_items(self):
153         pickable_ids = []
154         for t in [t for t in self.get_visible_things() if
155                   isinstance(t, ThingItem) and
156                   (t.position == self.position or
157                    t.position in
158                    self.world.map_.get_neighbors(self.position).values())]:
159             pickable_ids += [t.id_]
160         return pickable_ids
161
162
163
164 class ThingHuman(ThingAnimate):
165     type_ = 'human'
166
167
168
169 class ThingMonster(ThingAnimate):
170     type_ = 'monster'