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