1 /**
2 * @file
3 * @brief Functions for handling multi-turn actions.
4 **/
5
6 #pragma once
7
8 #include <vector>
9
10 #include "activity-interrupt-type.h"
11 #include "command-type.h"
12 #include "enum.h"
13 #include "item-prop-enum.h"
14 #include "mpr.h"
15 #include "operation-types.h"
16 #include "seen-context-type.h"
17
18 using std::vector;
19
20 class interrupt_block
21 {
22 public:
23 interrupt_block(bool block = true) {
24 m_block = block;
25 if (block)
26 ++interrupts_blocked;
27 }
~interrupt_block()28 ~interrupt_block() {
29 if (m_block)
30 --interrupts_blocked;
31 }
32
blocked()33 static bool blocked() { return interrupts_blocked > 0; }
34 private:
35 bool m_block;
36 static int interrupts_blocked;
37 };
38
39 class monster;
40 struct ait_hp_loss;
41
42 enum class ai_payload // activity interrupt payloads
43 {
44 none,
45 int_payload,
46 string_payload,
47 monster,
48 hp_loss,
49 };
50
51 struct activity_interrupt_data
52 {
53 ai_payload apt;
54 union
55 {
56 const char* string_data;
57 const int* int_data;
58 const ait_hp_loss* ait_hp_loss_data;
59 monster* mons_data;
60 nullptr_t no_data;
61 };
62
63 seen_context_type context;
64
activity_interrupt_dataactivity_interrupt_data65 activity_interrupt_data()
66 : apt(ai_payload::none), no_data(nullptr), context(SC_NONE)
67 {
68 }
activity_interrupt_dataactivity_interrupt_data69 activity_interrupt_data(const int *i)
70 : apt(ai_payload::int_payload), int_data(i), context(SC_NONE)
71 {
72 }
activity_interrupt_dataactivity_interrupt_data73 activity_interrupt_data(const char *s)
74 : apt(ai_payload::string_payload), string_data(s), context(SC_NONE)
75 {
76 }
activity_interrupt_dataactivity_interrupt_data77 activity_interrupt_data(const string &s)
78 : apt(ai_payload::string_payload), string_data(s.c_str()),
79 context(SC_NONE)
80 {
81 }
82 activity_interrupt_data(monster* m, seen_context_type ctx = SC_NONE)
aptactivity_interrupt_data83 : apt(ai_payload::monster), mons_data(m), context(ctx)
84 {
85 }
activity_interrupt_dataactivity_interrupt_data86 activity_interrupt_data(const ait_hp_loss *ahl)
87 : apt(ai_payload::hp_loss), ait_hp_loss_data(ahl), context(SC_NONE)
88 {
89 }
90 };
91
92 struct ait_hp_loss
93 {
94 int hp;
95 int hurt_type; // KILLED_BY_POISON, etc.
96
ait_hp_lossait_hp_loss97 ait_hp_loss(int _hp, int _ht) : hp(_hp), hurt_type(_ht) { }
98 };
99
100 class Delay
101 {
102 /**
103 * This is run if `started` is not yet true.
104 */
start()105 virtual void start()
106 { }
107
108 /**
109 * This is run before tick(); if it returns true, the delay is popped off
110 * the queue and handle() returns early.
111 */
invalidated()112 virtual bool invalidated()
113 {
114 return false;
115 }
116
117 /**
118 * This is run once each turn of the delay, including the first, but not if
119 * it has finished.
120 */
tick()121 virtual void tick()
122 { }
123
124 /**
125 * If the delay has finished, this is run instead. It should contain the payload of
126 * the delay.
127 */
finish()128 virtual void finish()
129 {
130 mpr("You finish doing something buggy.");
131 }
132 protected:
133 bool started = false;
134 public:
135 int duration;
136
Delay(int dur)137 Delay(int dur) : duration{dur}
138 { }
139
140 virtual ~Delay() = default;
141
142 /**
143 * @return whether this is a running delay (run, rest, travel), which
144 * involves moving/resting each turn until completed, rather than depending
145 * on `duration` itself.
146 */
is_run()147 virtual bool is_run() const
148 {
149 return false;
150 }
151
152 /**
153 * @return whether this is a stair travel delay, which are generally
154 * uninterruptible but are interrupted by teleport. Note that no stairs
155 * are necessarily involved.
156 */
is_stair_travel()157 virtual bool is_stair_travel() const
158 {
159 return is_stairs();
160 }
161
162 /**
163 * @return whether this involves going up or down stairs.
164 */
is_stairs()165 virtual bool is_stairs() const
166 {
167 return false;
168 }
169
is_resting()170 virtual bool is_resting() const
171 {
172 return false;
173 }
174
175 /**
176 * @return whether it's OK to start eating during this delay if hungry.
177 */
want_autoeat()178 virtual bool want_autoeat() const
179 {
180 return false;
181 }
182
is_macro()183 virtual bool is_macro() const
184 {
185 return false;
186 }
187
188 /**
189 * @return true if this delay can act as a parent to other delays, i.e. if
190 * other delays can be spawned while this delay is running. If is_parent
191 * returns true, new delays will be pushed immediately to the front of the
192 * delay in question, rather than at the end of the queue.
193 */
is_parent()194 virtual bool is_parent() const
195 {
196 return false;
197 }
198
199 /**
200 * This is called exactly each turn that the delay is active.
201 */
202 virtual void handle();
203
204 /**
205 * This is called on the front of the delay queue when stop_delay is called.
206 * If the player needs to be notified, it should also print a message.
207 * @return whether to pop the delay.
208 */
try_interrupt()209 virtual bool try_interrupt()
210 {
211 // The default is for delays to be uninterruptible once started.
212 return false;
213 }
214
215 /**
216 *@return true if the delay involves using the item in this way (and
217 * therefore the item should not be messed with).
218 */
is_being_used(const item_def &)219 virtual bool is_being_used(const item_def& /*item*/) const
220 {
221 return false;
222 }
223
224 /**
225 * @return the delay's name; used in debugging and for the interrupt_ option family.
226 */
227 virtual const char* name() const = 0;
228 };
229
230 class EquipOnDelay : public Delay
231 {
232 item_def& equip;
233 bool was_prompted = false;
234
235 void start() override;
236
tick()237 void tick() override
238 {
239 mprf(MSGCH_MULTITURN_ACTION, "You continue putting on %s.",
240 equip.name(DESC_YOUR).c_str());
241 }
242
243 void finish() override;
244 public:
EquipOnDelay(int dur,item_def & item)245 EquipOnDelay(int dur, item_def& item) :
246 Delay(dur), equip(item)
247 { }
248
249 bool try_interrupt() override;
250
name()251 const char* name() const override
252 {
253 return "armour_on";
254 }
255
is_being_used(const item_def & item)256 bool is_being_used(const item_def& item) const override
257 {
258 return &item == &equip;
259 }
260 };
261
262 class EquipOffDelay : public Delay
263 {
264 const item_def& equip;
265 bool was_prompted = false;
266
267 void start() override;
268
tick()269 void tick() override
270 {
271 mprf(MSGCH_MULTITURN_ACTION, "You continue taking off %s.",
272 equip.name(DESC_YOUR).c_str());
273 }
274
275 bool invalidated() override;
276
277 void finish() override;
278 public:
EquipOffDelay(int dur,const item_def & item)279 EquipOffDelay(int dur, const item_def& item) :
280 Delay(dur), equip(item)
281 { }
282
283 bool try_interrupt() override;
284
name()285 const char* name() const override
286 {
287 return "armour_off";
288 }
289
is_being_used(const item_def & item)290 bool is_being_used(const item_def& item) const override
291 {
292 return &item == &equip;
293 }
294 };
295
296 class JewelleryOnDelay : public Delay
297 {
298 item_def& jewellery;
299
300 void tick() override;
301
302 void finish() override;
303 public:
JewelleryOnDelay(int dur,item_def & item)304 JewelleryOnDelay(int dur, item_def& item) :
305 Delay(dur), jewellery(item)
306 { }
307
name()308 const char* name() const override
309 {
310 return "jewellery_on";
311 }
312
is_being_used(const item_def & item)313 bool is_being_used(const item_def& item) const override
314 {
315 return &item == &jewellery;
316 }
317 };
318
319 class MemoriseDelay : public Delay
320 {
321 void start() override;
322
tick()323 void tick() override
324 {
325 mprf(MSGCH_MULTITURN_ACTION, "You continue memorising.");
326 }
327
328 void finish() override;
329 public:
330 spell_type spell;
331
MemoriseDelay(int dur,spell_type sp)332 MemoriseDelay(int dur, spell_type sp) :
333 Delay(dur), spell{sp}
334 { }
335
336 bool try_interrupt() override;
337
name()338 const char* name() const override
339 {
340 return "memorise";
341 }
342 };
343
344 class PasswallDelay : public Delay
345 {
346 coord_def dest;
347 void start() override;
348
tick()349 void tick() override
350 {
351 mprf(MSGCH_MULTITURN_ACTION, "You continue meditating on the rock.");
352 }
353
354 void finish() override;
355 public:
PasswallDelay(int dur,coord_def pos)356 PasswallDelay(int dur, coord_def pos) :
357 Delay(dur), dest{pos}
358 { }
359
360 bool try_interrupt() override;
361
name()362 const char* name() const override
363 {
364 return "passwall";
365 }
366 };
367
368 class DropItemDelay : public Delay
369 {
370 item_def& item;
371
372 void tick() override;
373
374 void finish() override;
375 public:
DropItemDelay(int dur,item_def & it)376 DropItemDelay(int dur, item_def& it) :
377 Delay(dur), item(it)
378 { }
379
name()380 const char* name() const override
381 {
382 return "drop_item";
383 }
384
is_being_used(const item_def & item_)385 bool is_being_used(const item_def& item_) const override
386 {
387 return &item_ == &item;
388 }
389 };
390
391 struct SelItem;
392 class MultidropDelay : public Delay
393 {
394 vector<SelItem>& items;
395
396 bool invalidated() override;
397 void tick() override;
398 public:
MultidropDelay(int dur,vector<SelItem> & vec)399 MultidropDelay(int dur, vector<SelItem>& vec) :
400 Delay(dur), items(vec)
401 { }
402
403 bool try_interrupt() override;
404
name()405 const char* name() const override
406 {
407 return "multidrop";
408 }
409
is_parent()410 bool is_parent() const override
411 {
412 // Can spawn delay_drop_item
413 return true;
414 }
415 };
416
417 class AscendingStairsDelay : public Delay
418 {
419 void finish() override;
420 public:
AscendingStairsDelay(int dur)421 AscendingStairsDelay(int dur) : Delay(dur)
422 { }
423
424 bool try_interrupt() override;
425
is_stairs()426 bool is_stairs() const override
427 {
428 return true;
429 }
430
name()431 const char* name() const override
432 {
433 return "ascending_stairs";
434 }
435 };
436
437 class DescendingStairsDelay : public Delay
438 {
439 void finish() override;
440 public:
DescendingStairsDelay(int dur)441 DescendingStairsDelay(int dur) : Delay(dur)
442 { }
443
444 bool try_interrupt() override;
445
is_stairs()446 bool is_stairs() const override
447 {
448 return true;
449 }
450
name()451 const char* name() const override
452 {
453 return "descending_stairs";
454 }
455 };
456
457 // The run delays (run, travel, rest) have some common code that does not apply
458 // to any other ones.
459 class BaseRunDelay : public Delay
460 {
461 void handle() override;
462
463 virtual bool want_move() const = 0;
464 virtual bool want_clear_messages() const = 0;
465 virtual command_type move_cmd() const = 0;
466
467 protected:
468 bool unsafe_once = false;
469
470 public:
is_run()471 bool is_run() const override
472 {
473 return true;
474 }
475
is_parent()476 bool is_parent() const override
477 {
478 // Interlevel travel can do upstairs/downstairs delays.
479 // Additionally, travel/rest can do autoeat.
480 return true;
481 }
482
483 bool try_interrupt() override;
484
BaseRunDelay()485 BaseRunDelay() : Delay(1)
486 { }
487 };
488
489 class RunDelay : public BaseRunDelay
490 {
want_move()491 bool want_move() const override
492 {
493 return true;
494 }
495
want_clear_messages()496 bool want_clear_messages() const override
497 {
498 return true;
499 }
500
501 command_type move_cmd() const override;
502 public:
RunDelay()503 RunDelay() : BaseRunDelay()
504 { }
505
name()506 const char* name() const override
507 {
508 return "run";
509 }
510 };
511
512 class RestDelay : public BaseRunDelay
513 {
want_move()514 bool want_move() const override
515 {
516 return false;
517 }
518
want_autoeat()519 bool want_autoeat() const override
520 {
521 return true;
522 }
523
want_clear_messages()524 bool want_clear_messages() const override
525 {
526 return false;
527 }
528
529 command_type move_cmd() const override;
530 public:
RestDelay()531 RestDelay() : BaseRunDelay()
532 { }
533
is_resting()534 bool is_resting() const override
535 {
536 return true;
537 }
538
name()539 const char* name() const override
540 {
541 return "rest";
542 }
543 };
544
545 class TravelDelay : public BaseRunDelay
546 {
want_move()547 bool want_move() const override
548 {
549 return true;
550 }
551
want_autoeat()552 bool want_autoeat() const override
553 {
554 return true;
555 }
556
want_clear_messages()557 bool want_clear_messages() const override
558 {
559 return true;
560 }
561
562 command_type move_cmd() const override;
563 public:
TravelDelay(bool unsafe)564 TravelDelay(bool unsafe) : BaseRunDelay()
565 {
566 unsafe_once = unsafe;
567 }
568
name()569 const char* name() const override
570 {
571 return "travel";
572 }
573 };
574
575 class MacroDelay : public Delay
576 {
577 void handle() override;
578 public:
MacroDelay()579 MacroDelay() : Delay(1)
580 { }
581
582 bool try_interrupt() override;
583
is_parent()584 bool is_parent() const override
585 {
586 // Lua macros can in theory perform any of the other delays,
587 // including travel; in practise travel still assumes there can be
588 // no parent delay.
589 return true;
590 }
591
is_macro()592 bool is_macro() const override
593 {
594 return true;
595 }
596
name()597 const char* name() const override
598 {
599 //XXX: this is compared to in _userdef_interrupt_activity
600 return "macro";
601 }
602 };
603
604 class MacroProcessKeyDelay : public Delay
605 {
606 public:
MacroProcessKeyDelay()607 MacroProcessKeyDelay() : Delay(1)
608 { }
609
handle()610 void handle() override
611 {
612 // If a Lua macro wanted Crawl to process a key normally, early exit.
613 return;
614 }
615
name()616 const char* name() const override
617 {
618 return "macro_process_key";
619 }
620 };
621
622 class ShaftSelfDelay : public Delay
623 {
624 void start() override;
625
tick()626 void tick() override
627 {
628 mprf(MSGCH_MULTITURN_ACTION, "You continue digging a shaft.");
629 }
630
631 void finish() override;
632 public:
ShaftSelfDelay(int dur)633 ShaftSelfDelay(int dur) : Delay(dur)
634 { }
635
636 bool try_interrupt() override;
637
is_stair_travel()638 bool is_stair_travel() const override
639 {
640 return true;
641 }
642
name()643 const char* name() const override
644 {
645 return "shaft_self";
646 }
647 };
648
649 class ExsanguinateDelay : public Delay
650 {
651 bool was_prompted = false;
652
653 void start() override;
654
tick()655 void tick() override
656 {
657 mprf(MSGCH_MULTITURN_ACTION, "You continue bloodletting.");
658 }
659
660 void finish() override;
661 public:
ExsanguinateDelay(int dur)662 ExsanguinateDelay(int dur) : Delay(dur)
663 { }
664
665 bool try_interrupt() override;
666
name()667 const char* name() const override
668 {
669 return "exsanguinate";
670 }
671 };
672
673 class RevivifyDelay : public Delay
674 {
675 bool was_prompted = false;
676
677 void start() override;
678
tick()679 void tick() override
680 {
681 mprf(MSGCH_MULTITURN_ACTION, "You continue your ritual.");
682 }
683
684 void finish() override;
685 public:
RevivifyDelay(int dur)686 RevivifyDelay(int dur) : Delay(dur)
687 { }
688
689 bool try_interrupt() override;
690
name()691 const char* name() const override
692 {
693 return "revivify";
694 }
695 };
696
697 void push_delay(shared_ptr<Delay> delay);
698
699 template<typename T, typename... Args>
start_delay(Args &&...args)700 shared_ptr<Delay> start_delay(Args&&... args)
701 {
702 auto delay = make_shared<T>(forward<Args>(args)...);
703 push_delay(delay);
704 return delay;
705 }
706
707 void stop_delay(bool stop_stair_travel = false);
708 bool you_are_delayed();
709 shared_ptr<Delay> current_delay();
710 void handle_delay();
711
712 bool player_stair_delay();
713 bool already_learning_spell(int spell = -1);
714
715 void clear_macro_process_key_delay();
716
717 activity_interrupt get_activity_interrupt(const string &);
718
719 void run_macro(const char *macroname = nullptr);
720
721 void autotoggle_autopickup(bool off);
722 bool interrupt_activity(activity_interrupt ai,
723 const activity_interrupt_data &a
724 = activity_interrupt_data(),
725 vector<string>* msgs_buf = nullptr);
726