1 /**
2 * @file
3 * @brief Game state functions.
4 **/
5
6 #include "AppHdr.h"
7
8 #include "state.h"
9
10 #if defined(UNIX) || defined(TARGET_COMPILER_MINGW)
11 #include <unistd.h>
12 #endif
13
14 #include "dbg-util.h"
15 #include "delay.h"
16 #include "directn.h"
17 #include "hints.h"
18 #include "macro.h"
19 #include "menu.h"
20 #include "message.h"
21 #include "monster.h"
22 #include "player.h"
23 #include "religion.h"
24 #include "showsymb.h"
25 #include "unwind.h"
26
game_state()27 game_state::game_state()
28 : game_crashed(false), crash_debug_scans_safe(true),
29 mouse_enabled(false), waiting_for_command(false),
30 terminal_resized(false), last_winch(0),
31 seed(0),
32 io_inited(false),
33 need_save(false), game_started(false), saving_game(false),
34 updating_scores(false),
35 #ifndef USE_TILE_LOCAL
36 smallterm(false),
37 #endif
38 seen_hups(0), map_stat_gen(false), map_stat_dump_disconnect(false),
39 obj_stat_gen(false), type(GAME_TYPE_NORMAL),
40 last_type(GAME_TYPE_UNSPECIFIED), last_game_exit(game_exit::unknown),
41 marked_as_won(false), arena_suspended(false),
42 generating_level(false), dump_maps(false), test(false), script(false),
43 build_db(false), tests_selected(),
44 #ifdef DGAMELAUNCH
45 throttle(true),
46 bypassed_startup_menu(true),
47 #else
48 throttle(false),
49 bypassed_startup_menu(false),
50 #endif
51 show_more_prompt(true), terminal_resize_handler(nullptr),
52 terminal_resize_check(nullptr), doing_prev_cmd_again(false),
53 prev_cmd(CMD_NO_CMD), repeat_cmd(CMD_NO_CMD),
54 cmd_repeat_started_unsafe(false), lua_calls_no_turn(0),
55 stat_gain_prompt(false), simulating_xp_gain(false),
56 level_annotation_shown(false),
57 viewport_monster_hp(false), viewport_weapons(false),
58 tiles_disabled(false),
59 title_screen(true),
60 invisible_targeting(false),
61 darken_range(nullptr), unsaved_macros(false), disables(),
62 minor_version(-1), save_rcs_version(),
63 nonempty_buffer_flush_errors(false),
64 last_builder_error_fatal(false),
65 mon_act(nullptr)
66 {
67 reset_cmd_repeat();
68 reset_cmd_again();
69 #ifndef UNIX
70 no_gdb = "Non-UNIX Platform -> not running gdb.";
71 #else
72 no_gdb = access(GDB_PATH, 1) ? "gdb not executable." : 0;
73 #endif
74 }
75
76 /**
77 * Cleanup for when the game is reset.
78 *
79 * @see main.cc:_reset_game()
80 */
reset_game()81 void game_state::reset_game()
82 {
83 game_started = false;
84 // need_save is unset by death, but not by saving with restart_after_save.
85 need_save = false;
86 type = GAME_TYPE_UNSPECIFIED;
87 updating_scores = false;
88 clear_mon_acting();
89 reset_cmd_repeat();
90 reset_cmd_again();
91 }
92
93 ///////////////////////////////////////////////////////////////////////////
94 // Repeating commands and doing the previous command over again.
95
is_replaying_keys() const96 bool game_state::is_replaying_keys() const
97 {
98 return crawl_state.doing_prev_cmd_again
99 || crawl_state.is_repeating_cmd();
100 }
101
is_repeating_cmd() const102 bool game_state::is_repeating_cmd() const
103 {
104 return repeat_cmd != CMD_NO_CMD;
105 }
106
cancel_cmd_repeat(string reason,bool force)107 void game_state::cancel_cmd_repeat(string reason, bool force)
108 {
109 if (!force && !is_repeating_cmd())
110 return;
111
112 if (repeat_cmd == CMD_WIZARD)
113 {
114 // Don't interrupt wizard testing of religion.
115 if (is_god_acting())
116 return;
117
118 // Don't interrupt wizard testing just because we can't
119 // move.
120 if (you.cannot_act())
121 return;
122
123 // We've probably just recovered from being unable to act;
124 // again, don't interrupt.
125 if (you.turn_is_over)
126 return;
127 }
128
129 if (is_replaying_keys() || cmd_repeat_start)
130 flush_input_buffer(FLUSH_KEY_REPLAY_CANCEL);
131
132 if (is_processing_macro())
133 flush_input_buffer(FLUSH_ABORT_MACRO);
134
135 reset_cmd_repeat();
136
137 if (!reason.empty())
138 mpr(reason);
139 }
140
cancel_cmd_again(string reason,bool force)141 void game_state::cancel_cmd_again(string reason, bool force)
142 {
143 if (!force && !doing_prev_cmd_again)
144 return;
145
146 if (is_replaying_keys() || cmd_repeat_start)
147 flush_input_buffer(FLUSH_KEY_REPLAY_CANCEL);
148
149 if (is_processing_macro())
150 flush_input_buffer(FLUSH_ABORT_MACRO);
151
152 reset_cmd_again();
153
154 if (!reason.empty())
155 mpr(reason);
156 }
157
cancel_cmd_all(string reason)158 void game_state::cancel_cmd_all(string reason)
159 {
160 cancel_cmd_repeat(reason);
161 cancel_cmd_again(reason);
162 }
163
cant_cmd_repeat(string reason)164 void game_state::cant_cmd_repeat(string reason)
165 {
166 if (reason.empty())
167 reason = "Can't repeat that command.";
168
169 cancel_cmd_repeat(reason);
170 }
171
cant_cmd_again(string reason)172 void game_state::cant_cmd_again(string reason)
173 {
174 if (reason.empty())
175 reason = "Can't redo that command.";
176
177 cancel_cmd_again(reason);
178 }
179
cant_cmd_any(string reason)180 void game_state::cant_cmd_any(string reason)
181 {
182 cant_cmd_repeat(reason);
183 cant_cmd_again(reason);
184 }
185
186 // The method is called to prevent the "no repeating zero turns
187 // commands" message that input() generates (in the absence of
188 // cancelling the repetition) for a repeated command that took no
189 // turns. A wrapper around cancel_cmd_repeat(), its only purpose is
190 // to make it clear why cancel_cmd_repeat() is being called.
zero_turns_taken()191 void game_state::zero_turns_taken()
192 {
193 ASSERT(!you.turn_is_over);
194 cancel_cmd_repeat();
195 }
196
interrupt_cmd_repeat(activity_interrupt ai,const activity_interrupt_data & at)197 bool interrupt_cmd_repeat(activity_interrupt ai,
198 const activity_interrupt_data &at)
199 {
200 if (crawl_state.cmd_repeat_start)
201 return false;
202
203 if (crawl_state.repeat_cmd == CMD_WIZARD)
204 return false;
205
206 switch (ai)
207 {
208 case activity_interrupt::teleport:
209 case activity_interrupt::force:
210 case activity_interrupt::hp_loss:
211 case activity_interrupt::monster_attacks:
212 case activity_interrupt::mimic:
213 crawl_state.cancel_cmd_repeat("Command repetition interrupted.");
214 return true;
215
216 default:
217 break;
218 }
219
220 if (ai == activity_interrupt::see_monster)
221 {
222 const monster* mon = at.mons_data;
223 ASSERT(mon);
224 if (!you.can_see(*mon))
225 return false;
226
227 if (crawl_state.cmd_repeat_started_unsafe
228 && at.context != SC_NEWLY_SEEN)
229 {
230 return false;
231 }
232
233 crawl_state.cancel_cmd_repeat();
234
235 #ifndef DEBUG_DIAGNOSTICS
236 if (at.context == SC_NEWLY_SEEN)
237 {
238 monster_info mi(mon);
239
240 mprf(MSGCH_WARN, "%s comes into view.",
241 get_monster_equipment_desc(mi, DESC_WEAPON).c_str());
242 }
243
244 if (crawl_state.game_is_hints())
245 hints_monster_seen(*mon);
246 #else
247 formatted_string fs(channel_to_colour(MSGCH_WARN));
248 fs.cprintf("%s (", mon->name(DESC_PLAIN, true).c_str());
249 monster_info mi(mon);
250 fs.add_glyph(get_mons_glyph(mi));
251 fs.cprintf(") in view: (%d,%d), see_cell: %s",
252 mon->pos().x, mon->pos().y,
253 you.see_cell(mon->pos())? "yes" : "no");
254 formatted_mpr(fs, MSGCH_WARN);
255 #endif
256
257 return true;
258 }
259
260 // If command repetition is being used to imitate the rest command,
261 // then everything interrupts it.
262 if (crawl_state.repeat_cmd == CMD_WAIT)
263 {
264 if (ai == activity_interrupt::full_mp)
265 crawl_state.cancel_cmd_repeat("Magic restored.");
266 else if (ai == activity_interrupt::full_hp)
267 crawl_state.cancel_cmd_repeat("HP restored");
268 else
269 crawl_state.cancel_cmd_repeat("Command repetition interrupted.");
270
271 return true;
272 }
273
274 if (crawl_state.cmd_repeat_started_unsafe)
275 return false;
276
277 if (ai == activity_interrupt::hit_monster)
278 {
279 // This check is for when command repetition is used to
280 // whack away at a 0xp monster, since the player feels safe
281 // when the only monsters around are 0xp.
282 const monster* mon = at.mons_data;
283
284 if (!mons_is_threatening(*mon) && mon->visible_to(&you))
285 return false;
286
287 crawl_state.cancel_cmd_repeat("Command repetition interrupted.");
288 return true;
289 }
290
291 return false;
292 }
293
reset_cmd_repeat()294 void game_state::reset_cmd_repeat()
295 {
296 repeat_cmd = CMD_NO_CMD;
297 cmd_repeat_start = false;
298 }
299
reset_cmd_again()300 void game_state::reset_cmd_again()
301 {
302 doing_prev_cmd_again = false;
303 prev_cmd = CMD_NO_CMD;
304 prev_cmd_repeat_goal = 0;
305 prev_repeat_cmd = CMD_NO_CMD;
306
307 prev_cmd_keys.clear();
308 }
309
310 ///////////////////////////////////////////////////////////
311 // Keeping track of which god is currently doing something
312 ///////////////////////////////////////////////////////////
313
god_act_state()314 god_act_state::god_act_state()
315 {
316 reset();
317 }
318
reset()319 void god_act_state::reset()
320 {
321 which_god = GOD_NO_GOD;
322 retribution = false;
323 depth = 0;
324 }
325
is_god_acting() const326 bool game_state::is_god_acting() const
327 {
328 ASSERT(god_act.depth >= 0);
329 bool god_is_acting = god_act.depth > 0;
330 ASSERT(!(god_is_acting && god_act.which_god == GOD_NO_GOD));
331 ASSERT(god_is_acting || god_act.which_god == GOD_NO_GOD);
332 ASSERT(god_is_acting || god_act_stack.empty());
333
334 return god_is_acting;
335 }
336
is_god_retribution() const337 bool game_state::is_god_retribution() const
338 {
339 ASSERT(is_god_acting());
340
341 return god_act.retribution;
342 }
343
which_god_acting() const344 god_type game_state::which_god_acting() const
345 {
346 return god_act.which_god;
347 }
348
inc_god_acting(bool is_retribution)349 void game_state::inc_god_acting(bool is_retribution)
350 {
351 inc_god_acting(you.religion, is_retribution);
352 }
353
inc_god_acting(god_type which_god,bool is_retribution)354 void game_state::inc_god_acting(god_type which_god, bool is_retribution)
355 {
356 ASSERT(which_god != GOD_NO_GOD);
357
358 if (god_act.which_god != GOD_NO_GOD
359 && god_act.which_god != which_god)
360 {
361 ASSERT(god_act.depth >= 1);
362
363 god_act_stack.push_back(god_act);
364 god_act.reset();
365 }
366
367 god_act.which_god = which_god;
368 god_act.retribution = is_retribution || god_act.retribution;
369 god_act.depth++;
370 }
371
dec_god_acting()372 void game_state::dec_god_acting()
373 {
374 dec_god_acting(you.religion);
375 }
376
dec_god_acting(god_type which_god)377 void game_state::dec_god_acting(god_type which_god)
378 {
379 ASSERT(which_god != GOD_NO_GOD);
380 ASSERT(god_act.depth > 0);
381 ASSERT(god_act.which_god == which_god);
382
383 god_act.depth--;
384
385 if (god_act.depth == 0)
386 {
387 god_act.reset();
388 if (!god_act_stack.empty())
389 {
390 god_act = god_act_stack[god_act_stack.size() - 1];
391 god_act_stack.pop_back();
392 ASSERT(god_act.depth >= 1);
393 ASSERT(god_act.which_god != GOD_NO_GOD);
394 ASSERT(god_act.which_god != which_god);
395 }
396 }
397 }
398
clear_god_acting()399 void game_state::clear_god_acting()
400 {
401 ASSERT(!is_god_acting());
402 ASSERT(god_act_stack.empty());
403
404 god_act.reset();
405 }
406
other_gods_acting() const407 vector<god_act_state> game_state::other_gods_acting() const
408 {
409 ASSERT(is_god_acting());
410 return god_act_stack;
411 }
412
is_mon_acting() const413 bool game_state::is_mon_acting() const
414 {
415 return mon_act != nullptr;
416 }
417
which_mon_acting() const418 monster* game_state::which_mon_acting() const
419 {
420 return mon_act;
421 }
422
inc_mon_acting(monster * mon)423 void game_state::inc_mon_acting(monster* mon)
424 {
425 ASSERT(!invalid_monster(mon));
426
427 if (mon_act != nullptr)
428 mon_act_stack.push_back(mon_act);
429
430 mon_act = mon;
431 }
432
dec_mon_acting(monster * mon)433 void game_state::dec_mon_acting(monster* mon)
434 {
435 ASSERT(mon_act == mon);
436
437 mon_act = nullptr;
438
439 const unsigned int size = mon_act_stack.size();
440 if (size > 0)
441 {
442 mon_act = mon_act_stack[size - 1];
443 ASSERT(!invalid_monster(mon_act));
444 mon_act_stack.pop_back();
445 }
446 }
447
clear_mon_acting()448 void game_state::clear_mon_acting()
449 {
450 mon_act = nullptr;
451 mon_act_stack.clear();
452 }
453
mon_gone(monster * mon)454 void game_state::mon_gone(monster* mon)
455 {
456 for (unsigned int i = 0, size = mon_act_stack.size(); i < size; i++)
457 {
458 if (mon_act_stack[i] == mon)
459 {
460 mon_act_stack.erase(mon_act_stack.begin() + i);
461 i--;
462 size--;
463 }
464 }
465
466 if (mon_act == mon)
467 dec_mon_acting(mon);
468 }
469
dump()470 void game_state::dump()
471 {
472 fprintf(stderr, "\nGame state:\n\n");
473
474 fprintf(stderr, "mouse_enabled: %d, waiting_for_command: %d, "
475 "terminal_resized: %d\n",
476 mouse_enabled, waiting_for_command, terminal_resized);
477 fprintf(stderr, "io_inited: %d, need_save: %d, saving_game: %d, "
478 "updating_scores: %d:\n",
479 io_inited, need_save, saving_game, updating_scores);
480 fprintf(stderr, "seen_hups: %d, map_stat_gen: %d, type: %d, "
481 "arena_suspended: %d\n",
482 seen_hups, map_stat_gen, type, arena_suspended);
483 if (last_winch)
484 {
485 fprintf(stderr, "Last resize was %" PRId64" seconds ago.\n",
486 (int64_t)(time(0) - last_winch));
487 }
488
489 fprintf(stderr, "\n");
490
491 // Arena mode can change behavior of the rest of the code and/or lead
492 // to asserts.
493 unwind_var<game_type> _type(type, GAME_TYPE_NORMAL);
494 unwind_bool _arena_suspended(arena_suspended, false);
495
496 fprintf(stderr, "prev_cmd = %s\n", command_to_name(prev_cmd).c_str());
497
498 if (doing_prev_cmd_again)
499 {
500 fprintf(stderr, "Doing prev_cmd again with keys: ");
501 for (int key : prev_cmd_keys)
502 fprintf(stderr, "%d, ", key);
503 fprintf(stderr, "\n");
504 fprintf(stderr, "As ASCII keys: ");
505 for (int key : prev_cmd_keys)
506 fprintf(stderr, "%c", (char) key);
507 fprintf(stderr, "\n\n");
508 }
509 fprintf(stderr, "repeat_cmd = %s\n", command_to_name(repeat_cmd).c_str());
510
511 fprintf(stderr, "\n");
512
513 if (god_act.which_god != GOD_NO_GOD || god_act.depth != 0)
514 {
515 fprintf(stderr, "God %s currently acting with depth %d\n\n",
516 god_name(god_act.which_god).c_str(), god_act.depth);
517 }
518
519 if (!god_act_stack.empty())
520 {
521 fprintf(stderr, "Other gods acting:\n");
522 for (const god_act_state &godact : god_act_stack)
523 {
524 fprintf(stderr, "God %s with depth %d\n",
525 god_name(godact.which_god).c_str(), godact.depth);
526 }
527 fprintf(stderr, "\n\n");
528 }
529
530 if (mon_act != nullptr)
531 {
532 fprintf(stderr, "%s currently acting:\n\n",
533 debug_mon_str(mon_act).c_str());
534 debug_dump_mon(mon_act, true);
535 }
536
537 if (!mon_act_stack.empty())
538 {
539 fprintf(stderr, "Others monsters acting:\n");
540 for (const monster *mon : mon_act_stack)
541 fprintf(stderr, " %s\n", debug_mon_str(mon).c_str());
542 }
543 }
544
player_is_dead() const545 bool game_state::player_is_dead() const
546 {
547 return updating_scores && !need_save;
548 }
549
game_standard_levelgen() const550 bool game_state::game_standard_levelgen() const
551 {
552 return game_is_normal() || game_is_hints();
553 }
554
game_is_valid_type() const555 bool game_state::game_is_valid_type() const
556 {
557 return type < NUM_GAME_TYPE;
558 }
559
game_is_normal() const560 bool game_state::game_is_normal() const
561 {
562 ASSERT(game_is_valid_type());
563 return type == GAME_TYPE_NORMAL || type == GAME_TYPE_CUSTOM_SEED
564 || type == GAME_TYPE_UNSPECIFIED;
565 }
566
game_is_tutorial() const567 bool game_state::game_is_tutorial() const
568 {
569 ASSERT(game_is_valid_type());
570 return type == GAME_TYPE_TUTORIAL;
571 }
572
game_is_arena() const573 bool game_state::game_is_arena() const
574 {
575 ASSERT(game_is_valid_type());
576 return type == GAME_TYPE_ARENA;
577 }
578
game_is_sprint() const579 bool game_state::game_is_sprint() const
580 {
581 ASSERT(game_is_valid_type());
582 return type == GAME_TYPE_SPRINT;
583 }
584
game_is_hints() const585 bool game_state::game_is_hints() const
586 {
587 ASSERT(game_is_valid_type());
588 return type == GAME_TYPE_HINTS;
589 }
590
game_is_hints_tutorial() const591 bool game_state::game_is_hints_tutorial() const
592 {
593 return game_is_hints() || game_is_tutorial();
594 }
595
game_type_name() const596 string game_state::game_type_name() const
597 {
598 return game_type_name_for(type);
599 }
600
game_type_name_for(game_type _type)601 string game_state::game_type_name_for(game_type _type)
602 {
603 switch (_type)
604 {
605 case GAME_TYPE_UNSPECIFIED:
606 case GAME_TYPE_NORMAL:
607 case GAME_TYPE_HINTS:
608 default:
609 // No explicit game type name for default game.
610 return "";
611 case GAME_TYPE_CUSTOM_SEED:
612 return "Seeded";
613 case GAME_TYPE_TUTORIAL:
614 return "Tutorial";
615 case GAME_TYPE_ARENA:
616 return "Arena";
617 case GAME_TYPE_SPRINT:
618 return "Dungeon Sprint";
619 case NUM_GAME_TYPE:
620 return "Unknown";
621 }
622 }
623
game_savedir_path() const624 string game_state::game_savedir_path() const
625 {
626 if (!game_is_valid_type())
627 return ""; // a game from the future -- avoid the ASSERT below
628 return game_is_sprint()? "sprint/" : "";
629 }
630
game_type_qualifier() const631 string game_state::game_type_qualifier() const
632 {
633 if (type == GAME_TYPE_CUSTOM_SEED)
634 return "-seeded";
635 if (crawl_state.game_is_sprint())
636 return "-sprint";
637 if (crawl_state.game_is_tutorial())
638 return "-tutorial";
639 if (crawl_state.game_is_hints())
640 return "-hints";
641 return "";
642 }
643