1 /**
2 * @file
3 * @brief Dungeon related wizard functions.
4 **/
5
6 #include "AppHdr.h"
7
8 #include "wiz-dgn.h"
9
10 #include "act-iter.h"
11 #include "branch.h"
12 #include "coordit.h"
13 #include "dactions.h"
14 #include "delay.h"
15 #include "describe.h"
16 #include "dgn-overview.h"
17 #include "dungeon.h"
18 #include "tile-env.h"
19 #include "files.h"
20 #include "libutil.h"
21 #include "maps.h"
22 #include "message.h"
23 #include "place.h"
24 #include "prompt.h"
25 #include "religion.h"
26 #include "spl-goditem.h" // detect_items
27 #include "stairs.h"
28 #include "state.h"
29 #include "stringutil.h"
30 #include "tag-version.h"
31 #include "terrain.h"
32 #include "tileview.h"
33 #include "tiles-build-specific.h"
34 #include "traps.h"
35 #include "view.h"
36 #include "wiz-mon.h"
37
38 #ifdef WIZARD
_find_appropriate_stairs(bool down)39 static dungeon_feature_type _find_appropriate_stairs(bool down)
40 {
41 if (player_in_branch(BRANCH_PANDEMONIUM))
42 {
43 if (down)
44 return DNGN_TRANSIT_PANDEMONIUM;
45 else
46 return DNGN_EXIT_PANDEMONIUM;
47 }
48
49 int depth = you.depth;
50 if (down)
51 depth++;
52 else
53 depth--;
54
55 // Can't go down from bottom level of a branch.
56 if (depth > brdepth[you.where_are_you])
57 {
58 mpr("Can't go down from the bottom of a branch.");
59 return DNGN_UNSEEN;
60 }
61 // Going up from top level of branch
62 else if (depth == 0)
63 return your_branch().exit_stairs;
64 // Branch non-edge cases
65 else if (depth >= 1)
66 {
67 if (down)
68 return DNGN_STONE_STAIRS_DOWN_I;
69 else
70 return DNGN_ESCAPE_HATCH_UP;
71 }
72 else
73 {
74 mpr("Bug in determining level exit.");
75 return DNGN_UNSEEN;
76 }
77 }
78
wizard_place_stairs(bool down)79 void wizard_place_stairs(bool down)
80 {
81 dungeon_feature_type stairs = _find_appropriate_stairs(down);
82
83 if (stairs == DNGN_UNSEEN)
84 return;
85
86 mprf("Creating %sstairs.", down ? "down" : "up");
87 dungeon_terrain_changed(you.pos(), stairs);
88 }
89
90 static level_id _wizard_level_target = level_id();
91
wizard_level_travel(bool down)92 void wizard_level_travel(bool down)
93 {
94 dungeon_feature_type stairs = _find_appropriate_stairs(down);
95
96 if (stairs == DNGN_UNSEEN)
97 return;
98
99 // This lets us, for example, use &U to exit from Pandemonium and
100 // &D to go to the next level.
101 command_type real_dir = feat_stair_direction(stairs);
102 if (down && real_dir == CMD_GO_UPSTAIRS
103 || !down && real_dir == CMD_GO_DOWNSTAIRS)
104 {
105 down = !down;
106 }
107
108 _wizard_level_target = stair_destination(stairs, "", false);
109
110 if (down)
111 down_stairs(stairs, false, false);
112 else
113 up_stairs(stairs, false);
114
115 _wizard_level_target = level_id();
116 }
117
is_wizard_travel_target(const level_id l)118 bool is_wizard_travel_target(const level_id l)
119 {
120 return _wizard_level_target.is_valid() && l == _wizard_level_target;
121 }
122
_wizard_go_to_level(const level_pos & pos)123 static void _wizard_go_to_level(const level_pos &pos)
124 {
125 dungeon_feature_type stair_taken =
126 absdungeon_depth(pos.id.branch, pos.id.depth) > env.absdepth0 ?
127 DNGN_STONE_STAIRS_DOWN_I : DNGN_STONE_STAIRS_UP_I;
128
129 if (pos.id.depth == brdepth[pos.id.branch])
130 stair_taken = DNGN_STONE_STAIRS_DOWN_I;
131
132 if (!player_in_branch(pos.id.branch) && pos.id.depth == 1
133 && pos.id.branch != BRANCH_DUNGEON)
134 {
135 stair_taken = branches[pos.id.branch].entry_stairs;
136 }
137
138 if (is_connected_branch(pos.id.branch))
139 you.level_stack.clear();
140 else
141 {
142 for (int i = you.level_stack.size() - 1; i >= 0; i--)
143 if (you.level_stack[i].id == pos.id)
144 you.level_stack.resize(i);
145 if (!player_in_branch(pos.id.branch))
146 you.level_stack.push_back(level_pos::current());
147 }
148
149 const level_id old_level = level_id::current();
150
151 you.where_are_you = static_cast<branch_type>(pos.id.branch);
152 you.depth = pos.id.depth;
153 _wizard_level_target = pos.id;
154
155 leaving_level_now(stair_taken);
156 const bool newlevel = load_level(stair_taken, LOAD_ENTER_LEVEL, old_level);
157 tile_new_level(newlevel);
158 if (!crawl_state.test)
159 save_game_state();
160 new_level();
161 seen_monsters_react();
162 viewwindow();
163 update_screen();
164
165 // Tell stash-tracker and travel that we've changed levels.
166 trackers_init_new_level();
167 _wizard_level_target = level_id();
168 }
169
wizard_interlevel_travel()170 void wizard_interlevel_travel()
171 {
172 string name;
173 const level_pos pos =
174 prompt_translevel_target(TPF_ALLOW_UPDOWN | TPF_SHOW_ALL_BRANCHES, name);
175
176 if (pos.id.depth < 1 || pos.id.depth > brdepth[pos.id.branch])
177 {
178 canned_msg(MSG_OK);
179 return;
180 }
181
182 _wizard_go_to_level(pos);
183 }
184
wizard_create_feature(const coord_def & pos)185 bool wizard_create_feature(const coord_def& pos)
186 {
187 const bool mimic = (pos != you.pos());
188 char specs[256];
189 dungeon_feature_type feat;
190 if (mimic)
191 mprf(MSGCH_PROMPT, "Create what kind of feature mimic? ");
192 else
193 mprf(MSGCH_PROMPT, "Create which feature? ");
194
195 if (cancellable_get_line_autohist(specs, sizeof(specs)) || specs[0] == 0)
196 {
197 canned_msg(MSG_OK);
198 return false;
199 }
200
201 if (int feat_num = atoi(specs))
202 feat = static_cast<dungeon_feature_type>(feat_num);
203 else
204 {
205 string name = lowercase_string(specs);
206 name = replace_all(name, " ", "_");
207 feat = dungeon_feature_by_name(name);
208 if (feat == DNGN_UNSEEN) // no exact match
209 {
210 vector<string> matches = dungeon_feature_matches(name);
211
212 if (matches.empty())
213 {
214 const feature_property_type fprop(str_to_fprop(name));
215 if (fprop != FPROP_NONE)
216 {
217 env.pgrid(you.pos()) |= fprop;
218 mprf("Set fprops \"%s\" at (%d,%d)",
219 name.c_str(), you.pos().x, you.pos().y);
220 }
221 else
222 {
223 mprf(MSGCH_DIAGNOSTICS, "No features matching '%s'",
224 name.c_str());
225 }
226 return false;
227 }
228
229 // Only one possible match, use that.
230 if (matches.size() == 1)
231 {
232 name = matches[0];
233 feat = dungeon_feature_by_name(name);
234 }
235 // Multiple matches, list them to wizard
236 else
237 {
238 string prefix = "No exact match for feature '" +
239 name + "', possible matches are: ";
240
241 // Use mpr_comma_separated_list() because the list
242 // might be *LONG*.
243 mpr_comma_separated_list(prefix, matches, " and ", ", ",
244 MSGCH_DIAGNOSTICS);
245 return wizard_create_feature(pos);
246 }
247 }
248 }
249
250 if (mimic && !feat_is_mimicable(feat, false)
251 && !yesno("This isn't a valid feature mimic. Create it anyway? ",
252 true, 'n'))
253 {
254 canned_msg(MSG_OK);
255 return false;
256 }
257
258 if (feat == DNGN_ENTER_SHOP)
259 return debug_make_shop(pos);
260
261 if (feat_is_trap(feat))
262 return debug_make_trap(pos);
263
264 tile_env.flv(pos).feat = 0;
265 tile_env.flv(pos).special = 0;
266 env.grid_colours(pos) = 0;
267 const dungeon_feature_type old_feat = env.grid(pos);
268 dungeon_terrain_changed(pos, feat, false, false, false, true);
269 // Update gate tiles, if existing.
270 if (feat_is_door(old_feat) || feat_is_door(feat))
271 {
272 const coord_def left = pos - coord_def(1, 0);
273 const coord_def right = pos + coord_def(1, 0);
274 if (map_bounds(left) && feat_is_door(env.grid(left)))
275 tile_init_flavour(left);
276 if (map_bounds(right) && feat_is_door(env.grid(right)))
277 tile_init_flavour(right);
278 }
279 if (pos == you.pos() && cell_is_solid(pos))
280 you.wizmode_teleported_into_rock = true;
281
282 if (mimic)
283 env.level_map_mask(pos) |= MMT_MIMIC;
284
285 if (you.see_cell(pos))
286 view_update_at(pos);
287
288 return true;
289 }
290
wizard_list_branches()291 void wizard_list_branches()
292 {
293 for (branch_iterator it; it; ++it)
294 {
295 if (parent_branch(it->id) == NUM_BRANCHES)
296 continue;
297 else if (brentry[it->id].is_valid())
298 {
299 mprf(MSGCH_DIAGNOSTICS, "Branch %d (%s) is on %s",
300 it->id, it->longname, brentry[it->id].describe().c_str());
301 }
302 else if (is_random_subbranch(it->id))
303 {
304 mprf(MSGCH_DIAGNOSTICS, "Branch %d (%s) was not generated "
305 "this game", it->id, it->longname);
306 }
307 }
308
309 if (!you.props.exists(OVERFLOW_TEMPLES_KEY))
310 return;
311
312 mprf(MSGCH_DIAGNOSTICS, "----");
313 mprf(MSGCH_DIAGNOSTICS, "Overflow temples: ");
314
315 CrawlVector &levels = you.props[OVERFLOW_TEMPLES_KEY].get_vector();
316
317 vector<string> temple_strings;
318 vector<string> god_names;
319
320 for (unsigned int i = 0; i < levels.size(); i++)
321 {
322 CrawlStoreValue &val = levels[i];
323
324 // Does this level have an overflow temple?
325 if (val.get_flags() & SFLAG_UNSET)
326 continue;
327
328 CrawlVector &temples = val.get_vector();
329
330 if (temples.empty())
331 continue;
332
333 temple_strings.clear();
334
335 for (CrawlHashTable &temple_hash : temples)
336 {
337 god_names.clear();
338 CrawlVector &gods = temple_hash[TEMPLE_GODS_KEY];
339
340 for (unsigned int k = 0; k < gods.size(); k++)
341 {
342 god_type god = (god_type) gods[k].get_byte();
343
344 god_names.push_back(god_name(god));
345 }
346 temple_strings.push_back(
347 comma_separated_line(god_names.begin(), god_names.end()));
348 }
349
350 mprf(MSGCH_DIAGNOSTICS, "%u on D:%u (%s)", temples.size(),
351 i + 1,
352 comma_separated_line(temple_strings.begin(),
353 temple_strings.end(), "; ", "; ").c_str()
354 );
355 }
356 }
357
wizard_map_level()358 void wizard_map_level()
359 {
360 if (!is_map_persistent())
361 {
362 if (!yesno("Force level to be mappable?", true, 'n'))
363 {
364 canned_msg(MSG_OK);
365 return;
366 }
367 env.properties[FORCE_MAPPABLE_KEY] = true;
368 }
369
370 mpr("Mapping level.");
371 magic_mapping(1000, 100, true, true);
372
373 for (rectangle_iterator ri(BOUNDARY_BORDER - 1); ri; ++ri)
374 {
375 update_item_at(*ri, true);
376 show_update_at(*ri, LAYER_ITEMS);
377
378 #ifdef USE_TILE
379 tiles.update_minimap(*ri);
380 tile_draw_map_cell(*ri, true);
381 #endif
382 #ifdef USE_TILE_WEB
383 tiles.mark_for_redraw(*ri);
384 #endif
385 }
386 }
387
debug_make_trap(const coord_def & pos)388 bool debug_make_trap(const coord_def& pos)
389 {
390 char requested_trap[80];
391 trap_type trap = TRAP_UNASSIGNED;
392 int gridch = env.grid(pos);
393
394 if (gridch != DNGN_FLOOR)
395 {
396 mpr("You need to be on a floor square to make a trap.");
397 return false;
398 }
399
400 msgwin_get_line("What kind of trap? ",
401 requested_trap, sizeof(requested_trap));
402 if (!*requested_trap)
403 {
404 canned_msg(MSG_OK);
405 return false;
406 }
407
408 string spec = lowercase_string(requested_trap);
409 vector<trap_type> matches;
410 vector<string> match_names;
411
412 if (spec == "random" || spec == "any")
413 trap = TRAP_RANDOM;
414
415 for (int t =
416 #if TAG_MAJOR_VERSION == 34
417 TRAP_DART
418 #else
419 TRAP_ARROW
420 #endif
421 ; t < NUM_TRAPS; ++t)
422 {
423 const trap_type tr = static_cast<trap_type>(t);
424 string tname = lowercase_string(trap_name(tr));
425 if (spec.find(tname) != spec.npos)
426 {
427 trap = tr;
428 break;
429 }
430 else if (tname.find(spec) != tname.npos)
431 {
432 matches.push_back(tr);
433 match_names.push_back(tname);
434 }
435 }
436
437 if (trap == TRAP_UNASSIGNED)
438 {
439 if (matches.empty())
440 {
441 mprf("I know no traps named \"%s\".", spec.c_str());
442 return false;
443 }
444 // Only one match, use that
445 else if (matches.size() == 1)
446 trap = matches[0];
447 else
448 {
449 string prefix = "No exact match for trap '";
450 prefix += spec;
451 prefix += "', possible matches are: ";
452 mpr_comma_separated_list(prefix, match_names);
453 return false;
454 }
455 }
456
457 place_specific_trap(you.pos(), trap);
458 mprf("Created %s, marked it undiscovered.",
459 (trap == TRAP_RANDOM)
460 ? "a random trap"
461 : trap_at(you.pos())->name(DESC_A).c_str());
462
463 if (trap == TRAP_SHAFT && !is_valid_shaft_level())
464 mpr("NOTE: Shaft traps aren't valid on this level.");
465
466 return true;
467 }
468
debug_make_shop(const coord_def & pos)469 bool debug_make_shop(const coord_def& pos)
470 {
471 if (env.grid(pos) != DNGN_FLOOR)
472 {
473 mpr("Insufficient floor-space for new Wal-Mart.");
474 return false;
475 }
476
477 char requested_shop[80];
478 msgwin_get_line("What kind of shop? ",
479 requested_shop, sizeof(requested_shop));
480 if (!*requested_shop)
481 {
482 canned_msg(MSG_OK);
483 return false;
484 }
485
486 const shop_type new_shop_type = str_to_shoptype(requested_shop);
487
488 if (new_shop_type == SHOP_UNASSIGNED)
489 {
490 mprf("Bad shop type: \"%s\"", requested_shop);
491 list_shop_types();
492 return false;
493 }
494
495 place_spec_shop(pos, new_shop_type);
496 mpr("Done.");
497 return true;
498 }
499
_free_all_vaults()500 static void _free_all_vaults()
501 {
502 for (rectangle_iterator ri(MAPGEN_BORDER); ri; ++ri)
503 env.level_map_ids(*ri) = INVALID_MAP_INDEX;
504
505 for (auto &vp : env.level_vaults)
506 vp->seen = false;
507
508 dgn_erase_unused_vault_placements();
509 }
510
debug_load_map_by_name(string name,bool primary)511 static void debug_load_map_by_name(string name, bool primary)
512 {
513 if (primary)
514 _free_all_vaults();
515
516 const bool place_on_us = !primary && strip_tag(name, "*", true);
517
518 level_clear_vault_memory();
519 const map_def *toplace = find_map_by_name(name);
520 if (!toplace)
521 {
522 vector<string> matches = find_map_matches(name);
523
524 if (matches.empty())
525 {
526 mprf("Can't find map named '%s'.", name.c_str());
527 return;
528 }
529 else if (matches.size() == 1)
530 {
531 string prompt = "Only match is '";
532 prompt += matches[0];
533 prompt += "', use that?";
534 if (!yesno(prompt.c_str(), true, 'y'))
535 {
536 canned_msg(MSG_OK);
537 return;
538 }
539
540 toplace = find_map_by_name(matches[0]);
541 }
542 else
543 {
544 string prompt = "No exact matches for '";
545 prompt += name;
546 prompt += "', possible matches are: ";
547 mpr_comma_separated_list(prompt, matches);
548 return;
549 }
550 }
551
552 coord_def where(-1, -1);
553 if (place_on_us)
554 {
555 if (toplace->orient == MAP_FLOAT || toplace->orient == MAP_NONE)
556 {
557 coord_def size = toplace->map.size();
558 coord_def tl = you.pos() - (size / 2) - coord_def(-1, -1);
559 coord_def br = you.pos() + (size / 2) + coord_def(-1, -1);
560
561 for (rectangle_iterator ri(tl, br); ri; ++ri)
562 {
563 if (!in_bounds(*ri))
564 {
565 mprf("Placing %s on top of you would put part of the "
566 "map outside of the level, cancelling.",
567 toplace->name.c_str());
568 return;
569 }
570 }
571 // We're okay.
572 where = you.pos();
573 }
574 else
575 {
576 mprf("%s decides where it goes, can't place where you are.",
577 toplace->name.c_str());
578 }
579 }
580
581 if (primary)
582 {
583 if (toplace->orient == MAP_ENCOMPASS
584 && !toplace->is_usable_in(level_id::current())
585 && !yesno("Warning: this is an encompass vault not designed "
586 "for this location; placing it with &P may result in "
587 "crashes and save corruption. Continue?", true, 'y'))
588 {
589 mprf("Ok; try placing with &L or go to the relevant location to "
590 "safely place with &P.");
591 return;
592 }
593 if (toplace->is_minivault())
594 you.props["force_minivault"] = toplace->name;
595 else
596 you.props["force_map"] = toplace->name;
597 wizard_recreate_level();
598 you.props.erase("force_minivault");
599 you.props.erase("force_map");
600
601 // We just saved with you.props["force_map"] set; save again in
602 // case we crash (lest we have the property forever).
603 if (!crawl_state.test)
604 save_game_state();
605 }
606 else
607 {
608 unwind_var<string_set> um(you.uniq_map_names, string_set());
609 unwind_var<string_set> umt(you.uniq_map_tags, string_set());
610 unwind_var<string_set> um_a(you.uniq_map_names_abyss, string_set());
611 unwind_var<string_set> umt_a(you.uniq_map_tags_abyss, string_set());
612 unwind_var<string_set> lum(env.level_uniq_maps, string_set());
613 unwind_var<string_set> lumt(env.level_uniq_map_tags, string_set());
614 if (dgn_place_map(toplace, false, false, where))
615 {
616 mprf("Successfully placed %s.", toplace->name.c_str());
617 // Fix up doors from vaults and any changes to the default walls
618 // and floors from the vault.
619 tile_init_flavour();
620 // Transporters would normally be made from map markers by the
621 // builder.
622 dgn_make_transporters_from_markers();
623 }
624 else
625 {
626 mprf("Failed to place %s; last builder error: %s",
627 toplace->name.c_str(), crawl_state.last_builder_error.c_str());
628 }
629 }
630 }
631
632 static input_history mini_hist(10), primary_hist(10);
633
debug_place_map(bool primary)634 void debug_place_map(bool primary)
635 {
636 char what_to_make[100];
637 clear_messages();
638 mprf(MSGCH_PROMPT, primary ? "Enter map name: " :
639 "Enter map name (prefix it with * for local placement): ");
640 if (cancellable_get_line(what_to_make, sizeof what_to_make,
641 primary ? &primary_hist : &mini_hist))
642 {
643 canned_msg(MSG_OK);
644 return;
645 }
646
647 string what = what_to_make;
648 trim_string(what);
649 if (what.empty())
650 {
651 canned_msg(MSG_OK);
652 return;
653 }
654
655 debug_load_map_by_name(what, primary);
656 }
657
_debug_kill_traps()658 static void _debug_kill_traps()
659 {
660 for (rectangle_iterator ri(1); ri; ++ri)
661 if (feat_is_trap(env.grid(*ri)))
662 destroy_trap(*ri);
663 }
664
_debug_time_explore()665 static int _debug_time_explore()
666 {
667 viewwindow();
668 update_screen();
669 start_explore(false);
670
671 unwind_var<int> es(Options.explore_stop, 0);
672
673 const int start = you.num_turns;
674 while (you_are_delayed())
675 {
676 you.turn_is_over = false;
677 handle_delay();
678 you.num_turns++;
679 }
680
681 // Elapsed time might not match up if explore had to go through
682 // shallow water.
683 PlaceInfo& pi = you.get_place_info();
684 pi.elapsed_total = (pi.elapsed_explore + pi.elapsed_travel +
685 pi.elapsed_interlevel + pi.elapsed_resting +
686 pi.elapsed_other);
687
688 PlaceInfo& pi2 = you.global_info;
689 pi2.elapsed_total = (pi2.elapsed_explore + pi2.elapsed_travel +
690 pi2.elapsed_interlevel + pi2.elapsed_resting +
691 pi2.elapsed_other);
692
693 return you.num_turns - start;
694 }
695
_debug_destroy_doors()696 static void _debug_destroy_doors()
697 {
698 for (int y = 0; y < GYM; ++y)
699 for (int x = 0; x < GXM; ++x)
700 {
701 const dungeon_feature_type feat = env.grid[x][y];
702 if (feat_is_closed_door(feat))
703 env.grid[x][y] = DNGN_FLOOR;
704 }
705 }
706
707 // Turns off greedy explore, then:
708 // a) Destroys all traps on the level.
709 // b) Kills all monsters on the level.
710 // c) Suppresses monster generation.
711 // d) Converts all closed doors to floor.
712 // e) Forgets map.
713 // f) Counts number of turns needed to explore the level.
debug_test_explore()714 void debug_test_explore()
715 {
716 wizard_dismiss_all_monsters(true);
717 _debug_kill_traps();
718 _debug_destroy_doors();
719
720 forget_map();
721
722 // Remember where we are now.
723 const coord_def where = you.pos();
724
725 const int explore_turns = _debug_time_explore();
726
727 // Return to starting point.
728 you.moveto(where);
729
730 mprf("Explore took %d turns.", explore_turns);
731 }
732
wizard_list_levels()733 void wizard_list_levels()
734 {
735 if (!you.level_stack.empty())
736 {
737 mpr("Level stack:");
738 for (unsigned int i = 0; i < you.level_stack.size(); i++)
739 {
740 mprf(MSGCH_DIAGNOSTICS, i+1, // inhibit merging
741 "%-10s (%d,%d)", you.level_stack[i].id.describe().c_str(),
742 you.level_stack[i].pos.x, you.level_stack[i].pos.y);
743 }
744 }
745
746 travel_cache.update_daction_counters();
747
748 vector<level_id> levs = travel_cache.known_levels();
749
750 mpr("Known levels:");
751 for (unsigned int i = 0; i < levs.size(); i++)
752 {
753 const LevelInfo* lv = travel_cache.find_level_info(levs[i]);
754 ASSERT(lv);
755
756 string cnts = "";
757 for (int j = 0; j < NUM_DACTION_COUNTERS; j++)
758 {
759 char num[20];
760 sprintf(num, "%d/", lv->daction_counters[j]);
761 cnts += num;
762 }
763 mprf(MSGCH_DIAGNOSTICS, i+1, // inhibit merging
764 "%-10s : %s", levs[i].describe().c_str(), cnts.c_str());
765 }
766
767 string cnts = "";
768 for (int j = 0; j < NUM_DACTION_COUNTERS; j++)
769 {
770 char num[20];
771 sprintf(num, "%d/", query_daction_counter((daction_type)j));
772 cnts += num;
773 }
774 mprf("%-10s : %s", "`- total", cnts.c_str());
775 }
776
wizard_recreate_level()777 void wizard_recreate_level()
778 {
779 mpr("Regenerating level.");
780
781 // Need to allow reuse of vaults, otherwise we'd run out of them fast.
782 #ifndef DEBUG_VETO_RESUME
783 _free_all_vaults();
784 #endif
785
786 for (monster_iterator mi; mi; ++mi)
787 {
788 if (mons_is_unique(mi->type))
789 {
790 remove_unique_annotation(*mi);
791 you.unique_creatures.set(mi->type, false);
792 mi->flags |= MF_TAKING_STAIRS; // no Abyss transit
793 }
794 }
795
796 level_id lev = level_id::current();
797 _wizard_level_target = lev;
798 dungeon_feature_type stair_taken = DNGN_STONE_STAIRS_DOWN_I;
799
800 if (lev.depth == 1 && lev != BRANCH_DUNGEON)
801 stair_taken = branches[lev.branch].entry_stairs;
802
803 leaving_level_now(stair_taken);
804 delete_level(lev);
805 const bool newlevel = load_level(stair_taken, LOAD_START_GAME, lev);
806 you.get_place_info().levels_seen--;
807 tile_new_level(newlevel);
808 if (!crawl_state.test)
809 save_game_state();
810 new_level();
811 seen_monsters_react();
812 viewwindow();
813 update_screen();
814
815 trackers_init_new_level();
816 }
817
wizard_clear_used_vaults()818 void wizard_clear_used_vaults()
819 {
820 you.uniq_map_tags.clear();
821 you.uniq_map_names.clear();
822 you.uniq_map_tags_abyss.clear();
823 you.uniq_map_names_abyss.clear();
824 env.level_uniq_maps.clear();
825 env.level_uniq_map_tags.clear();
826 mpr("All vaults are now eligible for [re]use.");
827 }
828
wizard_abyss_speed()829 void wizard_abyss_speed()
830 {
831 char specs[256];
832 mprf(MSGCH_PROMPT, "Set Abyss speed to what? (now %d, higher value = "
833 "higher speed) ", you.abyss_speed);
834
835 if (!cancellable_get_line(specs, sizeof(specs)))
836 {
837 const int speed = atoi(specs);
838 if (speed || specs[0] == '0')
839 {
840 mprf("Setting Abyss speed to %i.", speed);
841 you.abyss_speed = speed;
842 return;
843 }
844 }
845
846 canned_msg(MSG_OK);
847 }
848 #endif
849