1 /**
2 * @file
3 * @brief Monster related debugging functions.
4 **/
5
6 #include "AppHdr.h"
7
8 #include "wiz-mon.h"
9
10 #include <sstream>
11
12 #include "abyss.h"
13 #include "act-iter.h"
14 #include "areas.h"
15 #include "cloud.h"
16 #include "colour.h"
17 #include "command.h"
18 #include "dbg-util.h"
19 #include "delay.h"
20 #include "directn.h"
21 #include "dungeon.h"
22 #include "english.h"
23 #include "files.h"
24 #include "ghost.h"
25 #include "god-blessing.h"
26 #include "invent.h"
27 #include "item-prop.h"
28 #include "items.h"
29 #include "jobs.h"
30 #include "libutil.h"
31 #include "macro.h"
32 #include "message.h"
33 #include "mon-death.h"
34 #include "mon-pathfind.h"
35 #include "mon-place.h"
36 #include "mon-poly.h"
37 #include "mon-speak.h"
38 #include "output.h"
39 #include "prompt.h"
40 #include "religion.h"
41 #include "shout.h"
42 #include "spl-miscast.h"
43 #include "state.h"
44 #include "stringutil.h"
45 #include "terrain.h"
46 #include "view.h"
47 #include "viewmap.h"
48
49 #ifdef WIZARD
50
51 // Creates a specific monster by name. Uses the same patterns as
52 // map definitions.
wizard_create_spec_monster_name()53 void wizard_create_spec_monster_name()
54 {
55 char specs[1024];
56 mprf(MSGCH_PROMPT, "Enter monster name (or MONS spec) (? for help): ");
57 if (cancellable_get_line_autohist(specs, sizeof specs) || !*specs)
58 {
59 canned_msg(MSG_OK);
60 return;
61 }
62
63 if (!strcmp(specs, "?"))
64 {
65 show_specific_help("wiz-monster");
66 return;
67 }
68
69 mons_list mlist;
70 string err = mlist.add_mons(specs);
71
72 if (!err.empty())
73 {
74 string newerr = "yes";
75 // Try for a partial match, but not if the user accidentally entered
76 // only a few letters.
77 monster_type partial = get_monster_by_name(specs, true);
78 if (strlen(specs) >= 3 && partial != MONS_PROGRAM_BUG)
79 {
80 mlist.clear();
81 newerr = mlist.add_mons(mons_type_name(partial, DESC_PLAIN));
82 }
83
84 if (!newerr.empty())
85 {
86 mpr(err);
87 return;
88 }
89 }
90
91 mons_spec mspec = mlist.get_monster(0);
92 if (mspec.type == MONS_NO_MONSTER)
93 {
94 mprf(MSGCH_DIAGNOSTICS, "Such a monster couldn't be found.");
95 return;
96 }
97
98 monster_type type =
99 fixup_zombie_type(static_cast<monster_type>(mspec.type),
100 mspec.monbase);
101
102 coord_def place = find_newmons_square(type, you.pos());
103 if (!in_bounds(place))
104 {
105 // Try again with habitat HT_LAND.
106 // (Will be changed to the necessary terrain type in dgn_place_monster.)
107 place = find_newmons_square(MONS_NO_MONSTER, you.pos());
108 }
109
110 if (!in_bounds(place))
111 {
112 mprf(MSGCH_DIAGNOSTICS, "Found no space to place monster.");
113 return;
114 }
115
116 // Wizmode users should be able to conjure up uniques even if they
117 // were already created. Yay, you can meet 3 Sigmunds at once! :p
118 if (mons_is_unique(type) && you.unique_creatures[type])
119 you.unique_creatures.set(type, false);
120
121 if (mons_class_requires_band(type) && !mspec.band)
122 {
123 mprf(MSGCH_DIAGNOSTICS,
124 "That monster can only be created with a band.");
125 return;
126 }
127
128 if (!dgn_place_monster(mspec, place, true, false))
129 {
130 mprf(MSGCH_DIAGNOSTICS, "Unable to place monster.");
131 return;
132 }
133
134 // FIXME: This is a bit useless, seeing how you cannot set the
135 // ghost's stats, brand or level, among other things.
136 if (mspec.type == MONS_PLAYER_GHOST)
137 {
138 unsigned short idx = env.mgrid(place);
139
140 if (idx >= MAX_MONSTERS || env.mons[idx].type != MONS_PLAYER_GHOST)
141 {
142 for (idx = 0; idx < MAX_MONSTERS; idx++)
143 {
144 if (env.mons[idx].type == MONS_PLAYER_GHOST
145 && env.mons[idx].alive())
146 {
147 break;
148 }
149 }
150 }
151
152 if (idx >= MAX_MONSTERS)
153 {
154 mpr("Couldn't find player ghost, probably going to crash.");
155 more();
156 return;
157 }
158
159 monster &mon = env.mons[idx];
160 ghost_demon ghost;
161
162 ghost.name = "John Doe";
163
164 char input_str[80];
165 msgwin_get_line("Make player ghost which species? (case-sensitive) ",
166 input_str, sizeof(input_str));
167
168 species_type sp_id = species::from_abbrev(input_str);
169 if (sp_id == SP_UNKNOWN)
170 sp_id = species::from_str(input_str);
171 if (sp_id == SP_UNKNOWN)
172 {
173 mpr("No such species, making it Human.");
174 sp_id = SP_HUMAN;
175 }
176 ghost.species = static_cast<species_type>(sp_id);
177
178 msgwin_get_line("Give player ghost which background? ",
179 input_str, sizeof(input_str));
180
181 int job_id = get_job_by_abbrev(input_str);
182
183 if (job_id == JOB_UNKNOWN)
184 job_id = get_job_by_name(input_str);
185
186 if (job_id == JOB_UNKNOWN)
187 {
188 mpr("No such background, making it a Fighter.");
189 job_id = JOB_FIGHTER;
190 }
191 ghost.job = static_cast<job_type>(job_id);
192 ghost.xl = 7;
193 ghost.max_hp = 20;
194 ASSERT(debug_check_ghost(ghost));
195
196 mon.set_ghost(ghost);
197 }
198 }
199
_sort_monster_list(int a,int b)200 static bool _sort_monster_list(int a, int b)
201 {
202 const monster* m1 = &env.mons[a];
203 const monster* m2 = &env.mons[b];
204
205 if (m1->alive() != m2->alive())
206 return m1->alive();
207 else if (!m1->alive())
208 return a < b;
209
210 if (m1->type == m2->type)
211 {
212 if (!m1->alive() || !m2->alive())
213 return false;
214
215 return m1->name(DESC_PLAIN, true) < m2->name(DESC_PLAIN, true);
216 }
217
218 const unsigned glyph1 = mons_char(m1->type);
219 const unsigned glyph2 = mons_char(m2->type);
220 if (glyph1 != glyph2)
221 return glyph1 < glyph2;
222
223 return m1->type < m2->type;
224 }
225
debug_list_monsters()226 void debug_list_monsters()
227 {
228 vector<string> mons;
229 int nfound = 0;
230
231 int mon_nums[MAX_MONSTERS];
232
233 for (int i = 0; i < MAX_MONSTERS; ++i)
234 mon_nums[i] = i;
235
236 sort(mon_nums, mon_nums + MAX_MONSTERS, _sort_monster_list);
237
238 int total_exp = 0, total_adj_exp = 0, total_nonuniq_exp = 0;
239
240 string prev_name = "";
241 int count = 0;
242
243 for (int i = 0; i < MAX_MONSTERS; ++i)
244 {
245 const int idx = mon_nums[i];
246 if (invalid_monster_index(idx))
247 continue;
248
249 const monster* mi(&env.mons[idx]);
250 if (!mi->alive())
251 continue;
252
253 string name = mi->name(DESC_PLAIN, true);
254
255 if (prev_name != name && count > 0)
256 {
257 char buf[80];
258 if (count > 1)
259 {
260 snprintf(buf, sizeof(buf), "%d %s", count,
261 pluralise_monster(prev_name).c_str());
262 }
263 else
264 snprintf(buf, sizeof(buf), "%s", prev_name.c_str());
265 mons.push_back(buf);
266
267 count = 0;
268 }
269 nfound++;
270 count++;
271 prev_name = name;
272
273 int exp = exper_value(*mi);
274 total_exp += exp;
275 if (!mons_is_unique(mi->type))
276 total_nonuniq_exp += exp;
277
278 if ((mi->flags & (MF_WAS_NEUTRAL | MF_NO_REWARD))
279 || mi->has_ench(ENCH_ABJ))
280 {
281 continue;
282 }
283 if (mi->flags & MF_PACIFIED)
284 exp /= 2;
285
286 total_adj_exp += exp;
287 }
288
289 char buf[80];
290 if (count > 1)
291 {
292 snprintf(buf, sizeof(buf), "%d %s", count,
293 pluralise_monster(prev_name).c_str());
294 }
295 else
296 snprintf(buf, sizeof(buf), "%s", prev_name.c_str());
297 mons.emplace_back(buf);
298
299 mpr_comma_separated_list("Monsters: ", mons);
300
301 if (total_adj_exp == total_exp)
302 {
303 mprf("%d monsters, %d total exp value (%d non-uniq)",
304 nfound, total_exp, total_nonuniq_exp);
305 }
306 else
307 {
308 mprf("%d monsters, %d total exp value (%d non-uniq, %d adjusted)",
309 nfound, total_exp, total_nonuniq_exp, total_adj_exp);
310 }
311 }
312
wizard_spawn_control()313 void wizard_spawn_control()
314 {
315 mprf(MSGCH_PROMPT, "(c)hange spawn rate or (s)pawn monsters? ");
316 const int c = toalower(getchm());
317
318 char specs[256];
319 bool done = false;
320
321 if (c == 'c')
322 {
323 mprf(MSGCH_PROMPT, "Set monster spawn rate to what? (now %d, lower value = higher rate) ",
324 env.spawn_random_rate);
325
326 if (!cancellable_get_line(specs, sizeof(specs)))
327 {
328 const int rate = atoi(specs);
329 if (rate || specs[0] == '0')
330 {
331 mprf("Setting monster spawn rate to %i.", rate);
332 env.spawn_random_rate = rate;
333 done = true;
334 }
335 }
336 }
337 else if (c == 's')
338 {
339 // 50 spots are reserved for non-wandering monsters.
340 int max_spawn = MAX_MONSTERS - 50;
341 for (monster_iterator mi; mi; ++mi)
342 if (mi->alive())
343 max_spawn--;
344
345 if (max_spawn <= 0)
346 {
347 mprf(MSGCH_PROMPT, "Level already filled with monsters, "
348 "get rid of some of them first.");
349 return;
350 }
351
352 mprf(MSGCH_PROMPT, "Spawn how many random monsters (max %d)? ",
353 max_spawn);
354
355 if (!cancellable_get_line(specs, sizeof(specs)))
356 {
357 const int num = min(atoi(specs), max_spawn);
358 if (num > 0)
359 {
360 mprf("Spawning %i monster%s.", num, num == 1 ? "" : "s");
361 int curr_rate = env.spawn_random_rate;
362 // Each call to spawn_random_monsters() will spawn one with
363 // the rate at 5 or less.
364 env.spawn_random_rate = 5;
365
366 for (int i = 0; i < num; ++i)
367 spawn_random_monsters();
368
369 env.spawn_random_rate = curr_rate;
370 done = true;
371 }
372 }
373 }
374
375 if (!done)
376 canned_msg(MSG_OK);
377 }
378
379 static const char* ht_names[] =
380 {
381 "land",
382 "amphibious",
383 "water",
384 "lava",
385 "amphibious_lava",
386 };
387
388 // Prints a number of useful (for debugging, that is) stats on monsters.
debug_stethoscope(int mon)389 void debug_stethoscope(int mon)
390 {
391 dist stth;
392 coord_def stethpos;
393
394 int i;
395
396 if (mon != RANDOM_MONSTER)
397 i = mon;
398 else
399 {
400 mprf(MSGCH_PROMPT, "Which monster?");
401
402 direction(stth, direction_chooser_args());
403
404 if (!stth.isValid)
405 return;
406
407 if (stth.isTarget)
408 stethpos = stth.target;
409 else
410 stethpos = you.pos() + stth.delta;
411
412 if (cloud_struct* cloud = cloud_at(stethpos))
413 {
414 mprf(MSGCH_DIAGNOSTICS, "cloud type: %d delay: %d",
415 cloud->type, cloud->decay);
416 }
417
418 if (!monster_at(stethpos))
419 {
420 mprf(MSGCH_DIAGNOSTICS, "item grid = %d", env.igrid(stethpos));
421 return;
422 }
423
424 i = env.mgrid(stethpos);
425 }
426
427 monster& mons(env.mons[i]);
428
429 // Print type of monster.
430 mprf(MSGCH_DIAGNOSTICS, "%s (id #%d; type=%d loc=(%d,%d) align=%s)",
431 mons.name(DESC_THE, true).c_str(),
432 i, mons.type, mons.pos().x, mons.pos().y,
433 ((mons.attitude == ATT_HOSTILE) ? "hostile" :
434 (mons.attitude == ATT_FRIENDLY) ? "friendly" :
435 (mons.attitude == ATT_NEUTRAL) ? "neutral" :
436 (mons.attitude == ATT_GOOD_NEUTRAL) ? "good neutral":
437 (mons.attitude == ATT_STRICT_NEUTRAL) ? "strictly neutral"
438 : "unknown alignment"));
439
440 // Print stats and other info.
441 mprf(MSGCH_DIAGNOSTICS,
442 "HD=%d/%d (%u) HP=%d/%d AC=%d(%d) EV=%d(%d) WL=%d XP=%d SP=%d "
443 "energy=%d%s%s mid=%u num=%d stealth=%d flags=%04" PRIx64,
444 mons.get_hit_dice(),
445 mons.get_experience_level(),
446 mons.experience,
447 mons.hit_points, mons.max_hit_points,
448 mons.base_armour_class(), mons.armour_class(),
449 mons.base_evasion(), mons.evasion(),
450 mons.willpower(),
451 exper_value(mons),
452 mons.speed, mons.speed_increment,
453 mons.base_monster != MONS_NO_MONSTER ? " base=" : "",
454 mons.base_monster != MONS_NO_MONSTER ?
455 get_monster_data(mons.base_monster)->name : "",
456 mons.mid, mons.number, mons.stealth(), mons.flags.flags);
457
458 if (mons.damage_total)
459 {
460 mprf(MSGCH_DIAGNOSTICS,
461 "pdam=%1.1f/%d (%d%%)",
462 0.5 * mons.damage_friendly, mons.damage_total,
463 50 * mons.damage_friendly / mons.damage_total);
464 }
465
466 // Print habitat and behaviour information.
467 const habitat_type hab = mons_habitat(mons);
468
469 COMPILE_CHECK(ARRAYSZ(ht_names) == NUM_HABITATS);
470 const actor * const summoner = actor_by_mid(mons.summoner);
471 mprf(MSGCH_DIAGNOSTICS,
472 "hab=%s beh=%s(%d) foe=%s(%d) mem=%d target=(%d,%d) "
473 "firing_pos=(%d,%d) patrol_point=(%d,%d) god=%s%s",
474 (hab >= 0 && hab < NUM_HABITATS) ? ht_names[hab] : "INVALID",
475 mons.asleep() ? "sleep"
476 : mons_is_wandering(mons) ? "wander"
477 : mons_is_seeking(mons) ? "seek"
478 : mons_is_fleeing(mons) ? "flee"
479 : mons.behaviour == BEH_RETREAT ? "retreat"
480 : mons_is_cornered(mons) ? "cornered"
481 : mons.behaviour == BEH_WITHDRAW ? "withdraw"
482 : "unknown",
483 mons.behaviour,
484 mons.foe == MHITYOU ? "you"
485 : mons.foe == MHITNOT ? "none"
486 : env.mons[mons.foe].type == MONS_NO_MONSTER ? "unassigned monster"
487 : env.mons[mons.foe].name(DESC_PLAIN, true).c_str(),
488 mons.foe,
489 mons.foe_memory,
490 mons.target.x, mons.target.y,
491 mons.firing_pos.x, mons.firing_pos.y,
492 mons.patrol_point.x, mons.patrol_point.y,
493 god_name(mons.god).c_str(),
494 (summoner ? make_stringf(" summoner=%s(%d)",
495 summoner->name(DESC_PLAIN, true).c_str(),
496 summoner->mindex()).c_str()
497 : ""));
498
499 // Print resistances.
500 mprf(MSGCH_DIAGNOSTICS, "resist: fire=%d cold=%d elec=%d pois=%d neg=%d "
501 "acid=%d sticky=%s miasma=%s",
502 mons.res_fire(),
503 mons.res_cold(),
504 mons.res_elec(),
505 mons.res_poison(),
506 mons.res_negative_energy(),
507 mons.res_acid(),
508 mons.res_sticky_flame() ? "yes" : "no",
509 mons.res_miasma() ? "yes" : "no");
510
511 mprf(MSGCH_DIAGNOSTICS, "ench: %s",
512 mons.describe_enchantments().c_str());
513
514 mprf(MSGCH_DIAGNOSTICS, "props: %s",
515 mons.describe_props().c_str());
516
517 ostringstream spl;
518 const monster_spells &hspell_pass = mons.spells;
519 bool found_spell = false;
520 for (unsigned int k = 0; k < hspell_pass.size(); ++k)
521 {
522 if (found_spell)
523 spl << ", ";
524
525 found_spell = true;
526
527 spl << k << ": ";
528
529 if (hspell_pass[k].spell >= NUM_SPELLS)
530 spl << "buggy spell";
531 else
532 spl << spell_title(hspell_pass[k].spell);
533
534 spl << "." << (int)hspell_pass[k].freq;
535 for (const auto flag : mon_spell_slot_flags::range())
536 {
537 if (!(hspell_pass[k].flags & flag))
538 continue;
539
540 // this is arguably redundant with mons_list::parse_mons_spells
541 // specificially the bit that turns names into flags
542 static const map<mon_spell_slot_flag, string> flagnames = {
543 { MON_SPELL_EMERGENCY, "E" },
544 { MON_SPELL_NATURAL, "N" },
545 { MON_SPELL_MAGICAL, "M" },
546 { MON_SPELL_WIZARD, "W" },
547 { MON_SPELL_PRIEST, "P" },
548 { MON_SPELL_VOCAL, "V" },
549 { MON_SPELL_BREATH, "br" },
550 { MON_SPELL_INSTANT, "in" },
551 { MON_SPELL_NOISY, "noi" },
552 };
553 spl << "." << lookup(flagnames, flag, "bug");
554 }
555
556 spl << " (#" << static_cast<int>(hspell_pass[k].spell) << ")";
557 }
558 if (found_spell)
559 mprf(MSGCH_DIAGNOSTICS, "spells: %s", spl.str().c_str());
560
561 ostringstream inv;
562 bool found_item = false;
563 for (mon_inv_iterator ii(mons); ii; ++ii)
564 {
565 if (found_item)
566 inv << ", ";
567
568 found_item = true;
569
570 inv << ii.slot() << ": ";
571
572 inv << item_base_name(*ii);
573
574 inv << " (" << static_cast<int>(ii->index()) << ")";
575 }
576 if (found_item)
577 mprf(MSGCH_DIAGNOSTICS, "inv: %s", inv.str().c_str());
578
579 if (mons_is_ghost_demon(mons.type))
580 {
581 ASSERT(mons.ghost);
582 const ghost_demon &ghost = *mons.ghost;
583 mprf(MSGCH_DIAGNOSTICS, "Ghost damage: %d; brand: %d; att_type: %d; "
584 "att_flav: %d",
585 ghost.damage, ghost.brand, ghost.att_type, ghost.att_flav);
586 }
587 }
588
589 // Detects all monsters on the level, using their exact positions.
wizard_detect_creatures()590 void wizard_detect_creatures()
591 {
592 int count = 0;
593 for (monster_iterator mi; mi; ++mi)
594 {
595 env.map_knowledge(mi->pos()).set_monster(monster_info(*mi));
596 env.map_knowledge(mi->pos()).set_detected_monster(mi->type);
597 #ifdef USE_TILE
598 tiles.update_minimap(mi->pos());
599 #endif
600 count++;
601 }
602 mprf("Detected %i monster%s.", count, count == 1 ? "" : "s");
603 }
604
605 // Dismisses all monsters on the level or all monsters that match a user
606 // specified regex.
wizard_dismiss_all_monsters(bool force_all)607 void wizard_dismiss_all_monsters(bool force_all)
608 {
609 char buf[1024] = "";
610 if (!force_all)
611 {
612 mprf(MSGCH_PROMPT, "What monsters to dismiss (ENTER for all, "
613 "\"harmful\", \"mobile\", \"los\" or a regex, "
614 "\"keepitem\" to leave items)? ");
615 bool validline = !cancellable_get_line_autohist(buf, sizeof buf);
616
617 if (!validline)
618 {
619 canned_msg(MSG_OK);
620 return;
621 }
622 }
623
624 int count = dismiss_monsters(buf);
625 mprf("Dismissed %i monster%s.", count, count == 1 ? "" : "s");
626 // If it was turned off turn autopickup back on if all monsters went away.
627 if (!*buf)
628 autotoggle_autopickup(false);
629 }
630
debug_make_monster_shout(monster * mon)631 void debug_make_monster_shout(monster* mon)
632 {
633 mprf(MSGCH_PROMPT, "Make the monster (S)hout or (T)alk? ");
634
635 char type = (char) getchm(KMC_DEFAULT);
636 type = toalower(type);
637
638 if (type != 's' && type != 't')
639 {
640 canned_msg(MSG_OK);
641 return;
642 }
643
644 int num_times = prompt_for_int("How many times? ", false);
645
646 if (num_times <= 0)
647 {
648 canned_msg(MSG_OK);
649 return;
650 }
651
652 if (type == 's')
653 for (int i = 0; i < num_times; ++i)
654 monster_shout(mon, mons_shouts(mon->type, false));
655 else
656 {
657 if (mon->invisible())
658 mpr("The monster is invisible and likely won't speak.");
659
660 if (silenced(you.pos()) && !silenced(mon->pos()))
661 {
662 mpr("You are silenced but the monster isn't; you will "
663 "probably hear/see nothing.");
664 }
665 else if (!silenced(you.pos()) && silenced(mon->pos()))
666 mpr("The monster is silenced and likely won't say anything.");
667 else if (silenced(you.pos()) && silenced(mon->pos()))
668 {
669 mpr("Both you and the monster are silenced, so you likely "
670 "won't hear anything.");
671 }
672
673 for (int i = 0; i< num_times; ++i)
674 mons_speaks(mon);
675 }
676
677 mpr("== Done ==");
678 }
679
wizard_gain_monster_level(monster * mon)680 void wizard_gain_monster_level(monster* mon)
681 {
682 // Give monster as much experience as it can hold,
683 // but cap the levels gained to just 1.
684 bool worked = mon->gain_exp(INT_MAX - mon->experience, 1);
685 if (!worked)
686 simple_monster_message(*mon, " seems unable to mature further.", MSGCH_WARN);
687
688 // (The gain_exp() method will chop the monster's experience down
689 // to half-way between its new level and the next, so we needn't
690 // worry about it being left with too much experience.)
691 }
692
wizard_apply_monster_blessing(monster * mon)693 void wizard_apply_monster_blessing(monster* mon)
694 {
695 mprf(MSGCH_PROMPT, "Apply blessing of (B)eogh, The (S)hining One, or (R)andomly? ");
696
697 char type = (char) getchm(KMC_DEFAULT);
698 type = toalower(type);
699
700 if (type != 'b' && type != 's' && type != 'r')
701 {
702 canned_msg(MSG_OK);
703 return;
704 }
705 god_type god = GOD_NO_GOD;
706 if (type == 'b' || type == 'r' && coinflip())
707 god = GOD_BEOGH;
708 else
709 god = GOD_SHINING_ONE;
710
711 if (!bless_follower(mon, god, true))
712 mprf("%s won't bless this monster for you!", god_name(god).c_str());
713 }
714
wizard_give_monster_item(monster * mon)715 void wizard_give_monster_item(monster* mon)
716 {
717 mon_itemuse_type item_use = mons_itemuse(*mon);
718 if (item_use < MONUSE_STARTING_EQUIPMENT)
719 {
720 mpr("That type of monster can't use any items.");
721 return;
722 }
723
724 int player_slot = prompt_invent_item("Give which item to monster?",
725 menu_type::drop, OSEL_ANY);
726
727 if (prompt_failed(player_slot))
728 return;
729
730 item_def &item = you.inv[player_slot];
731
732 if (item_is_equipped(item))
733 {
734 mpr("Can't give equipped items to a monster.");
735 return;
736 }
737
738 mon_inv_type mon_slot = item_to_mslot(item);
739
740 if (mon_slot == NUM_MONSTER_SLOTS)
741 {
742 mpr("You can't give that type of item to a monster.");
743 return;
744 }
745
746 if (mon_slot == MSLOT_WEAPON
747 && item.inscription.find("alt") != string::npos)
748 {
749 mon_slot = MSLOT_ALT_WEAPON;
750 }
751
752 if (!mon->take_item(player_slot, mon_slot))
753 mpr("Error: monster failed to take item.");
754 }
755
_move_player(const coord_def & where)756 static void _move_player(const coord_def& where)
757 {
758 if (!you.can_pass_through_feat(env.grid(where)))
759 {
760 env.grid(where) = DNGN_FLOOR;
761 set_terrain_changed(where);
762 }
763 move_player_to_grid(where, false);
764 // If necessary, update the Abyss.
765 if (player_in_branch(BRANCH_ABYSS))
766 maybe_shift_abyss_around_player();
767 }
768
_move_monster(const coord_def & where,int idx1)769 static void _move_monster(const coord_def& where, int idx1)
770 {
771 dist moves;
772 direction_chooser_args args;
773 args.unrestricted = true;
774 args.top_prompt = "Move monster to where?";
775 args.default_place = where;
776 direction(moves, args);
777
778 if (!moves.isValid || !in_bounds(moves.target))
779 return;
780
781 monster* mon1 = &env.mons[idx1];
782
783 const int idx2 = env.mgrid(moves.target);
784 monster* mon2 = monster_at(moves.target);
785
786 mon1->moveto(moves.target);
787 env.mgrid(moves.target) = idx1;
788 mon1->check_redraw(moves.target);
789
790 env.mgrid(where) = idx2;
791
792 if (mon2 != nullptr)
793 {
794 mon2->moveto(where);
795 mon1->check_redraw(where);
796 }
797 if (!you.see_cell(moves.target))
798 {
799 mon1->flags &= ~(MF_WAS_IN_VIEW | MF_SEEN);
800 mon1->seen_context = SC_NONE;
801 }
802 }
803
wizard_move_player_or_monster(const coord_def & where)804 void wizard_move_player_or_monster(const coord_def& where)
805 {
806 crawl_state.cancel_cmd_again();
807 crawl_state.cancel_cmd_repeat();
808
809 static bool already_moving = false;
810
811 if (already_moving)
812 {
813 mpr("Already doing a move command.");
814 return;
815 }
816
817 already_moving = true;
818
819 int idx = env.mgrid(where);
820
821 if (idx == NON_MONSTER)
822 {
823 if (crawl_state.arena_suspended)
824 {
825 mpr("You can't move yourself into the arena.");
826 more();
827 return;
828 }
829 _move_player(where);
830 }
831 else
832 _move_monster(where, idx);
833
834 already_moving = false;
835 }
836
wizard_make_monster_summoned(monster * mon)837 void wizard_make_monster_summoned(monster* mon)
838 {
839 int summon_type = 0;
840 if (mon->is_summoned(nullptr, &summon_type) || summon_type != 0)
841 {
842 mprf(MSGCH_PROMPT, "Monster is already summoned.");
843 return;
844 }
845
846 int dur = prompt_for_int("What summon longevity (1 to 6)? ", true);
847
848 if (dur < 1 || dur > 6)
849 {
850 canned_msg(MSG_OK);
851 return;
852 }
853
854 mprf(MSGCH_PROMPT, "[a] clone [b] animated [c] chaos [d] miscast [e] zot");
855 mprf(MSGCH_PROMPT, "[f] wrath [h] aid [m] misc [s] spell");
856
857 mprf(MSGCH_PROMPT, "Which summon type? ");
858
859 char choice = toalower(getchm());
860
861 if (!(choice >= 'a' && choice <= 'h') && choice != 'm' && choice != 's')
862 {
863 canned_msg(MSG_OK);
864 return;
865 }
866
867 int type = 0;
868
869 switch (choice)
870 {
871 case 'a': type = MON_SUMM_CLONE; break;
872 case 'b': type = MON_SUMM_ANIMATE; break;
873 case 'c': type = MON_SUMM_CHAOS; break;
874 case 'd': type = MON_SUMM_MISCAST; break;
875 case 'e': type = MON_SUMM_ZOT; break;
876 case 'f': type = MON_SUMM_WRATH; break;
877 case 'h': type = MON_SUMM_AID; break;
878 case 'm': type = 0; break;
879
880 case 's':
881 {
882 char specs[80];
883
884 msgwin_get_line("Cast which spell by name? ",
885 specs, sizeof(specs));
886
887 if (specs[0] == '\0')
888 {
889 canned_msg(MSG_OK);
890 return;
891 }
892
893 spell_type spell = spell_by_name(specs, true);
894 if (spell == SPELL_NO_SPELL)
895 {
896 mprf(MSGCH_PROMPT, "No such spell.");
897 return;
898 }
899 type = (int) spell;
900 break;
901 }
902
903 default:
904 die("Invalid summon type choice.");
905 break;
906 }
907
908 mon->mark_summoned(dur, true, type);
909 mpr("Monster is now summoned.");
910 }
911
wizard_polymorph_monster(monster * mon)912 void wizard_polymorph_monster(monster* mon)
913 {
914 monster_type old_type = mon->type;
915 monster_type type = debug_prompt_for_monster();
916
917 if (type == NUM_MONSTERS)
918 {
919 canned_msg(MSG_OK);
920 return;
921 }
922
923 if (invalid_monster_type(type))
924 {
925 mprf(MSGCH_PROMPT, "Invalid monster type.");
926 return;
927 }
928
929 if (type == old_type)
930 {
931 mpr("Old type and new type are the same, not polymorphing.");
932 return;
933 }
934
935 if (mons_species(type) == mons_species(old_type))
936 {
937 mpr("Target species must be different from current species.");
938 return;
939 }
940
941 monster_polymorph(mon, type, PPT_SAME);
942
943 if (!mon->alive())
944 {
945 mprf(MSGCH_ERROR, "Polymorph killed monster?");
946 return;
947 }
948
949 mon->check_redraw(mon->pos());
950
951 if (mon->type == old_type)
952 {
953 mpr("Trying harder");
954 change_monster_type(mon, type);
955 if (!mon->alive())
956 {
957 mprf(MSGCH_ERROR, "Polymorph killed monster?");
958 return;
959 }
960
961 mon->check_redraw(mon->pos());
962 }
963
964 if (mon->type == old_type)
965 mpr("Polymorph failed.");
966 else if (mon->type != type)
967 mpr("Monster turned into something other than the desired type.");
968 }
969
debug_pathfind(int idx)970 void debug_pathfind(int idx)
971 {
972 if (idx == NON_MONSTER)
973 return;
974
975 mpr("Choose a destination!");
976 #ifndef USE_TILE_LOCAL
977 more();
978 #endif
979 coord_def dest;
980 level_pos ldest;
981 bool chose = show_map(ldest, false, false);
982 dest = ldest.pos;
983 redraw_screen();
984 update_screen();
985 if (!chose)
986 {
987 canned_msg(MSG_OK);
988 return;
989 }
990
991 monster& mon = env.mons[idx];
992 mprf("Attempting to calculate a path from (%d, %d) to (%d, %d)...",
993 mon.pos().x, mon.pos().y, dest.x, dest.y);
994 monster_pathfind mp;
995 bool success = mp.init_pathfind(&mon, dest, true, true);
996 if (success)
997 {
998 vector<coord_def> path = mp.backtrack();
999 env.travel_trail = path;
1000 #ifdef USE_TILE_WEB
1001 for (coord_def pos : env.travel_trail)
1002 tiles.update_minimap(pos);
1003 #endif
1004 string path_str;
1005 mpr("Here's the shortest path: ");
1006 for (coord_def pos : path)
1007 path_str += make_stringf("(%d, %d) ", pos.x, pos.y);
1008 mpr(path_str);
1009 mprf("-> path length: %u", (unsigned int)path.size());
1010
1011 mpr("");
1012 path = mp.calc_waypoints();
1013 path_str = "";
1014 mpr("");
1015 mpr("And here are the needed waypoints: ");
1016 for (coord_def pos : path)
1017 path_str += make_stringf("(%d, %d) ", pos.x, pos.y);
1018 mpr(path_str);
1019 mprf("-> #waypoints: %u", (unsigned int)path.size());
1020 }
1021 }
1022
_miscast_screen_update()1023 static void _miscast_screen_update()
1024 {
1025 viewwindow();
1026
1027 you.redraw_status_lights = true;
1028 print_stats();
1029 update_screen();
1030
1031 #ifndef USE_TILE_LOCAL
1032 update_monster_pane();
1033 #endif
1034 }
1035
debug_miscast(int target_index)1036 void debug_miscast(int target_index)
1037 {
1038 crawl_state.cancel_cmd_repeat();
1039
1040 actor* target;
1041 if (target_index == NON_MONSTER)
1042 target = &you;
1043 else
1044 target = &env.mons[target_index];
1045
1046 if (!target->alive())
1047 {
1048 mpr("Can't make already dead target miscast.");
1049 return;
1050 }
1051
1052 char specs[100];
1053 mprf(MSGCH_PROMPT, "Miscast which school or spell, by name? ");
1054 if (cancellable_get_line_autohist(specs, sizeof specs) || !*specs)
1055 {
1056 canned_msg(MSG_OK);
1057 return;
1058 }
1059
1060 spell_type spell = spell_by_name(specs, true);
1061 spschool school = school_by_name(specs);
1062
1063 // Prefer exact matches for school name over partial matches for
1064 // spell name.
1065 if (school != spschool::none
1066 && (strcasecmp(specs, spelltype_short_name(school)) == 0
1067 || strcasecmp(specs, spelltype_long_name(school)) == 0))
1068 {
1069 spell = SPELL_NO_SPELL;
1070 }
1071
1072 if (spell == SPELL_NO_SPELL && school == spschool::none)
1073 {
1074 mpr("No matching spell or spell school.");
1075 return;
1076 }
1077 else if (spell != SPELL_NO_SPELL && school != spschool::none)
1078 {
1079 mprf("Ambiguous: can be spell '%s' or school '%s'.",
1080 spell_title(spell), spelltype_short_name(school));
1081 return;
1082 }
1083
1084 spschools_type disciplines = spschool::none;
1085 if (spell != SPELL_NO_SPELL)
1086 {
1087 disciplines = get_spell_disciplines(spell);
1088
1089 if (!disciplines)
1090 {
1091 mprf("Spell '%s' has no disciplines.", spell_title(spell));
1092 return;
1093 }
1094 }
1095
1096 if (spell != SPELL_NO_SPELL)
1097 mprf("Miscasting spell %s.", spell_title(spell));
1098 else
1099 mprf("Miscasting school %s.", spelltype_long_name(school));
1100
1101 if (spell != SPELL_NO_SPELL)
1102 mprf(MSGCH_PROMPT, "Enter fail: ");
1103 else
1104 mprf(MSGCH_PROMPT, "Enter level, fail: ");
1105
1106 if (cancellable_get_line_autohist(specs, sizeof specs) || !*specs)
1107 {
1108 canned_msg(MSG_OK);
1109 return;
1110 }
1111
1112 int level = -1, fail = -1;
1113
1114 if (strchr(specs, ','))
1115 {
1116 vector<string> nums = split_string(",", specs);
1117 level = atoi(nums[0].c_str());
1118 fail = atoi(nums[1].c_str());
1119
1120 if (level <= 0 || fail <= 0)
1121 {
1122 canned_msg(MSG_OK);
1123 return;
1124 }
1125 }
1126 else
1127 {
1128 if (spell != SPELL_NO_SPELL)
1129 {
1130 mpr("Can only enter spell level for schools, not spells.");
1131 return;
1132 }
1133
1134 level = atoi(specs);
1135 if (level < 0)
1136 {
1137 canned_msg(MSG_OK);
1138 return;
1139 }
1140 else if (level > 9)
1141 {
1142 mpr("Spell level can be at most 9.");
1143 return;
1144 }
1145 }
1146
1147 // Handle repeats ourselves since miscasts are likely to interrupt
1148 // command repetions, especially if the player is the target.
1149 int repeats = prompt_for_int("Number of repetitions? ", true);
1150 if (repeats < 1)
1151 {
1152 canned_msg(MSG_OK);
1153 return;
1154 }
1155
1156 while (target->alive() && repeats-- > 0)
1157 {
1158 if (kbhit())
1159 {
1160 mpr("Key pressed, interrupting miscast testing.");
1161 getchm();
1162 break;
1163 }
1164
1165 if (spell != SPELL_NO_SPELL)
1166 miscast_effect(spell, fail);
1167 else
1168 {
1169 miscast_effect(*target, target, {miscast_source::wizard}, school,
1170 level, fail, "testing miscast");
1171 }
1172
1173 _miscast_screen_update();
1174 }
1175 }
1176
1177 #ifdef DEBUG_BONES
debug_ghosts()1178 void debug_ghosts()
1179 {
1180 mprf(MSGCH_PROMPT, "(C)reate, create (T)emporary, or (L)oad bones file?");
1181 const char c = toalower(getchm());
1182
1183 if (c == 'c')
1184 save_ghosts(ghost_demon::find_ghosts(), true, true);
1185 else if (c == 't')
1186 save_ghosts(ghost_demon::find_ghosts(), true, false);
1187 else if (c == 'l')
1188 load_ghosts(MAX_GHOSTS, false);
1189 else
1190 canned_msg(MSG_OK);
1191 }
1192 #endif
1193
1194 #endif
1195