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