home · contact · privacy
Fix sign editing bugs.
[plomrogue2] / plomrogue / tasks.py
1 from plomrogue.errors import PlayError, GameError
2 from plomrogue.misc import quote
3
4
5
6 class Task:
7     argtypes = ''
8     todo = 1
9
10     def __init__(self, thing, args=()):
11         self.thing = thing
12         self.args = args
13
14     def _get_move_target(self):
15         if self.args[0] == 'HERE':
16             return self.thing.position
17         return self.thing.game.map_geometry.move_yxyx(self.thing.position,
18                                                       self.args[0])
19
20     def check(self):
21         pass
22
23
24
25 class Task_WAIT(Task):
26
27     def do(self):
28         pass
29
30
31
32 class Task_MOVE(Task):
33     argtypes = 'string:direction'
34
35     def check(self):
36         test_yxyx = self._get_move_target()
37         move_blockers = self.thing.game.get_movement_blockers()
38         if test_yxyx in [t.position for t in self.thing.game.things
39                          if t.blocks_movement]:
40             raise PlayError('blocked by other thing')
41         elif self.thing.game.maps[test_yxyx[0]][test_yxyx[1]] in move_blockers:
42             raise PlayError('blocked by impassable tile')
43
44     def do(self):
45         if self.thing.type_ == 'Player' and not self.thing.standing:
46             self.thing.standing = True
47             self.thing.send_msg('CHAT "You get up."')
48         self.thing.game.record_change(self.thing.position, 'other')
49         if self.thing.blocks_light:
50             self.thing.game.record_change(self.thing.position, 'fov')
51         self.thing.position = self._get_move_target()
52         for t in [t for t in self.thing.game.things
53                   if t.type_ == 'Player' and not t == self.thing
54                   and t.position == self.thing.position]:
55             self.thing.send_msg('CHAT %s' %
56                                 quote('You get awkwardly close to %s.' % t.name))
57             for c_id in self.thing.game.sessions:
58                 if self.thing.game.sessions[c_id]['thing_id'] == t.id_:
59                     t.send_msg('CHAT %s' %
60                                quote('%s gets awkwardly close to you.' %
61                                      self.thing.name))
62                     break
63         self.thing.game.record_change(self.thing.position, 'other')
64         terrain = \
65             self.thing.game.maps[self.thing.position[0]][self.thing.position[1]]
66         if terrain in self.thing.game.terrains:
67             terrain_type = self.thing.game.terrains[terrain]
68             if 'sittable' in terrain_type.tags:
69                 self.thing.standing = False
70                 self.thing.send_msg('CHAT "You sink into the %s. '
71                                     'Staying here will replenish your energy."'
72                                     % terrain_type.description)
73         for t in [t for t in self.thing.game.things
74                   if t.type_ == 'Chair' and t.position == self.thing.position]:
75             self.thing.standing = False
76             self.thing.send_msg('CHAT "You sink into the Chair. '
77                                 'Staying here will replenish your energy."')
78         self.thing.invalidate('fov')
79         if self.thing.blocks_light:
80             self.thing.game.record_change(self.thing.position, 'fov')
81         if self.thing.carrying:
82             self.thing.carrying.position = self.thing.position
83             if self.thing.carrying.type_ == 'Crate':
84                 for t in self.thing.carrying.content:
85                     t.position = self.thing.position
86
87
88
89 class Task_WRITE(Task):
90     argtypes = 'string:char string'
91
92     def check(self):
93         if not self.thing.game.can_do_tile_with_pw(*self.thing.position,
94                                                    self.args[1]):
95             raise GameError('wrong password for tile')
96
97     def do(self):
98         big_yx = self.thing.position[0]
99         little_yx = self.thing.position[1]
100         self.thing.game.maps[big_yx][little_yx] = self.args[0]
101         self.thing.game.record_change((big_yx, little_yx), 'fov')
102
103
104
105 class Task_FLATTEN_SURROUNDINGS(Task):
106     argtypes = 'string'
107
108     def check(self):
109         pass
110
111     def do(self):
112         for yxyx in [self.thing.position] + \
113                 list(self.thing.game.map_geometry.get_neighbors_yxyx(
114                     self.thing.position).values()):
115             if not self.thing.game.can_do_tile_with_pw(*yxyx, self.args[0]):
116                 continue
117             self.thing.game.maps[yxyx[0]][yxyx[1]] = self.thing.game.get_flatland()
118             self.thing.game.record_change(yxyx, 'fov')
119
120
121
122 class Task_PICK_UP(Task):
123     argtypes = 'int:pos'
124
125     def check(self):
126         if self.thing.carrying:
127             raise PlayError('already carrying something')
128         to_pick_up = self.thing.game.get_thing(self.args[0])
129         neighbors = self.thing.game.map_geometry.\
130             get_neighbors_yxyx(self.thing.position).values()
131         reach = [self.thing.position] + list(neighbors)
132         if to_pick_up is None:
133             raise PlayError('no such thing exists')
134         elif to_pick_up == self.thing:
135             raise PlayError('cannot pick up oneself')
136         elif to_pick_up.type_ == 'Player':
137             raise PlayError('cannot pick up player')
138         elif to_pick_up.carried:
139             raise PlayError('thing already carried by a player')
140         elif to_pick_up.position not in reach:
141             raise PlayError('thing not in reach')
142         elif not to_pick_up.portable:
143             raise PlayError('thing not portable')
144
145     def do(self):
146         to_pick_up = self.thing.game.get_thing(self.args[0])
147         to_pick_up.position = self.thing.position[:]
148         self.thing.carrying = to_pick_up
149         to_pick_up.carried = True
150         for t in [t for t in self.thing.game.things
151                   if t.type_ == 'Crate' and to_pick_up in t.content]:
152             t.remove_from_crate(to_pick_up)
153             self.thing.send_msg('CHAT "You take the item out of the crate."')
154             break
155         self.thing.game.record_change(self.thing.position, 'other')
156
157
158
159 class Task_DROP(Task):
160     argtypes = 'string:direction+here'
161
162     def check(self):
163         if not self.thing.carrying:
164             raise PlayError('nothing to drop')
165         target_position = self._get_move_target()
166         targets = [t for t in self.thing.game.things
167                    if t.position == target_position
168                    and not t == self.thing.carrying]
169         for target in targets:
170             if target.type_ == 'CookieSpawner' and\
171                not self.thing.carrying.cookable:
172                 raise PlayError('cannot cook items of this type')
173             elif target.type_ == 'BottleDeposit':
174                 if not self.thing.carrying.type_ == 'Bottle':
175                     raise PlayError('cannot only put bottle into bottle deposit')
176                 if self.thing.carrying.full:
177                     raise PlayError('cannot drop full '
178                                     'bottle into bottle deposit')
179             elif target.type_ == 'Crate' and\
180                  self.thing.carrying.type_ == 'Crate':
181                 raise PlayError('cannot put crate into crate')
182
183     def do(self):
184         target_position = self._get_move_target()
185         dropped = self.thing.uncarry()
186         dropped.position = target_position
187         if dropped.type_ == 'Crate':
188             for item in dropped.content:
189                 item.position = target_position
190         targets = [t for t in self.thing.game.things
191                    if t.position == dropped.position and not t == dropped]
192         for target in targets:
193             if target.type_ == 'CookieSpawner':
194                 target.accept(dropped)
195                 self.thing.game.remove_thing(dropped)
196                 break
197             elif target.type_ == 'BottleDeposit':
198                 target.accept()
199                 self.thing.game.remove_thing(dropped)
200                 break
201             elif target.type_ == 'Crate':
202                 target.accept(dropped)
203                 self.thing.send_msg('CHAT "You put the item into the crate."')
204                 break
205             elif target.type_ == 'HatRemixer':
206                 t.accept(dropped)
207                 break
208         self.thing.game.record_change(self.thing.position, 'other')
209
210
211
212 class Task_DOOR(Task):
213
214     def check(self):
215         action_radius = list(self.thing.game.map_geometry.
216                              get_neighbors_yxyx(self.thing.position).values())
217         reachable_doors = [t for t in self.thing.game.things if
218                            t.type_ == 'Door' and t.position in action_radius]
219         if len(reachable_doors) == 0:
220             raise PlayError('not standing next to a door to open/close')
221         for door in reachable_doors:
222             if not door.blocks_movement:
223                 return
224             if not door.locked:
225                 return
226             if self.thing.carrying and self.thing.carrying.type_ == 'DoorKey'\
227                and self.thing.carrying.door == door:
228                 return
229         raise PlayError('cannot open locked door without its key')
230
231     def do(self):
232         action_radius = list(self.thing.game.map_geometry.
233                              get_neighbors_yxyx(self.thing.position).values())
234         for t in [t for t in self.thing.game.things if
235                   t.type_ == 'Door' and t.position in action_radius]:
236             if t.blocks_movement:
237                 t.open()
238             else:
239                 t.close()
240                 if self.thing.carrying and\
241                    self.thing.carrying.type_ == 'DoorKey' and\
242                    self.thing.carrying.door == t:
243                     self.thing.send_msg('CHAT "You lock the door."')
244                     t.lock()
245             self.thing.game.record_change(t.position, 'other')
246             self.thing.game.record_change(t.position, 'fov')
247
248
249
250 class Task_INTOXICATE(Task):
251
252     def check(self):
253         if self.thing.carrying is None:
254             raise PlayError('carrying nothing to consume')
255         if not self.thing.carrying.consumable:
256             raise PlayError('cannot consume this kind of thing')
257         if self.thing.carrying.type_ == 'Bottle' and\
258            not self.thing.carrying.full:
259             raise PlayError('bottle is empty')
260
261     def do(self):
262         if self.thing.carrying.type_ == 'Bottle':
263             self.thing.carrying.full = False
264             self.thing.carrying.empty()
265             self.thing.send_msg('CHAT "You are drunk now."')
266             self.thing.need_for_toilet += 1
267             self.thing.drunk += 100000
268             self.thing.invalidate('fov')
269             self.thing.game.record_change(self.thing.position, 'other')
270         elif self.thing.carrying.type_ == 'Psychedelic':
271             self.thing.tripping += 100000
272             self.thing.send_msg('CHAT "You start tripping."')
273             self.thing.send_msg('RANDOM_COLORS')
274             eaten = self.thing.uncarry()
275             self.thing.game.remove_thing(eaten)
276         elif self.thing.carrying.type_ == 'Cookie':
277             self.thing.send_msg('CHAT ' + quote('You eat a cookie that grants the ability to draw the following character: "%s"' % self.thing.carrying.thing_char))
278             self.thing.add_cookie_char(self.thing.carrying.thing_char)
279             eaten = self.thing.uncarry()
280             self.thing.game.remove_thing(eaten)
281         elif self.thing.carrying.type_ == 'Stimulant':
282             self.thing.send_msg('CHAT "You feel a flash of energy."')
283             self.thing.energy += 50
284             eaten = self.thing.uncarry()
285             self.thing.game.remove_thing(eaten)
286
287
288
289 class Task_COMMAND(Task):
290     argtypes = 'string'
291
292     def check(self):
293         if self.thing.carrying is None:
294             raise PlayError('nothing to command')
295         if not self.thing.carrying.commandable:
296             raise PlayError('cannot command this item type')
297
298     def do(self):
299         reply_lines = self.thing.carrying.interpret(self.args[0])
300         for line in reply_lines:
301             self.thing.send_msg('REPLY ' + quote(line))
302
303
304
305 class Task_INSTALL(Task):
306     argtypes = 'string'
307
308     def _get_uninstallables(self):
309         return [t for t in self.thing.game.things
310                 if t != self.thing
311                 and hasattr(t, 'installable') and t.installable
312                 and (not t.portable)
313                 and t.position == self.thing.position]
314
315     def check(self):
316         if not self.thing.game.can_do_tile_with_pw(*self.thing.position,
317                                                    self.args[0]):
318             raise GameError('wrong password for tile')
319         if self.thing.carrying:
320             if not hasattr(self.thing.carrying, 'installable')\
321                or not self.thing.carrying.installable:
322                 raise PlayError('carried thing not installable')
323         elif len(self._get_uninstallables()) == 0:
324             raise PlayError('nothing to uninstall here')
325
326     def do(self):
327         if self.thing.carrying:
328             t = self.thing.uncarry()
329             t.install()
330             self.thing.send_msg('CHAT "You install the thing you carry."')
331         else:
332             self._get_uninstallables()[0].uninstall()
333             self.thing.send_msg('CHAT "You uninstall the thing here."')
334         self.thing.game.record_change(self.thing.position, 'other')
335
336
337
338 class Task_WEAR(Task):
339
340     def check(self):
341         if self.thing.name in self.thing.game.hats:
342             return
343         if not self.thing.carrying:
344             raise PlayError('carrying nothing to wear')
345         if self.thing.name in self.thing.game.hats:
346             raise PlayError('already wearing a hat')
347         if self.thing.carrying.type_ not in {'Hat', 'Bottle'}:
348             raise PlayError('can not wear the kind of thing you hold')
349
350     def do(self):
351         if self.thing.name in self.thing.game.hats:
352             t = self.thing.game.add_thing('Hat', self.thing.position)
353             t.design = self.thing.game.hats[self.thing.name]
354             del self.thing.game.hats[self.thing.name]
355             self.thing.send_msg('CHAT "You drop your hat."')
356             for remixer in [t for t in self.thing.game.things
357                             if t.type_ == 'HatRemixer'
358                             and t.position == self.thing.position]:
359                 remixer.accept(t)
360                 break
361         else:
362             if self.thing.carrying.type_ == 'Bottle':
363                 self.thing.send_msg('CHAT "Your attempt to wear a bottle on '
364                                     'your head fails."')
365                 self.thing.carrying.sound('BOTTLE', 'SMASH')
366             elif self.thing.carrying.type_ == 'Hat':
367                 self.thing.game.hats[self.thing.name] =\
368                     self.thing.carrying.design
369                 self.thing.send_msg('CHAT "You put on a hat."')
370             dropped = self.thing.uncarry()
371             self.thing.game.remove_thing(dropped)
372         self.thing.game.record_change(self.thing.position, 'other')
373
374
375
376 class Task_SPIN(Task):
377
378     def check(self):
379         if not self.thing.carrying:
380             raise PlayError('holding nothing to spin')
381         if not hasattr(self.thing.carrying, 'spinnable'):
382             raise PlayError('held object not spinnable')
383
384     def do(self):
385         self.thing.carrying.spin()
386         self.thing.send_msg('CHAT "You spin this object."')
387
388
389
390 class Task_DANCE(Task):
391
392     def do(self):
393         self.thing.send_msg('CHAT "You dance."')
394         self.thing.dancing += 10
395         self.thing.game.changed = True