c476d565e9a227f9d35cb3dd55d2c249fde83f64
[crawl.git] / crawl-ref / source / hints.cc
1 /**
2  * @file
3  * @brief A hints mode as an introduction on how to play Dungeon Crawl.
4 **/
5
6 #include "AppHdr.h"
7
8 #include "hints.h"
9
10 #include <cstring>
11 #include <sstream>
12
13 #include "ability.h"
14 #include "artefact.h"
15 #include "cloud.h"
16 #include "colour.h"
17 #include "database.h"
18 #include "decks.h"
19 #include "describe.h"
20 #include "end.h"
21 #include "english.h"
22 #include "env.h"
23 #include "fprop.h"
24 #include "invent.h"
25 #include "itemprop.h"
26 #include "items.h"
27 #include "jobs.h"
28 #include "libutil.h"
29 #include "macro.h"
30 #include "message.h"
31 #include "misc.h"
32 #include "mutation.h"
33 #include "options.h"
34 #include "output.h"
35 #include "religion.h"
36 #include "shopping.h"
37 #include "showsymb.h"
38 #include "skills.h"
39 #include "spl-book.h"
40 #include "state.h"
41 #include "stringutil.h"
42 #include "terrain.h"
43 #include "travel.h"
44 #include "viewchar.h"
45 #include "viewgeom.h"
46 #include "viewmap.h"
47
48 static species_type _get_hints_species(unsigned int type);
49 static job_type     _get_hints_job(unsigned int type);
50 static bool         _hints_feat_interesting(dungeon_feature_type feat);
51 static void         _hints_describe_disturbance(int x, int y);
52 static void         _hints_describe_cloud(int x, int y);
53 static void         _hints_describe_feature(int x, int y);
54 static bool         _water_is_disturbed(int x, int y);
55 static void         _hints_healing_reminder();
56
57 static int _get_hints_cols()
58 {
59 #ifdef USE_TILE_LOCAL
60     return crawl_view.msgsz.x;
61 #else
62     int ncols = get_number_of_cols();
63     return ncols > 80 ? 80 : ncols;
64 #endif
65 }
66
67 hints_state Hints;
68
69 void save_hints(writer& outf)
70 {
71     marshallInt(outf, HINT_EVENTS_NUM);
72     marshallShort(outf, Hints.hints_type);
73     for (int i = 0; i < HINT_EVENTS_NUM; ++i)
74         marshallBoolean(outf, Hints.hints_events[i]);
75 }
76
77 void load_hints(reader& inf)
78 {
79     int version = unmarshallInt(inf);
80     // Discard everything if the number doesn't match.
81     if (version != HINT_EVENTS_NUM)
82         return;
83
84     Hints.hints_type = unmarshallShort(inf);
85     for (int i = 0; i < HINT_EVENTS_NUM; ++i)
86         Hints.hints_events[i] = unmarshallBoolean(inf);
87 }
88
89 // Override init file definition for some options.
90 void init_hints_options()
91 {
92     if (!crawl_state.game_is_hints())
93         return;
94
95     // Clear possible debug messages before messing
96     // with messaging options.
97     clear_messages(true);
98 //     Options.clear_messages = true;
99     Options.show_more  = true;
100     Options.small_more = false;
101
102 #ifdef USE_TILE
103     Options.tile_tag_pref = TAGPREF_TUTORIAL;
104 #endif
105 }
106
107 void init_hints()
108 {
109     // Activate all triggers.
110     // This is rather backwards: If (true) an event still needs to be
111     // triggered, if (false) the relevant message was already printed.
112     Hints.hints_events.init(true);
113
114     // Used to compare which fighting means was used most often.
115     // XXX: This gets reset with every save, which seems odd.
116     //      On the other hand, it's precisely between saves that
117     //      players are most likely to forget these.
118     Hints.hints_spell_counter   = 0;
119     Hints.hints_throw_counter   = 0;
120     Hints.hints_melee_counter   = 0;
121     Hints.hints_berserk_counter = 0;
122
123     // Store whether explore, stash search or travelling was used.
124     // XXX: Also not stored across save games.
125     Hints.hints_explored = true;
126     Hints.hints_stashes  = true;
127     Hints.hints_travel   = true;
128
129     // For occasional healing reminders.
130     Hints.hints_last_healed = 0;
131
132     // Did the player recently see a monster turn invisible?
133     Hints.hints_seen_invisible = 0;
134 }
135
136 static void _print_hints_menu(hints_types type)
137 {
138     char letter = 'a' + type;
139     char desc[100];
140
141     switch (type)
142     {
143     case HINT_BERSERK_CHAR:
144         strcpy(desc, "(Melee oriented character with divine support)");
145         break;
146     case HINT_MAGIC_CHAR:
147         strcpy(desc, "(Magic oriented character)");
148         break;
149     case HINT_RANGER_CHAR:
150         strcpy(desc, "(Ranged fighter)");
151         break;
152     default: // no further choices
153         strcpy(desc, "(erroneous character)");
154         break;
155     }
156
157     cprintf("%c - %s %s %s\n",
158             letter, species_name(_get_hints_species(type)).c_str(),
159                     get_job_name(_get_hints_job(type)), desc);
160 }
161
162 // Hints mode selection screen and choice.
163 void pick_hints(newgame_def* choice)
164 {
165 again:
166     clrscr();
167
168     cgotoxy(1,1);
169     formatted_string::parse_string(
170         "<white>You must be new here indeed!</white>"
171         "\n\n"
172         "<cyan>You can be:</cyan>"
173         "\n").display();
174
175     textcolour(LIGHTGREY);
176
177     for (int i = 0; i < HINT_TYPES_NUM; i++)
178         _print_hints_menu((hints_types)i);
179
180     formatted_string::parse_string(
181         "<brown>\nEsc - Quit"
182         "\n* - Random hints mode character"
183         "</brown>\n").display();
184
185     while (true)
186     {
187         int keyn = getch_ck();
188         if (keyn == CK_REDRAW)
189             goto again;
190
191         // Random choice.
192         if (keyn == '*' || keyn == '+' || keyn == '!' || keyn == '#')
193             keyn = 'a' + random2(HINT_TYPES_NUM);
194
195         // Choose character for hints mode game and set starting values.
196         if (keyn >= 'a' && keyn <= 'a' + HINT_TYPES_NUM - 1)
197         {
198             Hints.hints_type = keyn - 'a';
199             choice->species  = _get_hints_species(Hints.hints_type);
200             choice->job = _get_hints_job(Hints.hints_type);
201             // easiest choice for fighters
202             choice->weapon = choice->job == JOB_HUNTER ? WPN_SHORTBOW
203                                                        : WPN_HAND_AXE;
204
205             return;
206         }
207
208         switch (keyn)
209         {
210         CASE_ESCAPE
211 #ifdef USE_TILE_WEB
212             tiles.send_exit_reason("cancel");
213 #endif
214             game_ended();
215         case 'X':
216             cprintf("\nGoodbye!");
217 #ifdef USE_TILE_WEB
218             tiles.send_exit_reason("cancel");
219 #endif
220             end(0);
221             return;
222         }
223     }
224 }
225
226 void hints_load_game()
227 {
228     if (!crawl_state.game_is_hints())
229         return;
230
231     learned_something_new(HINT_LOAD_SAVED_GAME);
232
233     // Reinitialise counters for explore, stash search and travelling.
234     Hints.hints_explored = Hints.hints_events[HINT_AUTO_EXPLORE];
235     Hints.hints_stashes  = true;
236     Hints.hints_travel   = true;
237 }
238
239 static species_type _get_hints_species(unsigned int type)
240 {
241     switch (type)
242     {
243     case HINT_BERSERK_CHAR:
244         return SP_MINOTAUR;
245     case HINT_MAGIC_CHAR:
246         return SP_DEEP_ELF;
247     case HINT_RANGER_CHAR:
248         return SP_CENTAUR;
249     default:
250         // Use something fancy for debugging.
251         return SP_TENGU;
252     }
253 }
254
255 static job_type _get_hints_job(unsigned int type)
256 {
257     switch (type)
258     {
259     case HINT_BERSERK_CHAR:
260         return JOB_BERSERKER;
261     case HINT_MAGIC_CHAR:
262         return JOB_CONJURER;
263     case HINT_RANGER_CHAR:
264         return JOB_HUNTER;
265     default:
266         // Use something fancy for debugging.
267         return JOB_NECROMANCER;
268     }
269 }
270
271 static void _replace_static_tags(string &text)
272 {
273     size_t p;
274     while ((p = text.find("$cmd[")) != string::npos)
275     {
276         size_t q = text.find("]", p + 5);
277         if (q == string::npos)
278         {
279             text += "<lightred>ERROR: unterminated $cmd</lightred>";
280             break;
281         }
282
283         string command = text.substr(p + 5, q - p - 5);
284         command_type cmd = name_to_command(command);
285
286         command = command_to_string(cmd);
287         if (command == "<")
288             command += "<";
289
290         text.replace(p, q - p + 1, command);
291     }
292
293     while ((p = text.find("$item[")) != string::npos)
294     {
295         size_t q = text.find("]", p + 6);
296         if (q == string::npos)
297         {
298             text += "<lightred>ERROR: unterminated $item</lightred>";
299             break;
300         }
301
302         string item = text.substr(p + 6, q - p - 6);
303         int type;
304         for (type = OBJ_WEAPONS; type < NUM_OBJECT_CLASSES; ++type)
305             if (item == item_class_name(type, true))
306                 break;
307
308         item_def dummy;
309         dummy.base_type = static_cast<object_class_type>(type);
310         dummy.sub_type = 0;
311         if (item == "amulet") // yay shared item classes
312             dummy.base_type = OBJ_JEWELLERY, dummy.sub_type = AMU_RAGE;
313         item = stringize_glyph(get_item_symbol(show_type(dummy).item));
314
315         if (item == "<")
316             item += "<";
317
318         text.replace(p, q - p + 1, item);
319     }
320
321     // Brand user-input -related (tutorial) items with <w>[(text here)]</w>.
322     while ((p = text.find("<input>")) != string::npos)
323     {
324         size_t q = text.find("</input>", p + 7);
325         if (q == string::npos)
326         {
327             text += "<lightred>ERROR: unterminated <input></lightred>";
328             break;
329         }
330
331         string input = text.substr(p + 7, q - p - 7);
332         input = "<w>[" + input;
333         input += "]</w>";
334         text.replace(p, q - p + 8, input);
335     }
336 }
337
338 // Prints the hints mode welcome screen.
339 void hints_starting_screen()
340 {
341     cgotoxy(1, 1);
342     clrscr();
343
344     int width = _get_hints_cols();
345 #ifdef USE_TILE_LOCAL
346     // Use a more sensible screen width.
347     if (width < 80 && width < crawl_view.msgsz.x + crawl_view.hudsz.x)
348         width = crawl_view.msgsz.x + crawl_view.hudsz.x;
349     if (width > 80)
350         width = 80;
351 #endif
352
353     string text = getHintString("welcome");
354     _replace_static_tags(text);
355
356     linebreak_string(text, width);
357     display_tagged_block(text);
358
359     {
360         mouse_control mc(MOUSE_MODE_MORE);
361         getchm();
362     }
363     redraw_screen();
364 }
365
366 // Called each turn from _input. Better name welcome.
367 void hints_new_turn()
368 {
369     if (crawl_state.game_is_hints())
370     {
371         Hints.hints_just_triggered = false;
372
373         if (you.attribute[ATTR_HELD])
374             learned_something_new(HINT_CAUGHT_IN_NET);
375         else if (i_feel_safe() && !player_in_branch(BRANCH_ABYSS))
376         {
377             // We don't want those "Whew, it's safe to rest now" messages
378             // if you were just cast into the Abyss. Right?
379
380             if (2 * you.hp < you.hp_max
381                 || 2 * you.magic_points < you.max_magic_points)
382             {
383                 _hints_healing_reminder();
384             }
385             else if (!you.running
386                      && Hints.hints_events[HINT_SHIFT_RUN]
387                      && you.num_turns >= 200
388                      && you.hp == you.hp_max
389                      && you.magic_points == you.max_magic_points)
390             {
391                 learned_something_new(HINT_SHIFT_RUN);
392             }
393             else if (!you.running
394                      && Hints.hints_events[HINT_MAP_VIEW]
395                      && you.num_turns >= 500
396                      && you.hp == you.hp_max
397                      && you.magic_points == you.max_magic_points)
398             {
399                 learned_something_new(HINT_MAP_VIEW);
400             }
401             else if (!you.running
402                      && Hints.hints_events[HINT_AUTO_EXPLORE]
403                      && you.num_turns >= 700
404                      && you.hp == you.hp_max
405                      && you.magic_points == you.max_magic_points)
406             {
407                 learned_something_new(HINT_AUTO_EXPLORE);
408             }
409         }
410         else
411         {
412             if (poison_is_lethal())
413             {
414                 if (Hints.hints_events[HINT_NEED_POISON_HEALING])
415                     learned_something_new(HINT_NEED_POISON_HEALING);
416             }
417             else if (2*you.hp < you.hp_max)
418                 learned_something_new(HINT_RUN_AWAY);
419
420             if (Hints.hints_type == HINT_MAGIC_CHAR && you.magic_points < 1)
421                 learned_something_new(HINT_RETREAT_CASTER);
422         }
423     }
424 }
425
426 /**
427  * Look up and display a hint message from the database. Is usable from dlua,
428  * so wizard mode in-game Lua interpreter can be used to test the messages.
429  * @param arg1 A string that can be inserted into the hint message.
430  * @param arg2 Another string that can be inserted into the hint message.
431  */
432 void print_hint(string key, const string arg1, const string arg2)
433 {
434     string text = getHintString(key);
435     if (text.empty())
436         return mprf(MSGCH_ERROR, "Error, no hint for '%s'.", key.c_str());
437
438     _replace_static_tags(text);
439     text = untag_tiles_console(text);
440     text = replace_all(text, "$1", arg1);
441     text = replace_all(text, "$2", arg2);
442
443     // "\n" to preserve indented parts, the rest is unwrapped, or split into
444     // paragraphs by "\n\n", split_string() will ignore the empty line.
445     for (const string &chunk : split_string("\n", text))
446         mprf(MSGCH_TUTORIAL, "%s", chunk.c_str());
447
448     stop_running();
449 }
450
451 // Once a hints mode character dies, offer some last playing hints.
452 void hints_death_screen()
453 {
454     string text;
455
456     print_hint("death");
457     more();
458
459     if (Hints.hints_type == HINT_MAGIC_CHAR
460         && Hints.hints_spell_counter < Hints.hints_melee_counter)
461     {
462         print_hint("death conjurer melee");
463     }
464     else if (you_worship(GOD_TROG) && Hints.hints_berserk_counter <= 3
465              && !you.berserk() && !you.duration[DUR_EXHAUSTED])
466     {
467         print_hint("death berserker unberserked");
468     }
469     else if (Hints.hints_type == HINT_RANGER_CHAR
470              && 2*Hints.hints_throw_counter < Hints.hints_melee_counter)
471     {
472         print_hint("death ranger melee");
473     }
474     else
475     {
476         int hint = random2(6);
477
478         bool skip_first_hint = false;
479         // If a character has been unusually busy with projectiles and spells
480         // give some other hint rather than the first one.
481         if (hint == 0 && Hints.hints_throw_counter + Hints.hints_spell_counter
482                           >= Hints.hints_melee_counter)
483         {
484             hint = random2(5) + 1;
485             skip_first_hint = true;
486         }
487         // FIXME: The hints below could be somewhat less random, so that e.g.
488         // the message for fighting several monsters in a corridor only happens
489         // if there's more than one monster around and you're not in a corridor,
490         // or the one about using consumable objects only if you actually have
491         // any (useful or unidentified) scrolls/wands/potions.
492
493         if (hint == 5)
494         {
495             vector<monster* > visible =
496                 get_nearby_monsters(false, true, true, false);
497
498             if (visible.size() < 2)
499             {
500                 if (skip_first_hint)
501                     hint = random2(4) + 1;
502                 else
503                     hint = random2(5);
504             }
505         }
506
507         print_hint(make_stringf("death random %d", hint));
508     }
509     mprf(MSGCH_TUTORIAL, "%s", untag_tiles_console(text).c_str());
510     more();
511
512     mprf(MSGCH_TUTORIAL, "See you next game!");
513
514     Hints.hints_events.init(false);
515 }
516
517 // If a character survives until XL 7, the hints mode is declared finished
518 // and they get a more advanced playing hint, depending on what they might
519 // know by now.
520 void hints_finished()
521 {
522     string text;
523
524     crawl_state.type = GAME_TYPE_NORMAL;
525
526     print_hint("finished");
527     more();
528
529     if (Hints.hints_explored)
530         print_hint("finished explored");
531     else if (Hints.hints_travel)
532         print_hint("finished travel");
533     else if (Hints.hints_stashes)
534         print_hint("finished stashes");
535     else
536         print_hint(make_stringf("finished random %d", random2(4)));
537     more();
538
539     Hints.hints_events.init(false);
540
541     // Remove the hints mode file.
542     you.save->delete_chunk("tut");
543 }
544
545 void hints_dissection_reminder(bool healthy)
546 {
547     if (!crawl_state.game_is_hints())
548         return;
549
550     if (Hints.hints_just_triggered)
551         return;
552
553     // When hungry, give appropriate message
554     if (you.hunger_state < HS_SATIATED && healthy)
555         learned_something_new(HINT_MAKE_CHUNKS);
556 }
557
558 static bool _advise_use_healing_potion()
559 {
560     for (int i = 0; i < ENDOFPACK; i++)
561     {
562         item_def &obj(you.inv[i]);
563
564         if (!obj.defined())
565             continue;
566
567         if (obj.base_type != OBJ_POTIONS)
568             continue;
569
570         if (!item_type_known(obj))
571             continue;
572
573         if (obj.sub_type == POT_CURING
574             || obj.sub_type == POT_HEAL_WOUNDS)
575         {
576             return true;
577         }
578     }
579
580     return false;
581 }
582
583 void hints_healing_check()
584 {
585     if (2*you.hp <= you.hp_max
586         && _advise_use_healing_potion())
587     {
588         learned_something_new(HINT_HEALING_POTIONS);
589     }
590 }
591
592 // Occasionally remind injured characters of resting.
593 static void _hints_healing_reminder()
594 {
595     if (!crawl_state.game_is_hints())
596         return;
597
598     if (Hints.hints_seen_invisible > 0
599         && you.num_turns - Hints.hints_seen_invisible <= 20)
600     {
601         // If we recently encountered an invisible monster, we need a
602         // special message.
603         learned_something_new(HINT_NEED_HEALING_INVIS);
604         // If that one was already displayed, don't print a reminder.
605     }
606     else
607     {
608         if (Hints.hints_events[HINT_NEED_HEALING])
609             learned_something_new(HINT_NEED_HEALING);
610         else if (you.num_turns - Hints.hints_last_healed >= 50
611                  && !you.duration[DUR_POISONING])
612         {
613             if (Hints.hints_just_triggered)
614                 return;
615
616             Hints.hints_just_triggered = true;
617
618             string text;
619             text =  "Remember to rest between fights and to enter unexplored "
620                     "terrain with full health and magic. Ideally you "
621                     "should retreat into areas you've already explored and "
622                     "cleared of monsters; resting on the edge of the explored "
623                     "terrain increases the chances of your rest being "
624                     "interrupted by wandering monsters. For resting, press "
625                     "<w>5</w> or <w>Shift-numpad 5</w>"
626                     "<tiles>, or <w>click on the stat area</w> with your mouse</tiles>"
627                     ".";
628
629             if (you.hp < you.hp_max && you_worship(GOD_TROG)
630                 && you.can_go_berserk())
631             {
632                 text += "\nAlso, berserking might help you not to lose so much "
633                         "health in the first place. To use your abilities type "
634                         "<w>a</w>.";
635             }
636             mprf(MSGCH_TUTORIAL, "%s", text.c_str());
637
638             if (is_resting())
639                 stop_running();
640         }
641         Hints.hints_last_healed = you.num_turns;
642     }
643 }
644
645 // Give a message if you see, pick up or inspect an item type for the
646 // first time.
647 void taken_new_item(object_class_type item_type)
648 {
649     switch (item_type)
650     {
651     case OBJ_WANDS:
652         learned_something_new(HINT_SEEN_WAND);
653         break;
654     case OBJ_SCROLLS:
655         learned_something_new(HINT_SEEN_SCROLL);
656         break;
657     case OBJ_JEWELLERY:
658         learned_something_new(HINT_SEEN_JEWELLERY);
659         break;
660     case OBJ_POTIONS:
661         learned_something_new(HINT_SEEN_POTION);
662         break;
663     case OBJ_BOOKS:
664         learned_something_new(HINT_SEEN_SPBOOK);
665         break;
666     case OBJ_FOOD:
667         learned_something_new(HINT_SEEN_FOOD);
668         break;
669     case OBJ_CORPSES:
670         learned_something_new(HINT_SEEN_CARRION);
671         break;
672     case OBJ_WEAPONS:
673         learned_something_new(HINT_SEEN_WEAPON);
674         break;
675     case OBJ_ARMOUR:
676         learned_something_new(HINT_SEEN_ARMOUR);
677         break;
678     case OBJ_MISSILES:
679         learned_something_new(HINT_SEEN_MISSILES);
680         break;
681     case OBJ_MISCELLANY:
682         learned_something_new(HINT_SEEN_MISC);
683         break;
684     case OBJ_STAVES:
685         learned_something_new(HINT_SEEN_STAFF);
686         break;
687     case OBJ_RODS:
688         learned_something_new(HINT_SEEN_ROD);
689         break;
690     case OBJ_GOLD:
691         learned_something_new(HINT_SEEN_GOLD);
692         break;
693     default: // nothing to be done
694         return;
695     }
696 }
697
698 // Give a special message if you gain a skill you didn't have before.
699 void hints_gained_new_skill(skill_type skill)
700 {
701     if (!crawl_state.game_is_hints())
702         return;
703
704     learned_something_new(HINT_SKILL_RAISE);
705
706     switch (skill)
707     {
708     // Special cases first.
709     case SK_FIGHTING:
710     case SK_ARMOUR:
711     case SK_STEALTH:
712     case SK_UNARMED_COMBAT:
713     case SK_INVOCATIONS:
714     case SK_EVOCATIONS:
715     case SK_DODGING:
716     case SK_SHIELDS:
717     case SK_THROWING:
718     case SK_SPELLCASTING:
719     {
720         mprf(MSGCH_TUTORIAL, "%s", get_skill_description(skill).c_str());
721         stop_running();
722         break;
723     }
724     // Only one message for all magic skills (except Spellcasting).
725     case SK_CONJURATIONS:
726     case SK_CHARMS:
727     case SK_HEXES:
728     case SK_SUMMONINGS:
729     case SK_NECROMANCY:
730     case SK_TRANSLOCATIONS:
731     case SK_TRANSMUTATIONS:
732     case SK_FIRE_MAGIC:
733     case SK_ICE_MAGIC:
734     case SK_AIR_MAGIC:
735     case SK_EARTH_MAGIC:
736     case SK_POISON_MAGIC:
737         learned_something_new(HINT_GAINED_MAGICAL_SKILL);
738         break;
739
740     // Melee skills.
741     case SK_SHORT_BLADES:
742     case SK_LONG_BLADES:
743     case SK_AXES:
744     case SK_MACES_FLAILS:
745     case SK_POLEARMS:
746     case SK_STAVES:
747         learned_something_new(HINT_GAINED_MELEE_SKILL);
748         break;
749
750     // Ranged skills.
751     case SK_SLINGS:
752     case SK_BOWS:
753     case SK_CROSSBOWS:
754         learned_something_new(HINT_GAINED_RANGED_SKILL);
755         break;
756
757     default:
758         break;
759     }
760 }
761
762 #ifndef USE_TILE
763 // As safely as possible, colourize the passed glyph.
764 // Stringizes it and handles quoting "<".
765 static string _colourize_glyph(int col, unsigned ch)
766 {
767     cglyph_t g;
768     g.col = col;
769     g.ch = ch;
770     return glyph_to_tagstr(g);
771 }
772 #endif
773
774 static bool _mons_is_highlighted(const monster* mons)
775 {
776     return mons->friendly()
777                && Options.friend_brand != CHATTR_NORMAL
778            || mons_looks_stabbable(mons)
779                && Options.stab_brand != CHATTR_NORMAL
780            || mons_looks_distracted(mons)
781                && Options.may_stab_brand != CHATTR_NORMAL;
782 }
783
784 static bool _advise_use_wand()
785 {
786     for (int i = 0; i < ENDOFPACK; i++)
787     {
788         item_def &obj(you.inv[i]);
789
790         if (!obj.defined())
791             continue;
792
793         if (obj.base_type != OBJ_WANDS)
794             continue;
795
796         // Wand type unknown, might be useful.
797         if (!item_type_known(obj))
798             return true;
799
800         // Empty wands are no good.
801         if (is_known_empty_wand(obj))
802             continue;
803
804         // Can it be used to fight?
805         switch (obj.sub_type)
806         {
807         case WAND_FLAME:
808         case WAND_FROST:
809         case WAND_SLOWING:
810         case WAND_MAGIC_DARTS:
811         case WAND_PARALYSIS:
812         case WAND_FIRE:
813         case WAND_COLD:
814         case WAND_CONFUSION:
815         case WAND_FIREBALL:
816         case WAND_TELEPORTATION:
817         case WAND_LIGHTNING:
818         case WAND_ENSLAVEMENT:
819         case WAND_DRAINING:
820         case WAND_RANDOM_EFFECTS:
821         case WAND_DISINTEGRATION:
822             return true;
823         }
824     }
825
826     return false;
827 }
828
829 void hints_monster_seen(const monster& mon)
830 {
831     if (mons_class_flag(mon.type, M_NO_EXP_GAIN))
832     {
833         if (Hints.hints_events[HINT_SEEN_ZERO_EXP_MON])
834         {
835             if (Hints.hints_just_triggered)
836                 return;
837
838             learned_something_new(HINT_SEEN_ZERO_EXP_MON, mon.pos());
839             return;
840         }
841
842         // Don't do HINT_SEEN_MONSTER for zero exp monsters.
843         if (Hints.hints_events[HINT_SEEN_MONSTER])
844             return;
845     }
846
847     if (!Hints.hints_events[HINT_SEEN_MONSTER])
848     {
849         if (Hints.hints_just_triggered)
850             return;
851
852         if (_mons_is_highlighted(&mon))
853             learned_something_new(HINT_MONSTER_BRAND, mon.pos());
854         if (mon.friendly())
855             learned_something_new(HINT_MONSTER_FRIENDLY, mon.pos());
856
857         if (you_worship(GOD_TROG) && you.can_go_berserk()
858             && one_chance_in(4))
859         {
860             learned_something_new(HINT_CAN_BERSERK);
861         }
862         return;
863     }
864
865     stop_running();
866
867     Hints.hints_events[HINT_SEEN_MONSTER] = false;
868     Hints.hints_just_triggered = true;
869
870     monster_info mi(&mon);
871 #ifdef USE_TILE
872     // need to highlight monster
873     const coord_def gc = mon.pos();
874     tiles.place_cursor(CURSOR_TUTORIAL, gc);
875     tiles.add_text_tag(TAG_TUTORIAL, mi);
876 #endif
877
878     string text = "That ";
879
880     if (is_tiles())
881     {
882         text +=
883             string("monster is a ") +
884             mon.name(DESC_PLAIN).c_str() +
885             ". Examples for typical early monsters are rats, giant newts, "
886             "kobolds, or goblins. You can gain information about any monster "
887             "by hovering your mouse over its tile, and read the monster "
888             "description by clicking on it with your <w>right mouse button</w>.";
889     }
890     else
891     {
892         text +=
893             glyph_to_tagstr(get_mons_glyph(mi)) +
894             " is a monster, usually depicted by a letter. Some typical "
895             "early monsters look like <brown>r</brown>, <green>l</green>, "
896             "<brown>K</brown> or <lightgrey>g</lightgrey>. ";
897         if (crawl_view.mlistsz.y > 0)
898         {
899             text += "Your console settings allowing, you'll always see a "
900                     "list of monsters somewhere on the screen.\n";
901         }
902         text += "You can gain information about it by pressing <w>x</w> and "
903                 "moving the cursor over the monster, and read the monster "
904                 "description by then pressing <w>v</w>. ";
905     }
906
907     text += "\nTo attack this monster with your wielded weapon, just move "
908             "into it. ";
909     if (is_tiles())
910     {
911         text +=
912             "Note that as long as there's a non-friendly monster in view you "
913             "won't be able to automatically move to distant squares, to avoid "
914             "death by misclicking.";
915     }
916
917     mprf(MSGCH_TUTORIAL, "%s", text.c_str());
918
919     if (Hints.hints_type == HINT_RANGER_CHAR)
920     {
921         text =  "However, as a hunter you will want to deal with it using your "
922                 "bow. If you have a look at your shortbow from your "
923                 "<w>i</w>nventory, you'll find an explanation of how to do "
924                 "this. ";
925
926         if (!you.weapon()
927             || you.weapon()->base_type != OBJ_WEAPONS
928             || you.weapon()->sub_type != WPN_SHORTBOW)
929         {
930             text += "First <w>w</w>ield it, then follow the instructions."
931                 "<tiles>\nAs a short-cut you can also <w>right-click</w> on your "
932                 "shortbow to read its description, and <w>left-click</w> to wield "
933                 "it.</tiles>";
934         }
935         else
936         {
937             text += "<tiles>Clicking with your <w>right mouse button</w> on your "
938                     "shortbow will also let you read its description.</tiles>";
939         }
940
941         mprf(MSGCH_TUTORIAL, "%s", untag_tiles_console(text).c_str());
942
943     }
944     else if (Hints.hints_type == HINT_MAGIC_CHAR)
945     {
946         text =  "However, as a conjurer you will want to deal with it using "
947                 "magic. If you have a look at your spellbook from your "
948                 "<w>i</w>nventory, you'll find an explanation of how to do "
949                 "this."
950                 "<tiles>\nAs a short-cut you can also <w>right-click</w> on your "
951                 "book in your inventory to read its description.</tiles>";
952         mprf(MSGCH_TUTORIAL, "%s", untag_tiles_console(text).c_str());
953
954     }
955 }
956
957 void hints_first_item(const item_def &item)
958 {
959     // Happens if monster is standing on dropped corpse or item.
960     if (monster_at(item.pos))
961         return;
962
963     if (!Hints.hints_events[HINT_SEEN_FIRST_OBJECT]
964         || Hints.hints_just_triggered)
965     {
966         // NOTE: Since a new player might not think to pick up a
967         // corpse (and why should they?), HINT_SEEN_CARRION is done when a
968         // corpse is first seen.
969         if (!Hints.hints_just_triggered
970             && item.base_type == OBJ_CORPSES
971             && !monster_at(item.pos))
972         {
973             learned_something_new(HINT_SEEN_CARRION, item.pos);
974         }
975         return;
976     }
977
978     stop_running();
979
980     Hints.hints_events[HINT_SEEN_FIRST_OBJECT] = false;
981     Hints.hints_just_triggered = true;
982
983 #ifdef USE_TILE
984     const coord_def gc = item.pos;
985     tiles.place_cursor(CURSOR_TUTORIAL, gc);
986     tiles.add_text_tag(TAG_TUTORIAL, item.name(DESC_A), gc);
987 #endif
988
989     print_hint("HINT_SEEN_FIRST_OBJECT",
990                glyph_to_tagstr(get_item_glyph(&item)));
991 }
992
993 static string _describe_portal(const coord_def &gc)
994 {
995     const dungeon_feature_type feat = grd(gc);
996     string text;
997
998     // For the sake of completeness, though it's very unlikely that a
999     // player will find a bazaar entrance before reaching XL 7.
1000     if (feat == DNGN_ENTER_BAZAAR)
1001     {
1002         text =  "is a portal to an inter-dimensional bazaar filled with "
1003                 "shops. It will disappear if you don't enter it soon, "
1004                 "so hurry. To enter ";
1005     }
1006     // Sewers can appear on D:3-6, ossuaries D:4-8.
1007     else
1008     {
1009         text =  "is a portal to a special level where you'll have to fight "
1010                 "your way back to the exit through some tougher than average "
1011                 "monsters (the monsters around the portal should give a "
1012                 "good indication as to how tough), but with the reward of "
1013                 "some good loot. There's no penalty for skipping it, but if "
1014                 "you do skip it the portal will disappear, so you have to "
1015                 "decide now if you want to risk it. To enter ";
1016     }
1017
1018     text += "stand over the portal and press <w>></w>. To return find "
1019             "<tiles>a similar looking portal tile </tiles>"
1020             "<console>another <w>"
1021           + stringize_glyph(get_feat_symbol(DNGN_EXIT_SEWER))
1022           + "</w> (though NOT the ancient stone arch you'll start "
1023             "out on) </console>"
1024             "and press <w><<</w>."
1025             "<tiles>\nAlternatively, clicking on your <w>left mouse button</w> "
1026             "while pressing the <w>Shift key</w> will let you enter any "
1027             "portal you're standing on.</tiles>";
1028
1029     return text;
1030 }
1031
1032 #define DELAY_EVENT \
1033 { \
1034     Hints.hints_events[seen_what] = true; \
1035     return; \
1036 }
1037
1038 // Really rare or important events should get a comment even if
1039 // learned_something_new() was already triggered this turn.
1040 // NOTE: If put off, the SEEN_<feature> variant will be triggered the
1041 //       next turn, so they may be rare but aren't urgent.
1042 static bool _rare_hints_event(hints_event_type event)
1043 {
1044     switch (event)
1045     {
1046     case HINT_SEEN_RUNED_DOOR: // The runed door could be opened in one turn.
1047     case HINT_KILLED_MONSTER:
1048     case HINT_NEW_LEVEL:
1049     case HINT_YOU_ENCHANTED:
1050     case HINT_YOU_POISON:
1051     case HINT_YOU_ROTTING:
1052     case HINT_YOU_CURSED:
1053     case HINT_YOU_HUNGRY:
1054     case HINT_YOU_STARVING:
1055     case HINT_GLOWING:
1056     case HINT_CAUGHT_IN_NET:
1057     case HINT_YOU_SILENCE:
1058     case HINT_NEED_POISON_HEALING:
1059     case HINT_INVISIBLE_DANGER:
1060     case HINT_NEED_HEALING_INVIS:
1061     case HINT_ABYSS:
1062     case HINT_RUN_AWAY:
1063     case HINT_RETREAT_CASTER:
1064     case HINT_YOU_MUTATED:
1065     case HINT_NEW_ABILITY_GOD:
1066     case HINT_NEW_ABILITY_MUT:
1067     case HINT_NEW_ABILITY_ITEM:
1068     case HINT_CONVERT:
1069     case HINT_GOD_DISPLEASED:
1070     case HINT_EXCOMMUNICATE:
1071     case HINT_GAINED_MAGICAL_SKILL:
1072     case HINT_GAINED_MELEE_SKILL:
1073     case HINT_GAINED_RANGED_SKILL:
1074     case HINT_CHOOSE_STAT:
1075     case HINT_AUTO_EXCLUSION:
1076         return true;
1077     default:
1078         return false;
1079     }
1080 }
1081
1082 // Allow for a few specific hint mode messages.
1083 static bool _tutorial_interesting(hints_event_type event)
1084 {
1085     switch (event)
1086     {
1087     case HINT_AUTOPICKUP_THROWN:
1088     case HINT_TARGET_NO_FOE:
1089     case HINT_YOU_POISON:
1090     case HINT_NEW_ABILITY_ITEM:
1091     case HINT_ITEM_RESISTANCES:
1092     case HINT_FLYING:
1093     case HINT_INACCURACY:
1094     case HINT_HEALING_POTIONS:
1095     case HINT_GAINED_SPELLCASTING:
1096     case HINT_FUMBLING_SHALLOW_WATER:
1097 #if TAG_MAJOR_VERSION == 34
1098     case HINT_MEMORISE_FAILURE:
1099 #endif
1100     case HINT_SPELL_MISCAST:
1101     case HINT_CLOUD_WARNING:
1102     case HINT_ANIMATE_CORPSE_SKELETON:
1103     case HINT_SKILL_RAISE:
1104         return true;
1105     default:
1106         return false;
1107     }
1108 }
1109
1110 // A few special tutorial explanations require triggers.
1111 // Initialize the corresponding events, so they can get displayed.
1112 void tutorial_init_hints()
1113 {
1114     Hints.hints_events.init(false);
1115     for (int i = 0; i < HINT_EVENTS_NUM; ++i)
1116         if (_tutorial_interesting((hints_event_type) i))
1117             Hints.hints_events[i] = true;
1118 }
1119
1120 // Here most of the hints mode messages for various triggers are handled.
1121 void learned_something_new(hints_event_type seen_what, coord_def gc)
1122 {
1123     if (!crawl_state.game_is_hints_tutorial())
1124         return;
1125
1126     // Already learned about that.
1127     if (!Hints.hints_events[seen_what])
1128         return;
1129
1130     // Don't trigger twice in the same turn.
1131     // Not required in the tutorial.
1132     if (crawl_state.game_is_hints() && Hints.hints_just_triggered
1133         && !_rare_hints_event(seen_what))
1134     {
1135         return;
1136     }
1137
1138     ostringstream text;
1139     vector<command_type> cmd;
1140
1141     Hints.hints_just_triggered    = true;
1142     Hints.hints_events[seen_what] = false;
1143
1144     switch (seen_what)
1145     {
1146     case HINT_SEEN_POTION:
1147         text << "You have picked up your first potion"
1148                 "<console> ('<w>"
1149              << stringize_glyph(get_item_symbol(SHOW_ITEM_POTION))
1150              << "</w>'). Use </console>"
1151                 "<tiles>. Simply click on it with your <w>left mouse button</w>, or "
1152                 "press </tiles>"
1153                 "<w>%</w> to quaff it.\n"
1154                 "Note that potion effects might be good or bad. For the bad "
1155                 "ones, you might want to wait until you're a bit tougher, but "
1156                 "at the same time it would be nice to know the good ones when "
1157                 "you need them. Ah, decisions, decisions...";
1158         cmd.push_back(CMD_QUAFF);
1159         break;
1160
1161     case HINT_SEEN_SCROLL:
1162         text << "You have picked up your first scroll"
1163                 "<console> ('<w>"
1164              << stringize_glyph(get_item_symbol(SHOW_ITEM_SCROLL))
1165              << "</w>'). Type </console>"
1166                 "<tiles>. Simply click on it with your <w>left mouse button</w>, or "
1167                 "type </tiles>"
1168                 "<w>%</w> to read it.";
1169         cmd.push_back(CMD_READ);
1170         break;
1171
1172     case HINT_SEEN_WAND:
1173         text << "You have picked up your first wand"
1174                 "<console> ('<w>"
1175              << stringize_glyph(get_item_symbol(SHOW_ITEM_WAND))
1176              << "</w>'). Type </console>"
1177                 "<tiles>. Simply click on it with your <w>left mouse button</w>, or "
1178                 "type </tiles>"
1179                 "<w>%</w> to evoke it.";
1180         cmd.push_back(CMD_EVOKE);
1181         break;
1182
1183     case HINT_SEEN_SPBOOK:
1184         text << "You have picked up a book"
1185                 "<console> ('<w>"
1186              << stringize_glyph(get_item_symbol(SHOW_ITEM_BOOK))
1187              << "'</w>) "
1188              << "that you can read by typing <w>%</w>. "
1189                 "If it's a spellbook you'll then be able to memorise spells "
1190                 "via <w>%</w> and cast a memorised spell with <w>%</w>.</console>"
1191                 "<tiles>. You can read it doing a <w>right click</w> with your "
1192                 "mouse, and memorise spells with a <w>left click</w>. </tiles>";
1193         cmd.push_back(CMD_READ);
1194         cmd.push_back(CMD_MEMORISE_SPELL);
1195         cmd.push_back(CMD_CAST_SPELL);
1196
1197         if (you_worship(GOD_TROG))
1198         {
1199             text << "\nAs a worshipper of "
1200                  << god_name(GOD_TROG)
1201                  << ", though, you might instead wish to burn those tomes "
1202                     "of hated magic by using the corresponding "
1203                     "<w>%</w>bility.";
1204             cmd.push_back(CMD_USE_ABILITY);
1205         }
1206         text << "\nIn hint mode you can reread this information at "
1207                 "any time by "
1208                 "<console>having a look in your <w>%</w>nventory at the item in "
1209                 "question.</console>"
1210                 "<tiles>clicking on it with your <w>right mouse button</w>.</tiles>";
1211         cmd.push_back(CMD_DISPLAY_INVENTORY);
1212         break;
1213
1214     case HINT_SEEN_WEAPON:
1215         text << "This is the first weapon "
1216                 "<console>('<w>"
1217              << stringize_glyph(get_item_symbol(SHOW_ITEM_WEAPON))
1218              << "</w>') </console>"
1219                 "you've picked up. Use <w>%</w> "
1220                 "<tiles>or click on it with your <w>left mouse button</w> </tiles>"
1221                 "to wield it, but be aware that this weapon "
1222                 "might train a different skill from your current one. You can "
1223                 "view the weapon's properties from your <w>%</w>nventory"
1224                 "<tiles> or by <w>right-clicking</w> on it</tiles>"
1225                 ".";
1226
1227         cmd.push_back(CMD_WIELD_WEAPON);
1228         cmd.push_back(CMD_DISPLAY_INVENTORY);
1229
1230         if (Hints.hints_type == HINT_BERSERK_CHAR)
1231         {
1232             text << "\nAs you're already trained in Axes you should stick "
1233                     "with these. Checking other axes' enchantments and "
1234                     "attributes can be worthwhile.";
1235         }
1236         break;
1237
1238     case HINT_SEEN_MISSILES:
1239         text << "This is the first stack of missiles "
1240                 "<console>('<w>"
1241              << stringize_glyph(get_item_symbol(SHOW_ITEM_MISSILE))
1242              << "</w>') </console>"
1243                 "you've picked up. Missiles like tomahwaks and throwing nets "
1244                 "can be thrown by hand, but other missiles like arrows and "
1245                 "needles require a launcher and training in using it to be "
1246                 "really effective. "
1247 #ifdef USE_TILE_LOCAL
1248                 "<w>Right-clicking</w> on "
1249 #else
1250                 "Selecting "
1251 #endif
1252                 "the item in your <w>%</w>nventory will give more "
1253                 "information about both missiles and launcher.";
1254
1255         cmd.push_back(CMD_DISPLAY_INVENTORY);
1256
1257         if (Hints.hints_type == HINT_RANGER_CHAR)
1258         {
1259             text << "\nAs you're already trained in Bows you should stick "
1260                     "with arrows and collect more of them in the dungeon.";
1261         }
1262         else if (Hints.hints_type == HINT_MAGIC_CHAR)
1263         {
1264             text << "\nHowever, as a spellslinger, you don't really need "
1265                     "another type of ranged attack, unless there's another "
1266                     "effect in addition to damage.";
1267         }
1268         else
1269         {
1270             text << "\nFor now you might be best off with sticking to "
1271                     "stones for ranged attacks.";
1272         }
1273         break;
1274
1275     case HINT_SEEN_ARMOUR:
1276         text << "This is the first piece of armour "
1277                 "<console>('<w>"
1278              << stringize_glyph(get_item_symbol(SHOW_ITEM_ARMOUR))
1279              << "</w>') </console>"
1280                 "you've picked up. "
1281                 "<tiles>You can click on it to wear it, and click a second time to "
1282                 "take it off again. Doing a <w>right mouse click</w> will "
1283                 "show you its properties.</tiles>"
1284                 "Use <w>%</w> to wear it and <w>%</w> to take it off again. "
1285                 "You can view its properties from your <w>%</w>nventory.";
1286         cmd.push_back(CMD_WEAR_ARMOUR);
1287         cmd.push_back(CMD_REMOVE_ARMOUR);
1288         cmd.push_back(CMD_DISPLAY_INVENTORY);
1289
1290         if (you.species == SP_CENTAUR || you.species == SP_MINOTAUR)
1291         {
1292             text << "\nNote that as a " << species_name(you.species)
1293                  << " you will be unable to wear "
1294                  << (you.species == SP_CENTAUR ? "boots" : "helmets")
1295                  << ".";
1296         }
1297         break;
1298
1299     case HINT_SEEN_RANDART:
1300         text << "Weapons and armour that have unusual descriptions like this "
1301                 "are much more likely to be of higher enchantment or have "
1302                 "special properties, good or bad.";
1303         break;
1304
1305     case HINT_SEEN_FOOD:
1306         text << "You have picked up some food"
1307                 "<console> ('<w>"
1308              << stringize_glyph(get_item_symbol(SHOW_ITEM_FOOD))
1309              << "</w>')</console>"
1310                 ". You can eat it by typing <w>e</w>"
1311                 "<tiles> or by clicking on it with your <w>left mouse button</w></tiles>"
1312                 ". However, it is usually best to conserve rations and fruit, "
1313                 "since raw meat from corpses is generally plentiful.";
1314         break;
1315
1316     case HINT_SEEN_CARRION:
1317         // NOTE: This is called when a corpse is first seen as well as when
1318         //       first picked up, since a new player might not think to pick
1319         //       up a corpse.
1320         // TODO: Specialcase skeletons!
1321
1322         if (gc.x <= 0 || gc.y <= 0)
1323             text << "Ah, a corpse!";
1324         else
1325         {
1326             int i = you.visible_igrd(gc);
1327             if (i == NON_ITEM)
1328                 text << "Ah, a corpse!";
1329             else
1330             {
1331                 text << "That <console>";
1332                 string glyph = glyph_to_tagstr(get_item_glyph(&mitm[i]));
1333                 const string::size_type found = glyph.find("%");
1334                 if (found != string::npos)
1335                     glyph.replace(found, 1, "percent");
1336                 text << glyph;
1337                 text << "</console> is a corpse.";
1338 #ifdef USE_TILE
1339                 tiles.place_cursor(CURSOR_TUTORIAL, gc);
1340                 tiles.add_text_tag(TAG_TUTORIAL, mitm[i].name(DESC_A), gc);
1341 #endif
1342             }
1343         }
1344
1345         text << " When a corpse is lying on the ground, you "
1346                 "can <w>%</w>hop it up. Once hungry you can "
1347                 "then <w>%</w>at the resulting chunks.";
1348         cmd.push_back(CMD_BUTCHER);
1349         cmd.push_back(CMD_EAT);
1350
1351         text << "<tiles> With tiles, you can also chop up any corpse that shows up in "
1352                 "the floor part of your inventory region, simply by doing a "
1353                 "<w>left mouse click</w> while pressing <w>Shift</w>, and "
1354                 "then eat the resulting chunks with <w>Shift + right mouse "
1355                 "click</w>.</tiles>";
1356
1357         text << "\nIn hint mode you can reread this information at "
1358                 "any time by selecting the item in question in your "
1359                 "<w>%</w>nventory.";
1360         cmd.push_back(CMD_DISPLAY_INVENTORY);
1361         break;
1362
1363     case HINT_SEEN_JEWELLERY:
1364         text << "You have picked up a a piece of jewellery, either a ring"
1365              << "<console> ('<w>"
1366              << stringize_glyph(get_item_symbol(SHOW_ITEM_RING))
1367              << "</w>')</console>"
1368              << " or an amulet"
1369              << "<console> ('<w>"
1370              << stringize_glyph(get_item_symbol(SHOW_ITEM_AMULET))
1371              << "</w>')"
1372              << ". Type <w>%</w> to put it on and <w>%</w> to remove "
1373                 "it. You can view its properties from your <w>%</w>nventory</console>"
1374              << "<tiles>. You can click on it to put it on, and click a second time "
1375                 "remove it again. By clicking on it with your <w>right mouse "
1376                 "button</w> you can view its properties</tiles>.";
1377         cmd.push_back(CMD_WEAR_JEWELLERY);
1378         cmd.push_back(CMD_REMOVE_JEWELLERY);
1379         cmd.push_back(CMD_DISPLAY_INVENTORY);
1380         break;
1381
1382     case HINT_SEEN_MISC:
1383         text << "This is a curious object indeed. You can play around with "
1384                 "it to find out what it does by "
1385                 "<tiles>clicking on it to e<w>%</w>oke </tiles>"
1386                 "<console>e<w>%</w>oking </console>"
1387                 "it. As usual, selecting it from your <w>%</w>nventory "
1388                 "might give you more information.";
1389         cmd.push_back(CMD_EVOKE);
1390         cmd.push_back(CMD_WIELD_WEAPON);
1391         cmd.push_back(CMD_EVOKE_WIELDED);
1392         cmd.push_back(CMD_DISPLAY_INVENTORY);
1393         break;
1394
1395     case HINT_SEEN_ROD:
1396         text << "You have picked up a magical rod"
1397                 "<console> ('<w>";
1398         text << stringize_glyph(get_item_symbol(SHOW_ITEM_ROD))
1399              << "</w>')</console>"
1400                 ". It must be <w>%</w>ielded to be of use. "
1401                 "A rod allows the casting of the unique spell it contains "
1402                 "even without magic knowledge simply by "
1403                 "e<w>%</w>oking it. It has a limited pool of magic which"
1404                 "recharges over time, and its power depends on "
1405                 "your Evocations skill. It can also be used as a cudgel, "
1406                 "with its combat value increasing with its recharge rate.";
1407         cmd.push_back(CMD_WIELD_WEAPON);
1408         cmd.push_back(CMD_EVOKE_WIELDED);
1409
1410         text << "<tiles> Both wielding and evoking a wielded item can be achieved "
1411                 "by clicking on it with your <w>left mouse button</w>.</tiles>"
1412                 "\nIn hint mode you can reread this information at "
1413                 "any time by selecting the item in question in your "
1414                 "<w>%</w>nventory.";
1415         cmd.push_back(CMD_DISPLAY_INVENTORY);
1416         break;
1417
1418     case HINT_SEEN_STAFF:
1419         text << "You have picked up a magic staff"
1420                 "<console> ('<w>";
1421
1422         text << stringize_glyph(get_item_symbol(SHOW_ITEM_STAFF))
1423              << "</w>')</console>"
1424                 ". It must be <w>%</w>ielded to be of use. "
1425                 "Magicians use staves to increase their power in certain "
1426                 "spell schools. It can also be used as a weapon."
1427                 "<tiles> Both wielding and evoking a wielded item can be achieved "
1428                 "by clicking on it with your <w>left mouse button</w>.</tiles>"
1429                 "\nIn hint mode you can reread this information at "
1430                 "any time by selecting the item in question in your "
1431                 "<w>%</w>nventory.";
1432         cmd.push_back(CMD_WIELD_WEAPON);
1433         cmd.push_back(CMD_DISPLAY_INVENTORY);
1434         break;
1435
1436     case HINT_SEEN_GOLD:
1437         text << "You have picked up your first pile of gold"
1438                 "<console> ('<yellow>"
1439              << stringize_glyph(get_item_symbol(SHOW_ITEM_GOLD))
1440              << "</yellow>')</console>"
1441                 ". Unlike most other objects in Crawl it doesn't show up in "
1442                 "your inventory, takes up no space in your inventory, weighs "
1443                 "nothing and can't be dropped. Gold can be used to buy "
1444                 "items from shops, and can also be sacrificed to some gods. ";
1445
1446         text << "Whenever you pick up some gold, your current amount will "
1447                 "be mentioned. If you'd like to check your wealth at other "
1448                 "times, you can press <w>$</w>. It will also be "
1449                 "listed on the <w>%</w> screen.";
1450         cmd.push_back(CMD_LIST_GOLD);
1451         cmd.push_back(CMD_RESISTS_SCREEN);
1452         break;
1453
1454     case HINT_SEEN_STAIRS:
1455         // Don't give this information during the first turn, to give
1456         // the player time to have a look around.
1457         if (you.num_turns < 1)
1458             DELAY_EVENT;
1459
1460         text << "These ";
1461 #ifndef USE_TILE
1462         // Is a monster blocking the view?
1463         if (monster_at(gc))
1464             DELAY_EVENT;
1465
1466         text << glyph_to_tagstr(get_cell_glyph(gc)) << " ";
1467 #else
1468         tiles.place_cursor(CURSOR_TUTORIAL, gc);
1469         tiles.add_text_tag(TAG_TUTORIAL, "Stairs", gc);
1470 #endif
1471         text << "are some downstairs. You can enter the next (deeper) "
1472                 "level by following them down (<w>%</w>). To get back to "
1473                 "this level again, press <w>%</w> while standing on the "
1474                 "upstairs.";
1475         cmd.push_back(CMD_GO_DOWNSTAIRS);
1476         cmd.push_back(CMD_GO_UPSTAIRS);
1477
1478 #ifdef USE_TILE
1479         text << "\nAlternatively, clicking on your <w>left mouse button</w> "
1480                 "will let you follow any stairs you're standing on.";
1481 #endif
1482         break;
1483
1484     case HINT_SEEN_ESCAPE_HATCH:
1485         if (you.num_turns < 1)
1486             DELAY_EVENT;
1487
1488         // monsters standing on stairs
1489         if (monster_at(gc))
1490             DELAY_EVENT;
1491
1492         text << "These ";
1493 #ifndef USE_TILE
1494         text << glyph_to_tagstr(get_cell_glyph(gc));
1495         text << " ";
1496 #else
1497         tiles.place_cursor(CURSOR_TUTORIAL, gc);
1498         tiles.add_text_tag(TAG_TUTORIAL, "Escape hatch", gc);
1499 #endif
1500         text << "are some kind of escape hatch. You can use them to "
1501                 "leave a level with <w>%</w> and <w>%</w> respectively"
1502 #ifdef USE_TILE
1503                 " (or by using your <w>left mouse button</w>)"
1504 #endif
1505                 ", but you will usually be unable to return right away.";
1506         cmd.push_back(CMD_GO_UPSTAIRS);
1507         cmd.push_back(CMD_GO_DOWNSTAIRS);
1508         break;
1509
1510     case HINT_SEEN_BRANCH:
1511         text << "This ";
1512 #ifndef USE_TILE
1513         // Is a monster blocking the view?
1514         if (monster_at(gc))
1515             DELAY_EVENT;
1516
1517         // FIXME: Branch entrance character is not being coloured yellow.
1518         text << glyph_to_tagstr(get_cell_glyph(gc)) << " ";
1519 #else
1520         tiles.place_cursor(CURSOR_TUTORIAL, gc);
1521         tiles.add_text_tag(TAG_TUTORIAL, "Branch stairs", gc);
1522 #endif
1523         text << "is the entrance to a different branch of the dungeon, "
1524                 "which might have different terrain, level layout and "
1525                 "monsters from the current main branch you're in. Some "
1526                 "branches contain only a single level, and others are many "
1527                 "levels deep. They can also contain entrances to other "
1528                 "branches."
1529
1530                 "\n\nThe first three branches you'll encounter are the "
1531                 "Temple, the Lair and the Orcish Mines. While the Lair"
1532                 "and the Mines can be dangerous for the new adventurer, "
1533                 "the Temple is completely safe and contains a number of "
1534                 "altars at which you might convert to a new god.";
1535         break;
1536
1537     case HINT_SEEN_PORTAL:
1538         // Delay in the unlikely event that a player still in hints mode
1539         // creates a portal with a Trowel card, since a portal vault
1540         // entry's description doesn't seem to get set properly until
1541         // after the vault is done being placed.
1542         if (you.pos() == gc)
1543             DELAY_EVENT;
1544
1545         text << "This ";
1546 #ifndef USE_TILE
1547         // Is a monster blocking the view?
1548         if (monster_at(gc))
1549             DELAY_EVENT;
1550
1551         text << glyph_to_tagstr(get_cell_glyph(gc)) << " ";
1552 #else
1553         tiles.place_cursor(CURSOR_TUTORIAL, gc);
1554         tiles.add_text_tag(TAG_TUTORIAL, "Portal", gc);
1555 #endif
1556         text << _describe_portal(gc);
1557         break;
1558
1559     case HINT_STAIR_BRAND:
1560         // Monster or player standing on stairs.
1561         if (actor_at(gc))
1562             DELAY_EVENT;
1563
1564 #ifdef USE_TILE
1565         text << "A small symbol on a stair tile signifies that there are "
1566                 "items in that position that you may want to check out.";
1567 #else
1568         text << "If any items are covering stairs or an escape hatch, then "
1569                 "that will be indicated by highlighting the <w><<</w> or "
1570                 "<w>></w> symbol, instead of hiding the stair symbol with "
1571                 "an item glyph.";
1572 #endif
1573         break;
1574
1575     case HINT_HEAP_BRAND:
1576         // Monster or player standing on heap.
1577         if (actor_at(gc))
1578             DELAY_EVENT;
1579
1580 #ifdef USE_TILE
1581         text << "A small symbol on an item tile signifies that there is at "
1582                 "least one other item in the same heap that you may want to "
1583                 "check out.";
1584         break;
1585 #else
1586         text << "If two or more items are on a single square, then the square "
1587                 "will be highlighted, and the symbol for the item on the top "
1588                 "of the heap will be shown.";
1589 #endif
1590         break;
1591
1592     case HINT_TRAP_BRAND:
1593 #ifdef USE_TILE
1594         // Tiles show both the trap and the item heap.
1595         return;
1596 #else
1597         // Monster or player standing on trap.
1598         if (actor_at(gc))
1599             DELAY_EVENT;
1600
1601         text << "If any items are covering a trap, then that will be "
1602                 "indicated by highlighting the <w>^</w> symbol, instead of "
1603                 "hiding the trap symbol with an item glyph.";
1604 #endif
1605         break;
1606
1607     case HINT_SEEN_TRAP:
1608         if (you.pos() == gc)
1609             text << "Oops... you just triggered a trap. ";
1610         else
1611             text << "You just discovered a trap. ";
1612
1613         text << "An unwary adventurer will occasionally stumble into one "
1614                 "of these nasty constructions";
1615 #ifndef USE_TILE
1616         {
1617             cglyph_t g = get_cell_glyph(gc);
1618
1619             if (g.ch == ' ' || g.col == BLACK)
1620                 g.col = LIGHTCYAN;
1621
1622             text << " depicted by " << _colourize_glyph(g.col, '^');
1623         }
1624 #endif
1625         text << ". They can have a range of effects, such as alerting "
1626                 "enemies to your presence, causing random teleportation, "
1627                 "or worse.";
1628         break;
1629
1630     case HINT_SEEN_ALTAR:
1631         text << "That ";
1632 #ifndef USE_TILE
1633         // Is a monster blocking the view?
1634         if (monster_at(gc))
1635             DELAY_EVENT;
1636
1637         text << glyph_to_tagstr(get_cell_glyph(gc)) << " ";
1638 #else
1639         {
1640             tiles.place_cursor(CURSOR_TUTORIAL, gc);
1641             string altar = "An altar to ";
1642             altar += god_name(feat_altar_god(grd(gc)));
1643             tiles.add_text_tag(TAG_TUTORIAL, altar, gc);
1644         }
1645 #endif
1646         text << "is an altar. "
1647 #ifdef USE_TILE
1648                 "By <w>rightclicking</w> on it with your mouse "
1649 #else
1650                 "If you target the altar with <w>x</w>, then press <w>v</w> "
1651 #endif
1652                 "you can get a short description.\n"
1653                 "Press <w>%</w> while standing on the square to join the faith "
1654                 "or read some information about the god in question. Before "
1655                 "taking up the corresponding faith you'll be asked for "
1656                 "confirmation.";
1657         cmd.push_back(CMD_PRAY);
1658
1659         if (you_worship(GOD_NO_GOD)
1660             && Hints.hints_type == HINT_MAGIC_CHAR)
1661         {
1662             text << "\n\nThe best god for an unexperienced conjurer is "
1663                     "probably Vehumet, though Sif Muna is a good second "
1664                     "choice.";
1665         }
1666         break;
1667
1668     case HINT_SEEN_SHOP:
1669 #ifdef USE_TILE
1670         tiles.place_cursor(CURSOR_TUTORIAL, gc);
1671         tiles.add_text_tag(TAG_TUTORIAL, shop_name(gc), gc);
1672 #else
1673         // Is a monster blocking the view?
1674         if (monster_at(gc))
1675             DELAY_EVENT;
1676 #endif
1677         text << "That "
1678 #ifndef USE_TILE
1679              << glyph_to_tagstr(get_cell_glyph(gc)) << " "
1680 #endif
1681                 "is a shop. You can enter it by typing <w>%</w> or <w>%</w>"
1682 #ifdef USE_TILE
1683                 ", or by clicking on it with your <w>left mouse button</w> "
1684 #endif
1685                 "while standing on the square.";
1686         cmd.push_back(CMD_GO_UPSTAIRS);
1687         cmd.push_back(CMD_GO_DOWNSTAIRS);
1688
1689         text << "\n\nIf there's anything you want which you can't afford yet "
1690                 "you can hold <w>Shift</w> and select those items to put them "
1691                 "on your shopping list. The game will then remind you when "
1692                 "you gather enough gold to buy the items on your list.";
1693         break;
1694
1695     case HINT_SEEN_DOOR:
1696         if (you.num_turns < 1)
1697             DELAY_EVENT;
1698
1699 #ifdef USE_TILE
1700         tiles.place_cursor(CURSOR_TUTORIAL, gc);
1701         tiles.add_text_tag(TAG_TUTORIAL, "Closed door", gc);
1702 #endif
1703
1704         text << "That "
1705 #ifndef USE_TILE
1706              << glyph_to_tagstr(get_cell_glyph(gc)) << " "
1707 #endif
1708                 "is a closed door. You can open it by walking into it. "
1709                 "Sometimes it is useful to close a door. Do so by pressing "
1710                 "<w>%</w> while standing next to it. If there are several "
1711                 "doors, you will then be prompted for a direction. "
1712                 "Alternatively, you can also use <w>Ctrl-Direction</w>.";
1713         cmd.push_back(CMD_CLOSE_DOOR);
1714
1715 #ifdef USE_TILE
1716         text << "\nIn Tiles, the same can be achieved by clicking on an "
1717                 "adjacent door square.";
1718 #endif
1719         if (!Hints.hints_explored)
1720         {
1721             text << "\nTo avoid accidentally opening a door you'd rather "
1722                     "remain closed during travel or autoexplore, you can mark "
1723                     "it with an exclusion from the map view (<w>%</w>) with "
1724                     "<w>ee</w> while your cursor is on the grid in question. "
1725                     "Such an exclusion will prevent autotravel from ever "
1726                     "entering that grid until you remove the exclusion with "
1727                     "another press of <w>%e</w>.";
1728             cmd.push_back(CMD_DISPLAY_MAP);
1729             cmd.push_back(CMD_DISPLAY_MAP);
1730         }
1731         break;
1732
1733     case HINT_SEEN_RUNED_DOOR:
1734 #ifdef USE_TILE
1735         tiles.place_cursor(CURSOR_TUTORIAL, gc);
1736         tiles.add_text_tag(TAG_TUTORIAL, "Runed door", gc);
1737 #endif
1738         text << "That ";
1739 #ifndef USE_TILE
1740         text << glyph_to_tagstr(get_cell_glyph(gc)) << " ";
1741 #endif
1742         text << "is a runed door. The runic writings covering it are a "
1743                 "warning of nearby danger. Other denizens of the dungeon will "
1744                 "typically leave it alone, but other than that it functions "
1745                 "no differently from a normal door. You may elect to disregard "
1746                 "the warning and open it anyway. Doing so will break the runes.";
1747         break;
1748
1749     case HINT_KILLED_MONSTER:
1750         text << "Congratulations, your character just gained some experience "
1751                 "by killing this monster! This will raise some of your skills, "
1752                 "making you more deadly.";
1753         // A more detailed description of skills is given when you go past an
1754         // integer point.
1755
1756         if (you_worship(GOD_TROG))
1757         {
1758             text << " Also, most kills will grant you favour in the eyes of "
1759                     "Trog.";
1760         }
1761         break;
1762
1763     case HINT_NEW_LEVEL:
1764         if (you.skills[SK_SPELLCASTING])
1765         {
1766             if (!crawl_state.game_is_hints())
1767             {
1768                 text << "Gaining an experience level allows you to learn more "
1769                         "difficult spells. However, you don't have any level "
1770                         "two spells in your current spellbook, so you'll just "
1771                         "have to keep exploring!";
1772                 break;
1773             }
1774             text << "Gaining an experience level allows you to learn more "
1775                     "difficult spells. Time to memorise your second spell "
1776                     "with <w>%</w>"
1777 #ifdef USE_TILE
1778                  << " or by <w>clicking</w> on it in the memorisation tab"
1779 #endif
1780                  << ".";
1781             cmd.push_back(CMD_MEMORISE_SPELL);
1782         }
1783         else
1784         {
1785             text << "Well done! Reaching a new experience level is always a "
1786                     "nice event: you get more health and magic points, and "
1787                     "occasionally increases to your attributes (strength, "
1788                     "intelligence, dexterity).";
1789         }
1790
1791         if (Hints.hints_type == HINT_MAGIC_CHAR)
1792         {
1793             text << "\nAlso, new experience levels let you learn more spells "
1794                     "(the Spellcasting skill also does this). For now, you "
1795                     "should try to memorise the second spell of your "
1796                     "starting book with <w>%a</w>, which can then be zapped "
1797                     "with <w>%b</w>.";
1798 #ifdef USE_TILE
1799             text << " Memorising is also possible by doing a <w>left "
1800                     "click</w> on the book in your inventory, or by clicking "
1801                     "on the <w>spellbook tab</w> to the left of your "
1802                     "inventory.";
1803 #endif
1804             cmd.push_back(CMD_MEMORISE_SPELL);
1805             cmd.push_back(CMD_CAST_SPELL);
1806         }
1807         break;
1808
1809     case HINT_SKILL_RAISE:
1810
1811         text << "One of your skills just passed a whole integer point. The "
1812                 "skills you use are automatically trained whenever you gain "
1813                 "experience (by killing monsters). By default, experience goes "
1814                 "towards skills you actively use, although you may choose "
1815                 "otherwise. To view or manage your skill set, type <w>%</w>.";
1816
1817         cmd.push_back(CMD_DISPLAY_SKILLS);
1818         break;
1819
1820     case HINT_GAINED_MAGICAL_SKILL:
1821         text << "Being skilled in a magical \"school\" makes it easier to "
1822                 "learn and cast spells of this school. Many spells belong to "
1823                 "a combination of several schools, in which case the average "
1824                 "skill in these schools will decide on spellcasting success "
1825                 "and power.";
1826         break;
1827
1828     case HINT_GAINED_MELEE_SKILL:
1829         text << "Being skilled with a particular type of weapon will make it "
1830                 "easier to fight with all weapons of this type, and make you "
1831                 "deal more damage with them. It is generally recommended to "
1832                 "concentrate your efforts on one or two weapon types to become "
1833                 "more powerful in them. Some weapons are closely related, and "
1834                 "being trained in one will ease training the other. This is "
1835                 "true for the following pairs: Short Blades/Long Blades, "
1836                 "Axes/Polearms, Polearms/Staves, Axes/Maces and Maces/Staves.";
1837         break;
1838
1839     case HINT_GAINED_RANGED_SKILL:
1840         text << "Being skilled in a particular type of ranged attack will let "
1841                 "you deal more damage when using the appropriate weapons. It "
1842                 "is usually best to concentrate on one type of ranged attack "
1843                 "(including spells), and to add another one as back-up.";
1844         break;
1845
1846     case HINT_CHOOSE_STAT:
1847         text << "Every third level you get to choose a stat to raise: "
1848                 "Strength, Intelligence, or Dexterity. "
1849                 "<w>Strength</w> affects your effectiveness in combat "
1850                 "and makes it easier to wear heavy armour. "
1851                 "<w>Intelligence</w> makes it easier to cast spells and "
1852                 "reduces the amount by which you hunger when you do so. "
1853                 "<w>Dexterity</w> increases your evasion "
1854                 "and stealth.\n";
1855         break;
1856
1857     case HINT_YOU_ENCHANTED:
1858         text << "Enchantments of all types can befall you temporarily. "
1859                 "Brief descriptions of these appear at the lower end of the "
1860                 "stats area. Press <w>%</w> for more details. A list of all "
1861                 "possible enchantments is in the manual (<w>%5</w>).";
1862         cmd.push_back(CMD_DISPLAY_CHARACTER_STATUS);
1863         cmd.push_back(CMD_DISPLAY_COMMANDS);
1864         break;
1865
1866     case HINT_YOU_POISON:
1867         if (crawl_state.game_is_hints())
1868         {
1869             // Hack: reset hints_just_triggered, to force recursive calling of
1870             // learned_something_new(). Don't do this for the tutorial!
1871             Hints.hints_just_triggered = false;
1872             learned_something_new(HINT_YOU_ENCHANTED);
1873             Hints.hints_just_triggered = true;
1874         }
1875         text << "Poison will slowly reduce your health. You can try to wait it out "
1876                 "with <w>%</w>, but if you're low on health it's usually safer "
1877                 "to quaff a potion of curing.";
1878         cmd.push_back(CMD_REST);
1879         break;
1880
1881     case HINT_YOU_ROTTING:
1882         // Hack: Reset hints_just_triggered, to force recursive calling of
1883         //       learned_something_new().
1884         Hints.hints_just_triggered = false;
1885         learned_something_new(HINT_YOU_ENCHANTED);
1886         Hints.hints_just_triggered = true;
1887
1888         text << "Ugh, your flesh has been rotted away! This reduces your"
1889                 "<w>maximum</w> health (your usual maximum health will be "
1890                 "indicated by a number in parentheses).\n"
1891                 "You can restore your maximum health to normal by drinking "
1892                 "potions of curing and heal wounds.";
1893         cmd.push_back(CMD_QUAFF);
1894         break;
1895
1896     case HINT_YOU_CURSED:
1897         text << "Cursed equipment, once worn or wielded, cannot be dropped or "
1898                 "removed. Curses can be removed by reading certain scrolls.";
1899         break;
1900
1901     case HINT_REMOVED_CURSE:
1902         text << "The curses on your worn equipment have been removed, so you "
1903                 "can now unequip any previously cursed items.";
1904         break;
1905
1906     case HINT_YOU_HUNGRY:
1907         text << "There are two ways to overcome hunger: food you started "
1908                 "with or found, and self-made chunks from corpses. To get the "
1909                 "latter, all you need to do is <w>%</w>hop up a corpse. "
1910                 "Luckily, all adventurers carry a pocket knife with them "
1911                 "which is perfect for butchering. Try to dine on chunks in "
1912                 "order to save permanent food.";
1913         if (Hints.hints_type == HINT_BERSERK_CHAR)
1914             text << "\nNote that you cannot Berserk while very hungry or worse.";
1915         cmd.push_back(CMD_BUTCHER);
1916         break;
1917
1918     case HINT_YOU_STARVING:
1919         text << "You are now suffering from terrible hunger. You'll need to "
1920                 "<w>%</w>at something quickly, or you'll die. The safest "
1921                 "way to deal with this is to simply eat something from your "
1922                 "inventory, rather than wait for a monster to leave a corpse.";
1923         cmd.push_back(CMD_EAT);
1924
1925         if (Hints.hints_type == HINT_MAGIC_CHAR)
1926             text << "\nNote that you cannot cast spells while starving.";
1927         break;
1928
1929     case HINT_MULTI_PICKUP:
1930         text << "There are a lot of items here. You choose what to pick up "
1931                 "from a menu: type <w>%</w> "
1932 #ifdef USE_TILE
1933                 "or <w>click</w> on the player doll "
1934 #endif
1935                 "to enter the pickup menu. To leave the menu, confirm your "
1936                 "selection with <w>Enter</w>.";
1937         cmd.push_back(CMD_PICKUP);
1938         break;
1939
1940     case HINT_FULL_INVENTORY:
1941         text << "Sadly, your inventory is limited to 52 items, and it "
1942             "appears your knapsack is full.";
1943         text << " However, this is easy enough to rectify: simply "
1944                 "<w>%</w>rop some of the stuff you don't need right now.";
1945         cmd.push_back(CMD_DROP);
1946
1947 #ifdef USE_TILE
1948         text << " In the drop menu you can then comfortably select which "
1949                 "items to drop by pressing their inventory letter, or by "
1950                 "clicking on them.";
1951 #endif
1952
1953         if (Hints.hints_stashes)
1954         {
1955             text << "\n\nYou can easily find items you've left on the floor "
1956                     "with the <w>%</w> command, which will let you "
1957                     "search for all known items in the dungeon. For example, "
1958                     "<w>% \"dagger\"</w> will list all daggers. You can "
1959                     "can then travel to one of the spots.";
1960             Hints.hints_stashes = false;
1961             cmd.push_back(CMD_SEARCH_STASHES);
1962             cmd.push_back(CMD_SEARCH_STASHES);
1963         }
1964         break;
1965
1966     case HINT_MAKE_CHUNKS:
1967         text << "How lucky! That monster left a corpse which you can now "
1968                 "<w>%</w>hop up. One or more chunks will appear that you can "
1969                 "then <w>%</w>at. Beware that some chunks may be hazardous. "
1970                 "You can find out whether that might be the case by ";
1971         cmd.push_back(CMD_BUTCHER);
1972         cmd.push_back(CMD_EAT);
1973
1974 #ifdef USE_TILE
1975         text << "hovering your mouse over the corpse or chunk.";
1976 #else
1977         text << "<w>v</w>iewing a corpse or chunk on the floor or by having a "
1978                 "look at it in your <w>%</w>nventory.";
1979         cmd.push_back(CMD_DISPLAY_INVENTORY);
1980 #endif
1981         break;
1982
1983     case HINT_SHIFT_RUN:
1984         text << "Walking around takes fewer keystrokes if you press "
1985                 "<w>Shift-direction</w> or <w>/ direction</w>. "
1986                 "That will let you run until a monster comes into sight or "
1987                 "your character sees something interesting.";
1988         break;
1989
1990     case HINT_MAP_VIEW:
1991         text << "As you explore a level, orientation can become difficult. "
1992                 "Press <w>%</w> to bring up the level map. Typing <w>?</w> "
1993                 "shows the list of level map commands. "
1994                 "Most importantly, moving the cursor to a spot and pressing "
1995                 "<w>.</w> or <w>Enter</w> lets your character move there on "
1996                 "its own.";
1997         cmd.push_back(CMD_DISPLAY_MAP);
1998
1999 #ifdef USE_TILE
2000         text << "\nAlso, clicking on the right-side minimap with your "
2001                 "<w>right mouse button</w> will zoom into that dungeon area. "
2002                 "Clicking with the <w>left mouse button</w> instead will let "
2003                 "you move there.";
2004 #endif
2005         break;
2006
2007     case HINT_AUTO_EXPLORE:
2008         if (!Hints.hints_explored)
2009             return;
2010
2011         text << "Fully exploring a level and picking up all the interesting "
2012                 "looking items can be tedious. To save on this tedium you "
2013                 "can press <w>%</w> to auto-explore, which will "
2014                 "automatically explore unmapped regions, automatically pick "
2015                 "up interesting items, and stop if a monster or interesting "
2016                 "dungeon feature (stairs, altar, etc.) is encountered.";
2017         cmd.push_back(CMD_EXPLORE);
2018         Hints.hints_explored = false;
2019         break;
2020
2021     case HINT_DONE_EXPLORE:
2022         // XXX: You'll only get this message if you're using auto exploration.
2023         text << "Hey, you've finished exploring the dungeon on this level! "
2024                 "You can search for stairs from the level map (<w>%</w>) "
2025                 "by pressing <w>></w>. The cursor will jump to the nearest "
2026                 "staircase, and by pressing <w>.</w> or <w>Enter</w> your "
2027                 "character can move there, too. Each level of Crawl has three "
2028 #ifndef USE_TILE
2029                 "white "
2030 #endif
2031                 "up and three "
2032 #ifndef USE_TILE
2033                 "white "
2034 #endif
2035                 "down stairs. Unexplored parts can often be accessed via "
2036                 "another level.";
2037         cmd.push_back(CMD_DISPLAY_MAP);
2038         break;
2039
2040     case HINT_AUTO_EXCLUSION:
2041         // In the highly unlikely case the player encounters a
2042         // hostile statue or oklob plant during the hints mode...
2043         if (Hints.hints_explored)
2044         {
2045             // Hack: Reset hints_just_triggered, to force recursive calling of
2046             //       learned_something_new().
2047             Hints.hints_just_triggered = false;
2048             learned_something_new(HINT_AUTO_EXPLORE);
2049             Hints.hints_just_triggered = true;
2050         }
2051         text << "\nTo prevent autotravel or autoexplore taking you into "
2052                 "dangerous territory, you can set travel exclusions by "
2053                 "entering the map view (<w>%</w>) and then toggling the "
2054                 "exclusion radius on the monster position with <w>e</w>. "
2055                 "To make this easier some immobile monsters listed in the "
2056                 "<w>auto_exclude</w> option (such as this one) are considered "
2057                 "dangerous enough to warrant an automatic setting of an "
2058                 "exclusion. It will be automatically cleared if you manage to "
2059                 "kill the monster. You could also manually remove the "
2060                 "exclusion with <w>%ee</w> but unless you remove this monster "
2061                 "from the auto_exclude list, the exclusion will be reset the "
2062                 "next turn.";
2063         cmd.push_back(CMD_DISPLAY_MAP);
2064         cmd.push_back(CMD_DISPLAY_MAP);
2065         break;
2066
2067     case HINT_HEALING_POTIONS:
2068         text << "Your health is getting dangerously low. Retreating and/or "
2069                 "quaffing a potion of heal wounds or curing might be a good idea.";
2070         break;
2071
2072     case HINT_NEED_HEALING:
2073         text << "If you're low on health or magic and there's no urgent "
2074                 "need to move, you can rest for a bit. Ideally, you should "
2075                 "retreat to an area you've already explored and cleared "
2076                 "of monsters before resting, since resting on the edge of "
2077                 "the explored terrain increases the risk of rest being "
2078                 "interrupted by a wandering monster. Press <w>%</w> or "
2079                 "<w>shift-numpad-5</w>"
2080 #ifdef USE_TILE
2081                 ", or click into the stat area"
2082 #endif
2083                 " to do so.";
2084         cmd.push_back(CMD_REST);
2085         break;
2086
2087     case HINT_NEED_POISON_HEALING:
2088         text << "You are lethally poisoned, so now would be a good time to "
2089                 "<w>%</w>uaff a potion of heal wounds or, better yet, a "
2090                 "potion of curing. If you have seen neither of these so far, "
2091                 "try unknown potions in your inventory. Good luck!";
2092         cmd.push_back(CMD_QUAFF);
2093         break;
2094
2095     case HINT_INVISIBLE_DANGER:
2096         text << "Fighting against a monster you cannot see is difficult. "
2097                 "Your best bet is probably a strategic retreat, be it via "
2098                 "teleportation or by getting off the level. "
2099                 "Or else, luring the monster into a corridor should at least "
2100                 "make it easier for you to hit it.";
2101
2102         // To prevent this text being immediately followed by the next one...
2103         Hints.hints_last_healed = you.num_turns - 30;
2104         break;
2105
2106     case HINT_NEED_HEALING_INVIS:
2107         text << "You recently noticed an invisible monster, so unless you "
2108                 "killed it or left the scene resting might not be safe. If you "
2109                 "still need to replenish your health or magic, you'll have "
2110                 "to quaff an appropriate potion. For normal resting you will "
2111                 "first have to get away from the danger.";
2112
2113         Hints.hints_last_healed = you.num_turns;
2114         break;
2115
2116     case HINT_CAN_BERSERK:
2117         // Don't print this information if the player already knows it.
2118         if (Hints.hints_berserk_counter)
2119             return;
2120
2121         text << "Against particularly difficult foes, you should use your "
2122                 "Berserk <w>%</w>bility. Berserk will last longer if you "
2123                 "kill a lot of monsters.";
2124         cmd.push_back(CMD_USE_ABILITY);
2125         break;
2126
2127     case HINT_POSTBERSERK:
2128         text << "Berserking is extremely exhausting! It burns a lot of "
2129                 "nutrition, and afterwards you are slowed down and "
2130                 "occasionally even pass out. Press <w>%</w> to see your "
2131                 "current exhaustion status.";
2132         cmd.push_back(CMD_DISPLAY_CHARACTER_STATUS);
2133         break;
2134
2135     case HINT_RUN_AWAY:
2136         text << "Whenever your health is very low and you're in danger of "
2137                 "dying, check your options carefully. Often, retreat or use "
2138                 "of some item might be a viable alternative to fighting on.";
2139
2140         if (you.species == SP_CENTAUR)
2141         {
2142             text << " As a four-legged centaur you are particularly quick - "
2143                     "running is an option!";
2144         }
2145
2146         text << " If retreating to another level, keep in mind that monsters "
2147                 "may follow you if they're standing right next to you when "
2148                 "you start climbing or descending the stairs. And even if "
2149                 "you've managed to shake them off, they'll still be there when "
2150                 "you come back, so you might want to use a different set of "
2151                 "stairs when you return.";
2152
2153         if (you_worship(GOD_TROG) && you.can_go_berserk())
2154         {
2155             text << "\nAlso, with "
2156                  << apostrophise(god_name(you.religion))
2157                  << " support you can use your Berserk ability (<w>%</w>) to "
2158                     "temporarily gain more health and greater strength. Bear "
2159                     "in mind that berserking at the last minute is often "
2160                     "risky, and prevents you from using items to escape!";
2161             cmd.push_back(CMD_USE_ABILITY);
2162         }
2163         break;
2164
2165     case HINT_RETREAT_CASTER:
2166         text << "Without magical power you're unable to cast spells. While "
2167                 "melee is a possibility, that's not where your strengths "
2168                 "lie, so retreat (if possible) might be the better option.";
2169
2170         if (_advise_use_wand())
2171         {
2172             text << "\n\nOr you could e<w>%</w>oke a wand to deal damage.";
2173             cmd.push_back(CMD_EVOKE);
2174         }
2175         break;
2176
2177     case HINT_YOU_MUTATED:
2178         text << "Mutations can be obtained from several sources, among them "
2179                 "potions, spell miscasts, and overuse of strong enchantments "
2180                 "like invisibility. The only reliable way to get rid of "
2181                 "mutations is with potions of cure mutation. There are about "
2182                 "as many harmful as beneficial mutations, and some of them "
2183                 "have multiple levels. Check your mutations with <w>%</w>.";
2184         cmd.push_back(CMD_DISPLAY_MUTATIONS);
2185         break;
2186
2187     case HINT_NEW_ABILITY_GOD:
2188         switch (you.religion)
2189         {
2190         // Gods where first granted ability is passive.
2191         case GOD_ASHENZARI:
2192         case GOD_BEOGH:
2193         case GOD_MAKHLEB:
2194         case GOD_VEHUMET:
2195         case GOD_XOM:
2196         case GOD_SHINING_ONE:
2197             text << "You just gained a divine ability. Press <w>%</w> "
2198 #ifdef USE_TILE
2199                     "or press <w>Shift</w> and <w>right-click</w> on the "
2200                     "player tile "
2201 #endif
2202                     "to take a look at your abilities.";
2203             cmd.push_back(CMD_DISPLAY_RELIGION);
2204             break;
2205
2206         // Gods where first granted ability is active.
2207         default:
2208             text << "You just gained a divine ability. Press <w>%</w> to "
2209                     "take a look at your abilities or to use one of them.";
2210             cmd.push_back(CMD_USE_ABILITY);
2211             break;
2212         }
2213         break;
2214
2215     case HINT_NEW_ABILITY_MUT:
2216         text << "That mutation granted you a new ability. Press <w>%</w> to "
2217                 "take a look at your abilities or to use one of them.";
2218         cmd.push_back(CMD_USE_ABILITY);
2219         break;
2220
2221     case HINT_NEW_ABILITY_ITEM:
2222         // Specialcase flight because it's a guaranteed trigger in the
2223         // tutorial.
2224         if (you.evokable_flight())
2225         {
2226             text << "Flight will allow you to cross deep water or lava. To "
2227                     "activate it, select the corresponding ability in the ability "
2228                     "menu (<w>%</w>"
2229 #ifdef USE_TILE
2230                     " or via <w>mouseclick</w> in the <w>command panel</w>"
2231 #endif
2232                     "). Once flying, keep an eye on the status line and messages "
2233                     "as it will eventually time out and may cause you to fall "
2234                     "into water and drown.";
2235         }
2236         else
2237         {
2238             text << "That item you just equipped granted you a new ability. "
2239                     "Press <w>%</w> "
2240 #ifdef USE_TILE
2241                     "(or <w>click</w> in the <w>command panel</w>) "
2242 #endif
2243                     "to take a look at your abilities or to use one of them.";
2244         }
2245         cmd.push_back(CMD_USE_ABILITY);
2246         break;
2247
2248     case HINT_ITEM_RESISTANCES:
2249         text << "Equipping this item affects your resistances. Check the "
2250                 "overview screen (<w>%</w>"
2251 #ifdef USE_TILE
2252                 " or click on the <w>character overview button</w> in the "
2253                 "command panel"
2254 #endif
2255                 ") for details.";
2256         cmd.push_back(CMD_RESISTS_SCREEN);
2257         break;
2258
2259     case HINT_FLYING:
2260         if (you.evokable_flight())
2261         {
2262             text << "To stop flying, use the corresponding ability "
2263                     "in the ability menu (<w>%</w>).";
2264             cmd.push_back(CMD_USE_ABILITY);
2265         }
2266         break;
2267
2268     case HINT_INACCURACY:
2269         text << "Not all items are useful, and some of them are outright "
2270                 "harmful. Press <w>%</w> ";
2271 #ifdef USE_TILE
2272         text << "or <w>click</w> on your equipped amulet to remove it.";
2273 #else
2274         text << "to remove your amulet.";
2275 #endif
2276         cmd.push_back(CMD_REMOVE_JEWELLERY);
2277         break;
2278
2279     case HINT_CONVERT:
2280         if (you_worship(GOD_XOM))
2281             return print_hint("HINT_CONVERT Xom");
2282
2283         print_hint("HINT_CONVERT");
2284         break;
2285
2286     case HINT_GOD_DISPLEASED:
2287         text << "Uh-oh, " << god_name(you.religion) << " is growing "
2288                 "displeased because your piety is running low. Possibly this "
2289                 "is the case because you're committing heretic acts, "
2290                 "because " << god_name(you.religion) << " finds your "
2291                 "worship lacking, or a combination of the two. "
2292                 "If your piety goes to zero, then you'll be excommunicated. "
2293                 "Better get cracking on raising your piety, and/or stop "
2294                 "annoying your god. ";
2295
2296         text << "In any case, you'd better reread the religious description. "
2297                 "To do so, type <w>%</w>"
2298 #ifdef USE_TILE
2299                 " or press <w>Shift</w> and <w>right-click</w> on your avatar"
2300 #endif
2301                 ".";
2302         cmd.push_back(CMD_DISPLAY_RELIGION);
2303         break;
2304
2305     case HINT_EXCOMMUNICATE:
2306     {
2307         const god_type new_god   = (god_type) gc.x;
2308         const int      old_piety = gc.y;
2309
2310         god_type old_god = GOD_NO_GOD;
2311         for (int i = 0; i < NUM_GODS; i++)
2312             if (you.worshipped[i] > 0)
2313             {
2314                 old_god = (god_type) i;
2315                 break;
2316             }
2317
2318         const string old_god_name  = god_name(old_god);
2319         const string new_god_name  = god_name(new_god);
2320
2321         if (new_god == GOD_NO_GOD)
2322         {
2323             if (old_piety < 1)
2324             {
2325                 text << "Uh-oh, " << old_god_name << " just excommunicated you "
2326                         "for running out of piety (your divine favour went "
2327                         "to nothing). Maybe you repeatedly violated the "
2328                         "religious rules, or maybe you failed to please your "
2329                         "deity often enough, or some combination of the two. "
2330                         "If you can find an altar dedicated to "
2331                      << old_god_name;
2332             }
2333             else
2334             {
2335                 text << "Should you decide that abandoning " << old_god_name
2336                      << "wasn't such a smart move after all, and you'd like to "
2337                         "return to your old faith, you'll have to find an "
2338                         "altar dedicated to " << old_god_name << " where";
2339             }
2340             text << " you can re-convert, and all will be well. Otherwise "
2341                     "you'll have to weather this god's displeasure until all "
2342                     "divine wrath is spent.";
2343
2344         }
2345         else
2346         {
2347             bool angry = false;
2348             if (is_good_god(old_god))
2349             {
2350                 if (is_good_god(new_god))
2351                 {
2352                     text << "Fortunately, it seems that " << old_god_name <<
2353                             " didn't mind your converting to " << new_god_name
2354                          << ". ";
2355
2356                     if (old_piety > piety_breakpoint(0))
2357                         text << "You even kept some of your piety! ";
2358
2359                     text << "Note that this kind of alliance only exists "
2360                             "between the three good gods, so don't expect this "
2361                             "to be the norm.";
2362                 }
2363                 else if (!god_hates_your_god(old_god))
2364                 {
2365                     text << "Fortunately, it seems that " << old_god_name <<
2366                             " didn't mind your converting to " << new_god_name
2367                          << ". That's because " << old_god_name << " is one of "
2368                             "the good gods who generally are rather forgiving "
2369                             "about change of faith - unless you switch over to "
2370                             "the path of evil, in which case their retribution "
2371                             "can be nasty indeed!";
2372                 }
2373                 else
2374                 {
2375                     text << "Looks like " << old_god_name << " didn't "
2376                             "appreciate your converting to " << new_god_name
2377                          << "! But really, changing from one of the good gods "
2378                             "to an evil one, what did you expect!? For any god "
2379                             "not on the opposing side of the faith, "
2380                          << old_god_name << " would have been much more "
2381                             "forgiving. ";
2382
2383                     angry = true;
2384                 }
2385             }
2386             else
2387             {
2388                 text << "Looks like " << old_god_name << " didn't appreciate "
2389                         "your converting to " << new_god_name << "! (Actually, "
2390                         "only the three good gods will sometimes be forgiving "
2391                         "about this kind of faithlessness.) ";
2392
2393                 angry = true;
2394             }
2395
2396             if (angry)
2397             {
2398                 text << "Unfortunately, while converting back would appease "
2399                      << old_god_name << ", it would annoy " << new_god_name
2400                      << ", so you're stuck with having to suffer the wrath of "
2401                         "one god or another.";
2402             }
2403         }
2404
2405         break;
2406     }
2407
2408     case HINT_WIELD_WEAPON:
2409     {
2410         int wpn = you.equip[EQ_WEAPON];
2411         if (wpn != -1
2412             && you.inv[wpn].base_type == OBJ_WEAPONS
2413             && you.inv[wpn].cursed())
2414         {
2415             // Don't trigger if the wielded weapon is cursed.
2416             Hints.hints_events[seen_what] = true;
2417             return;
2418         }
2419
2420         if (Hints.hints_type == HINT_RANGER_CHAR && wpn != -1
2421             && you.inv[wpn].is_type(OBJ_WEAPONS, WPN_SHORTBOW))
2422         {
2423             text << "You can easily switch between weapons in slots a and "
2424                     "b by pressing <w>%</w>.";
2425             cmd.push_back(CMD_WEAPON_SWAP);
2426         }
2427         else
2428         {
2429             text << "You can easily switch back to your weapon in slot a by "
2430                     "pressing <w>%</w>. To change the slot of an item, type "
2431                     "<w>%i</w> and choose the appropriate slots.";
2432             cmd.push_back(CMD_WEAPON_SWAP);
2433             cmd.push_back(CMD_ADJUST_INVENTORY);
2434         }
2435         break;
2436     }
2437     case HINT_FLEEING_MONSTER:
2438         if (Hints.hints_type != HINT_BERSERK_CHAR)
2439             return;
2440
2441         text << "Now that monster is scared of you! Note that you do not "
2442                 "absolutely have to follow it. Rather, you can let it run "
2443                 "away. Sometimes, though, it can be useful to attack a "
2444                 "fleeing creature by throwing something after it. If you "
2445                 "have any stones in your <w>%</w>nventory, you can look "
2446                 "at one of them to read an explanation of how to do this.";
2447         cmd.push_back(CMD_DISPLAY_INVENTORY);
2448         break;
2449
2450     case HINT_MONSTER_BRAND:
2451 #ifdef USE_TILE
2452         tiles.place_cursor(CURSOR_TUTORIAL, gc);
2453         if (const monster* m = monster_at(gc))
2454             tiles.add_text_tag(TAG_TUTORIAL, m->name(DESC_A), gc);
2455 #endif
2456         text << "That monster looks a bit unusual. You might wish to examine "
2457                 "it a bit more closely by "
2458 #ifdef USE_TILE
2459                 "hovering your mouse over its tile.";
2460 #else
2461                 "pressing <w>%</w> and moving the cursor onto its square.";
2462         cmd.push_back(CMD_LOOK_AROUND);
2463 #endif
2464         break;
2465
2466     case HINT_MONSTER_FRIENDLY:
2467     {
2468         const monster* m = monster_at(gc);
2469
2470         if (!m)
2471             DELAY_EVENT;
2472
2473 #ifdef USE_TILE
2474         tiles.place_cursor(CURSOR_TUTORIAL, gc);
2475         tiles.add_text_tag(TAG_TUTORIAL, m->name(DESC_A), gc);
2476 #endif
2477         text << "That monster is friendly to you and will attack your "
2478                 "enemies, though you'll get only part of the experience for "
2479                 "monsters damaged by allies, compared to what you'd get for "
2480                 "doing all the work yourself. You can command your allies by "
2481                 "pressing <w>%</w> to talk to them.";
2482         cmd.push_back(CMD_SHOUT);
2483
2484         if (!mons_att_wont_attack(m->attitude))
2485         {
2486             text << "\nHowever, it is only <w>temporarily</w> friendly, and "
2487                     "will become dangerous again when this friendliness "
2488                     "wears off.";
2489         }
2490         break;
2491     }
2492
2493     case HINT_MONSTER_SHOUT:
2494     {
2495         const monster* m = monster_at(gc);
2496
2497         if (!m)
2498             DELAY_EVENT;
2499
2500         // "Shouts" from zero experience monsters are boring, ignore
2501         // them.
2502         if (mons_class_flag(m->type, M_NO_EXP_GAIN))
2503         {
2504             Hints.hints_events[HINT_MONSTER_SHOUT] = true;
2505             return;
2506         }
2507
2508         const bool vis = you.can_see(m);
2509
2510 #ifdef USE_TILE
2511         if (vis)
2512         {
2513             tiles.place_cursor(CURSOR_TUTORIAL, gc);
2514             tiles.add_text_tag(TAG_TUTORIAL, m->name(DESC_A), gc);
2515         }
2516 #endif
2517         if (!vis)
2518         {
2519             text << "Uh-oh, some monster noticed you, either one that's "
2520                     "around a corner or one that's invisible. Plus, the "
2521                     "noise it made will alert other monsters in the "
2522                     "vicinity, who will come to check out what the commotion "
2523                     "was about.";
2524         }
2525         else if (mons_shouts(m->type, false) == S_SILENT)
2526         {
2527             text << "Uh-oh, that monster noticed you! Fortunately, it "
2528                     "didn't make any noise, but many monsters do make "
2529                     "noise when they notice you. This will alert other "
2530                     "monsters in the area, who will come to check out what "
2531                     "the commotion was about.";
2532         }
2533         else
2534         {
2535             text << "Uh-oh, that monster noticed you! Plus, the "
2536                     "noise it made will alert other monsters in the "
2537                     "vicinity, who will come to check out what the commotion "
2538                     "was about.";
2539         }
2540         break;
2541     }
2542
2543     case HINT_MONSTER_LEFT_LOS:
2544     {
2545         const monster* m = monster_at(gc);
2546
2547         if (!m || !you.can_see(m))
2548             DELAY_EVENT;
2549
2550         text << m->name(DESC_THE, true) << " didn't vanish, but merely "
2551                 "moved onto a square which you can't currently see. It's still "
2552                 "nearby, unless something happens to it in the short amount of "
2553                 "time it's out of sight.";
2554         break;
2555     }
2556
2557     case HINT_SEEN_MONSTER:
2558     case HINT_SEEN_FIRST_OBJECT:
2559         // Handled in special functions.
2560         break;
2561
2562     case HINT_SEEN_ZERO_EXP_MON:
2563     {
2564         const monster_info* mi = env.map_knowledge(gc).monsterinfo();
2565
2566         if (!mi)
2567             DELAY_EVENT;
2568
2569         text << "That ";
2570 #ifdef USE_TILE
2571         // need to highlight monster
2572         tiles.place_cursor(CURSOR_TUTORIAL, gc);
2573         tiles.add_text_tag(TAG_TUTORIAL, *mi);
2574
2575         text << "is a ";
2576 #else
2577         text << glyph_to_tagstr(get_mons_glyph(*mi)) << " is a ";
2578 #endif
2579         text << mi->proper_name(DESC_PLAIN).c_str() << ". ";
2580
2581         text << "While <w>technically</w> a monster, it's more like "
2582                 "dungeon furniture, since it's harmless and doesn't move. "
2583                 "If it's in your way you can attack and kill it like other "
2584                 "monsters, but you won't get any experience for doing so. ";
2585         break;
2586     }
2587
2588     case HINT_ABYSS:
2589         text << "Uh-oh, you've wound up in the Abyss! The Abyss is a special "
2590                 "place where you cannot remember or map where you've been; it "
2591                 "is filled with nasty monsters, and you're probably going to "
2592                 "die.\n";
2593         text << "To increase your chances of survival until you can find the "
2594                 "exit"
2595 #ifndef USE_TILE
2596                 " (a flickering <w>"
2597              << stringize_glyph(get_feat_symbol(DNGN_EXIT_ABYSS))
2598              << "</w>)"
2599 #endif
2600                 ", keep moving, don't fight any of the monsters, and don't "
2601                 "bother picking up any items on the ground. If the monsters "
2602                 "are closing in, try to use items of hasting to get away.";
2603         break;
2604
2605     case HINT_SPELL_MISCAST:
2606     {
2607         // Don't give at the beginning of your spellcasting career.
2608         if (you.max_magic_points <= 2)
2609             DELAY_EVENT;
2610
2611         if (!crawl_state.game_is_hints())
2612         {
2613             text << "Miscasting a spell can have nasty consequences, "
2614                     "particularly for the more difficult spells. Your chance "
2615                     "of successfully casting a spell increases with your magic "
2616                     "skills, and can also be improved with the help of some "
2617                     "items. Use the <w>%</w> command "
2618 #ifdef USE_TILE
2619                     "or mouse over the spell tiles "
2620 #endif
2621                     "to check your current failure rates.";
2622             cmd.push_back(CMD_DISPLAY_SPELLS);
2623             break;
2624         }
2625         text << "You just miscast a spell. ";
2626
2627         const item_def *shield = you.slot_item(EQ_SHIELD, false);
2628         if (!player_effectively_in_light_armour() || shield)
2629         {
2630             text << "Wearing heavy body armour or using a shield, especially a "
2631                     "large one, can severely hamper your spellcasting "
2632                     "abilities. You can check the effect of this by comparing "
2633                     "the failure rates on the <w>%\?</w> screen with and "
2634                     "without the item being worn.\n\n";
2635             cmd.push_back(CMD_CAST_SPELL);
2636         }
2637
2638         text << "If the spellcasting success chance is high (which can be "
2639                 "checked by entering <w>%\?</w> or <w>%</w>) then a miscast "
2640                 "merely means the spell is not working, along with a harmless "
2641                 "side effect. "
2642                 "However, for spells with a high failure rate, there's a "
2643                 "chance of contaminating yourself with magical energy, plus a "
2644                 "chance of an additional harmful side effect. Normally this "
2645                 "isn't a problem, since magical contamination bleeds off over "
2646                 "time, but if you're repeatedly contaminated in a short amount "
2647                 "of time you'll mutate or suffer from other ill side effects."
2648                 "\n\n";
2649         cmd.push_back(CMD_CAST_SPELL);
2650         cmd.push_back(CMD_DISPLAY_SPELLS);
2651
2652         text << "Note that a miscast spell will still consume the full amount "
2653                 "of MP and nutrition that a successfully cast spell would.";
2654         break;
2655     }
2656     case HINT_SPELL_HUNGER:
2657         text << "The spell you just cast made you hungrier; you can see how "
2658                 "hungry spells make you by "
2659 #ifdef USE_TILE
2660                 "examining your spells in the spell display, or by "
2661 #endif
2662                 "entering <w>%\?!</w> or <w>%I</w>. "
2663                 "The amount of nutrition consumed increases with the level of "
2664                 "the spell and decreases depending on your intelligence stat "
2665                 "and your Spellcasting skill. If both of these are high "
2666                 "enough a spell might even not cost you any nutrition at all.";
2667         cmd.push_back(CMD_CAST_SPELL);
2668         cmd.push_back(CMD_DISPLAY_SPELLS);
2669         break;
2670
2671     case HINT_GLOWING:
2672         text << "You've accumulated so much magical contamination that you're "
2673                 "glowing! You usually acquire magical contamination from using "
2674                 "some powerful magics, like invisibility or haste, or from "
2675                 "miscasting spells. ";
2676
2677         if (get_contamination_level() < 2)
2678         {
2679             text << "As long as the status only shows in grey nothing will "
2680                     "actually happen as a result of it, but as you continue "
2681                     "suffusing yourself with magical contamination you'll "
2682                     "eventually start glowing for real, which ";
2683         }
2684         else
2685         {
2686             text << "This normally isn't a problem as contamination slowly "
2687                     "bleeds off on its own, but it seems that you've "
2688                     "accumulated so much contamination over a short amount of "
2689                     "time that it ";
2690         }
2691         text << "can have nasty effects, such as mutating you or dealing direct "
2692                 "damage. In addition, glowing is going to make you much more "
2693                 "noticeable.";
2694         break;
2695
2696     case HINT_YOU_RESIST:
2697         text << "There are many dangers in Crawl. Luckily, there are ways to "
2698                 "(at least partially) resist some of them, if you are "
2699                 "fortunate enough to find them. There are two basic variants "
2700                 "of resistances: the innate magic resistance that depends on "
2701                 "your species, grows with experience level, and protects "
2702                 "against hostile enchantments; and the specific resistances "
2703                 "against certain types of magic and also other effects, e.g. "
2704                 "fire or draining.\n"
2705                 "You can find items in the dungeon or gain mutations that will "
2706                 "increase (or lower) one or more of your resistances. To view "
2707                 "your current set of resistances, "
2708 #ifdef USE_TILE
2709                 "<w>right-click</w> on the player avatar.";
2710 #else
2711                 "press <w>%</w>.";
2712         cmd.push_back(CMD_RESISTS_SCREEN);
2713 #endif
2714         break;
2715
2716     case HINT_CAUGHT_IN_NET:
2717         text << "While you are held in a net, you cannot move around or engage "
2718                 "monsters in combat. Instead, any movement you take is counted "
2719                 "as an attempt to struggle free from the net. With a wielded "
2720                 "bladed weapon you will be able to cut the net faster";
2721
2722         if (Hints.hints_type == HINT_BERSERK_CHAR)
2723             text << ", especially if you're berserking while doing so";
2724
2725         text << ". Small species may also wriggle out of a net, only damaging "
2726                 "it a bit, so as to then <w>%</w>ire it at a monster.";
2727         cmd.push_back(CMD_FIRE);
2728
2729         if (Hints.hints_type == HINT_MAGIC_CHAR)
2730         {
2731             text << " Note that casting spells is still very much possible, "
2732                     "as is using wands, scrolls and potions.";
2733         }
2734         else
2735         {
2736             text << " Note that using wands, scrolls and potions is still "
2737                     "very much possible.";
2738         }
2739         break;
2740
2741     case HINT_YOU_SILENCE:
2742         redraw_screen();
2743         text << "While you are silenced, you cannot cast spells, read scrolls "
2744                 "or use divine invocations. The same is true for any monster "
2745                 "within the effect radius. The field of silence (recognizable "
2746                 "by "
2747 #ifdef USE_TILE
2748                 "the special-looking frame tiles"
2749 #else
2750                 "different-coloured floor squares"
2751 #endif
2752                 ") is always centered on you and will move along with you. "
2753                 "The radius will gradually shrink, eventually making you the "
2754                 "only one affected, before the effect fades entirely.";
2755         break;
2756
2757     case HINT_LOAD_SAVED_GAME:
2758     {
2759         text << "Welcome back! If it's been a while, you may want to refresh "
2760                 "your memory.\nYour <w>%</w>nventory, ";
2761         cmd.push_back(CMD_DISPLAY_INVENTORY);
2762
2763         vector<const char *> listed;
2764         if (you.spell_no > 0)
2765         {
2766             listed.push_back("your spells (<w>%?</w>)");
2767             cmd.push_back(CMD_CAST_SPELL);
2768         }
2769         if (!your_talents(false).empty())
2770         {
2771             listed.push_back("your <w>%</w>bilities");
2772             cmd.push_back(CMD_USE_ABILITY);
2773         }
2774         if (Hints.hints_type != HINT_MAGIC_CHAR || how_mutated())
2775         {
2776             listed.push_back("your set of mutations (<w>%</w>)");
2777             cmd.push_back(CMD_DISPLAY_MUTATIONS);
2778         }
2779         if (!you_worship(GOD_NO_GOD))
2780         {
2781             listed.push_back("your religious standing (<w>%</w>)");
2782             cmd.push_back(CMD_DISPLAY_RELIGION);
2783         }
2784
2785         listed.push_back("the message history (<w>%</w>)");
2786         listed.push_back("the character overview screen (<w>%</w>)");
2787         listed.push_back("the dungeon overview screen (<w>%</w>)");
2788         text << comma_separated_line(listed.begin(), listed.end())
2789              << " are good things to check.";
2790         cmd.push_back(CMD_REPLAY_MESSAGES);
2791         cmd.push_back(CMD_RESISTS_SCREEN);
2792         cmd.push_back(CMD_DISPLAY_OVERMAP);
2793         break;
2794     }
2795     case HINT_AUTOPICKUP_THROWN:
2796         text << "When stepping on items you've thrown, they will be "
2797                 "picked up automatically.";
2798         break;
2799     case HINT_GAINED_SPELLCASTING:
2800         text << "As your Spellcasting skill increases, you will be able to "
2801              << "memorise more spells, and will suffer less hunger and "
2802              << "somewhat fewer failures when you cast them.\n"
2803              << "Press <w>%</w> "
2804 #ifdef USE_TILE
2805              << "(or click on the <w>skill button</w> in the command panel) "
2806 #endif
2807              << "to have a look at your skills and manage their training.";
2808         cmd.push_back(CMD_DISPLAY_SKILLS);
2809         break;
2810 #if TAG_MAJOR_VERSION == 34
2811     case HINT_MEMORISE_FAILURE:
2812         text << "At low skills, spells may be difficult to learn or cast. "
2813                 "For now, just keep trying!";
2814         break;
2815 #endif
2816     case HINT_FUMBLING_SHALLOW_WATER:
2817         text << "Fighting in shallow water will sometimes cause you to slip "
2818                 "and fumble your attack. If possible, try to fight on "
2819                 "firm ground.";
2820         break;
2821     case HINT_CLOUD_WARNING:
2822         text << "Rather than step into this cloud and hurt yourself, you should "
2823                 "say <w>N</w>o and either wait for a few turns to see if it "
2824                 "vanishes (with <w>%</w>), or just step around it.";
2825         cmd.push_back(CMD_WAIT);
2826         break;
2827     case HINT_ANIMATE_CORPSE_SKELETON:
2828         text << "As long as a monster has a skeleton, Animate Skeleton also "
2829                 "works on unskeletalized corpses.";
2830         break;
2831     default:
2832         text << "You've found something new (but I don't know what)!";
2833     }
2834
2835     if (!text.str().empty())
2836     {
2837         string output = text.str();
2838         if (!cmd.empty())
2839             insert_commands(output, cmd);
2840         mprf(MSGCH_TUTORIAL, "%s", output.c_str());
2841
2842         stop_running();
2843     }
2844 }
2845
2846 formatted_string hints_abilities_info()
2847 {
2848     ostringstream text;
2849     text << "<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">";
2850     string broken = "This screen shows your character's set of talents. "
2851         "You can gain new abilities via certain items, through religion or by "
2852         "way of mutations. Activation of an ability usually comes at a cost, "
2853         "e.g. nutrition or Magic power. Press '<w>!</w>' or '<w>?</w>' to "
2854         "toggle between ability selection and description.";
2855     linebreak_string(broken, _get_hints_cols());
2856     text << broken;
2857
2858     text << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">";
2859
2860     return formatted_string::parse_string(text.str(), false);
2861 }
2862
2863 // Explains the basics of the skill screen. Don't bother the player with the
2864 // aptitude information.
2865 string hints_skills_info()
2866 {
2867     textcolour(channel_to_colour(MSGCH_TUTORIAL));
2868     ostringstream text;
2869     text << "<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">";
2870     string broken = "This screen shows the skill set of your character. "
2871         "The number next to the skill is your current level, the higher the "
2872         "better. The <brown>brown percent value</brown> shows how much "
2873         "experience is allocated to go towards that skill. "
2874         "You can toggle which skills to train by "
2875         "pressing their slot letters. A <darkgrey>grey</darkgrey> skill "
2876         "will not be trained and ease the training of others.";
2877     text << broken;
2878     text << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">";
2879
2880     return text.str();
2881 }
2882
2883 string hints_skill_training_info()
2884 {
2885     textcolour(channel_to_colour(MSGCH_TUTORIAL));
2886     ostringstream text;
2887     text << "<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">";
2888     string broken = "The training percentage (in <brown>brown</brown>) "
2889         "shows the relative amount of the experience gained which will be "
2890         "used to train each skill. It is automatically set depending on "
2891         "which skills you have used recently. Disabling a skill sets the "
2892         "training rate to 0.";
2893     text << broken;
2894     text << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">";
2895
2896     return text.str();
2897 }
2898
2899 string hints_skills_description_info()
2900 {
2901     textcolour(channel_to_colour(MSGCH_TUTORIAL));
2902     ostringstream text;
2903     text << "<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">";
2904     string broken = "This screen shows the skill set of your character. "
2905                     "Press the letter of a skill to read its description, or "
2906                     "press <w>?</w> again to return to the skill selection.";
2907
2908     linebreak_string(broken, _get_hints_cols());
2909     text << broken;
2910     text << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">";
2911
2912     return text.str();
2913 }
2914
2915 // A short explanation of Crawl's target mode and its most important commands.
2916 static string _hints_target_mode(bool spells = false)
2917 {
2918     string result;
2919     result = "then be taken to target mode with the nearest monster or "
2920              "previous target already targeted. You can also cycle through "
2921              "all hostile monsters in sight with <w>+</w> or <w>-</w>. "
2922              "Once you're aiming at the correct monster, simply hit "
2923              "<w>f</w>, <w>Enter</w> or <w>.</w> to shoot at it. "
2924              "If you miss, <w>";
2925
2926     command_type cmd;
2927     if (spells)
2928     {
2929         result += "%ap";
2930         cmd = CMD_CAST_SPELL;
2931     }
2932     else
2933     {
2934         result += "%f";
2935         cmd = CMD_FIRE;
2936     }
2937
2938     result += "</w> fires at the same target again.";
2939     insert_commands(result, cmd, 0);
2940
2941     return result;
2942 }
2943
2944 static string _hints_abilities(const item_def& item)
2945 {
2946     string str = "To do this, ";
2947
2948     vector<command_type> cmd;
2949     if (!item_is_equipped(item))
2950     {
2951         switch (item.base_type)
2952         {
2953         case OBJ_WEAPONS:
2954             str += "first <w>%</w>ield it";
2955             cmd.push_back(CMD_WIELD_WEAPON);
2956             break;
2957         case OBJ_ARMOUR:
2958             str += "first <w>%</w>ear it";
2959             cmd.push_back(CMD_WEAR_ARMOUR);
2960             break;
2961         case OBJ_JEWELLERY:
2962             str += "first <w>%</w>ut it on";
2963             cmd.push_back(CMD_WEAR_JEWELLERY);
2964             break;
2965         default:
2966             str += "<r>(BUG! this item shouldn't give an ability)</r>";
2967             break;
2968         }
2969         str += ", then ";
2970     }
2971     str += "enter the ability menu with <w>%</w>, and then "
2972            "choose the corresponding ability. Note that such an attempt of "
2973            "activation, especially by the untrained, is likely to fail.";
2974     cmd.push_back(CMD_USE_ABILITY);
2975
2976     insert_commands(str, cmd);
2977     return str;
2978 }
2979
2980 static string _hints_throw_stuff(const item_def &item)
2981 {
2982     string result;
2983
2984     result  = "To do this, type <w>%</w> to fire, then <w>";
2985     result += item.slot;
2986     result += "</w> for ";
2987     result += (item.quantity > 1 ? "these" : "this");
2988     result += " ";
2989     result += item_base_name(item);
2990     result += (item.quantity > 1? "s" : "");
2991     result += ". You'll ";
2992     result += _hints_target_mode();
2993
2994     insert_commands(result, CMD_FIRE, 0);
2995     return result;
2996 }
2997
2998 // num_old_talents describes the number of activatable abilities you had
2999 // before putting on this item.
3000 void check_item_hint(const item_def &item, unsigned int num_old_talents)
3001 {
3002     if (item.cursed())
3003         learned_something_new(HINT_YOU_CURSED);
3004     else if (Hints.hints_events[HINT_NEW_ABILITY_ITEM]
3005              && your_talents(false).size() > num_old_talents)
3006     {
3007         learned_something_new(HINT_NEW_ABILITY_ITEM);
3008     }
3009     else if (Hints.hints_events[HINT_ITEM_RESISTANCES]
3010              && gives_resistance(item))
3011     {
3012         learned_something_new(HINT_ITEM_RESISTANCES);
3013     }
3014 }
3015
3016 // Explains the most important commands necessary to use an item, and mentions
3017 // special effects, etc.
3018 // NOTE: For identified artefacts don't give all this information!
3019 //       (The screen is likely to overflow.) Artefacts need special information
3020 //       if they are evokable or grant resistances.
3021 //       In any case, check whether we still have enough space for the
3022 //       inscription prompt and answer.
3023 void hints_describe_item(const item_def &item)
3024 {
3025     ostringstream ostr;
3026     ostr << "<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">";
3027     vector<command_type> cmd;
3028
3029     switch (item.base_type)
3030     {
3031         case OBJ_WEAPONS:
3032         {
3033             if (is_artefact(item) && item_type_known(item))
3034             {
3035                 if (gives_ability(item)
3036                     && wherey() <= get_number_of_lines() - 5)
3037                 {
3038                     // You can activate it.
3039                     ostr << "When wielded, some weapons (such as this one) "
3040                             "offer certain abilities you can activate. ";
3041                     ostr << _hints_abilities(item);
3042                     break;
3043                 }
3044                 else if (gives_resistance(item)
3045                          && wherey() <= get_number_of_lines() - 3)
3046                 {
3047                     // It grants a resistance.
3048                     ostr << "\nThis weapon offers its wearer protection from "
3049                             "certain sources. For an overview of your "
3050                             "resistances (among other things) type <w>%</w>"
3051 #ifdef USE_TILE
3052                             " or click on your avatar with the <w>right mouse "
3053                             "button</w>"
3054 #endif
3055                             ".";
3056                     cmd.push_back(CMD_RESISTS_SCREEN);
3057                     break;
3058                 }
3059                 return;
3060             }
3061
3062             item_def *weap = you.slot_item(EQ_WEAPON, false);
3063             bool wielded = (weap && weap->slot == item.slot);
3064             bool long_text = false;
3065
3066             if (!wielded)
3067             {
3068                 ostr << "You can wield this weapon with <w>%</w>, or use "
3069                         "<w>%</w> to switch between the weapons in slot "
3070                         "a and b. (Use <w>%i</w> to adjust item slots.)";
3071                 cmd.push_back(CMD_WIELD_WEAPON);
3072                 cmd.push_back(CMD_WEAPON_SWAP);
3073                 cmd.push_back(CMD_ADJUST_INVENTORY);
3074
3075                 // Weapon skill used by this weapon and the best weapon skill.
3076                 skill_type curr_wpskill = item_attack_skill(item);
3077                 skill_type best_wpskill;
3078
3079                 // Maybe this is a launching weapon?
3080                 if (is_range_weapon(item))
3081                     best_wpskill = best_skill(SK_SLINGS, SK_THROWING);
3082                 else
3083                     best_wpskill = best_skill(SK_SHORT_BLADES, SK_STAVES);
3084
3085                 // Maybe unarmed is better.
3086                 if (you.skills[SK_UNARMED_COMBAT] > you.skills[best_wpskill])
3087                     best_wpskill = SK_UNARMED_COMBAT;
3088
3089                 if (you.skills[curr_wpskill] + 2 < you.skills[best_wpskill])
3090                 {
3091                     ostr << "\nHowever, you've been training in <w>"
3092                          << skill_name(best_wpskill)
3093                          << "</w> for a while, so maybe you should "
3094                             "continue training that rather than <w>"
3095                          << skill_name(curr_wpskill)
3096                          << "</w>. (Type <w>%</w> to see the skill "
3097                             "management screen for the actual numbers.)";
3098
3099                     cmd.push_back(CMD_DISPLAY_SKILLS);
3100                     long_text = true;
3101                 }
3102             }
3103             else // wielded weapon
3104             {
3105                 if (is_range_weapon(item))
3106                 {
3107                     ostr << "To attack a monster, ";
3108 #ifdef USE_TILE
3109                     ostr << "if you have appropriate ammo quivered you can "
3110                             "<w>left mouse click</w> on the monster while "
3111                             "prssing the <w>Shift key</w>. Alternatively, "
3112                             "you can <w>left mouse click</w> on the tile for "
3113                             "the ammo you wish to fire, and then <w>left "
3114                             "mouse click</w> on the monster.\n\n";
3115                     ostr << "To launch ammunition using the keyboard, ";
3116 #endif
3117                     ostr << "you only need to "
3118                             "<w>%</w>ire the appropriate type of ammunition. "
3119                             "You'll ";
3120                     ostr << _hints_target_mode();
3121                     cmd.push_back(CMD_FIRE);
3122                 }
3123                 else
3124                     ostr << "To attack a monster, you can simply walk into it.";
3125             }
3126
3127             if (!item_type_known(item)
3128                 && (is_artefact(item)
3129                     || get_equip_desc(item) != ISFLAG_NO_DESC))
3130             {
3131                 ostr << "\n\nWeapons and armour that have unusual descriptions "
3132                      << "like this are much more likely to be of higher "
3133                      << "enchantment or have special properties, good or bad.";
3134
3135                 Hints.hints_events[HINT_SEEN_RANDART] = false;
3136             }
3137             if (item_known_cursed(item) && !long_text)
3138             {
3139                 ostr << "\n\nOnce wielded, a cursed weapon can't be "
3140                         "unwielded until the curse has been lifted by "
3141                         "reading a scroll of remove curse or one of the "
3142                         "enchantment scrolls.";
3143
3144                 Hints.hints_events[HINT_YOU_CURSED] = false;
3145             }
3146             Hints.hints_events[HINT_SEEN_WEAPON] = false;
3147             break;
3148         }
3149         case OBJ_MISSILES:
3150             if (is_throwable(&you, item))
3151             {
3152                 ostr << item.name(DESC_YOUR)
3153                      << " can be <w>%</w>ired without the use of a launcher. ";
3154                 ostr << _hints_throw_stuff(item);
3155                 cmd.push_back(CMD_FIRE);
3156             }
3157             else if (is_launched(&you, you.weapon(), item))
3158             {
3159                 ostr << "As you're already wielding the appropriate launcher, "
3160                         "you can simply ";
3161 #ifdef USE_TILE
3162                 ostr << "<w>left mouse click</w> on the monster you want "
3163                         "to hit while pressing the <w>Shift key</w>. "
3164                         "Alternatively, you can <w>left mouse click</w> on "
3165                         "this tile of the ammo you want to fire, and then "
3166                         "<w>left mouse click</w> on the monster you want "
3167                         "to hit.\n\n"
3168
3169                         "To launch this ammo using the keyboard, you can "
3170                         "simply ";
3171 #endif
3172
3173                 ostr << "<w>%</w>ire "
3174                      << (item.quantity > 1 ? "these" : "this")
3175                      << " " << item.name(DESC_BASENAME)
3176                      << (item.quantity > 1? "s" : "")
3177                      << ". You'll ";
3178                 ostr << _hints_target_mode();
3179                 cmd.push_back(CMD_FIRE);
3180             }
3181             else
3182             {
3183                 ostr << "To shoot "
3184                      << (item.quantity > 1 ? "these" : "this")
3185                      << " " << item.name(DESC_BASENAME)
3186                      << (item.quantity > 1? "s" : "")
3187                      << ", first you need to <w>%</w>ield an appropriate "
3188                         "launcher.";
3189                 cmd.push_back(CMD_WIELD_WEAPON);
3190             }
3191             Hints.hints_events[HINT_SEEN_MISSILES] = false;
3192             break;
3193
3194         case OBJ_ARMOUR:
3195         {
3196             bool wearable = true;
3197             if (you.species == SP_CENTAUR && item.sub_type == ARM_BOOTS)
3198             {
3199                 ostr << "As a Centaur you cannot wear boots. "
3200                         "(Type <w>%</w> to see a list of your mutations and "
3201                         "innate abilities.)";
3202                 cmd.push_back(CMD_DISPLAY_MUTATIONS);
3203                 wearable = false;
3204             }
3205             else if (you.species == SP_MINOTAUR && is_hard_helmet(item))
3206             {
3207                 ostr << "As a Minotaur you cannot wear helmets. "
3208                         "(Type <w>%</w> to see a list of your mutations and "
3209                         "innate abilities.)";
3210                 cmd.push_back(CMD_DISPLAY_MUTATIONS);
3211                 wearable = false;
3212             }
3213             else if (item.sub_type == ARM_CENTAUR_BARDING)
3214             {
3215                 ostr << "Only centaurs can wear centaur barding.";
3216                 wearable = false;
3217             }
3218             else if (item.sub_type == ARM_NAGA_BARDING)
3219             {
3220                 ostr << "Only nagas can wear naga barding.";
3221                 wearable = false;
3222             }
3223             else
3224             {
3225                 ostr << "You can wear pieces of armour with <w>%</w> and take "
3226                         "them off again with <w>%</w>"
3227 #ifdef USE_TILE
3228                         ", or, alternatively, simply click on their tiles to "
3229                         "perform either action."
3230 #endif
3231                         ".";
3232                 cmd.push_back(CMD_WEAR_ARMOUR);
3233                 cmd.push_back(CMD_REMOVE_ARMOUR);
3234             }
3235
3236             if (Hints.hints_type == HINT_MAGIC_CHAR
3237                 && get_armour_slot(item) == EQ_BODY_ARMOUR
3238                 && !is_effectively_light_armour(&item))
3239             {
3240                 ostr << "\nNote that body armour with a high encumbrance "
3241                         "rating may hinder your ability to cast spells. Light "
3242                         "armour such as robes and leather armour will be "
3243                         "generally safe for any aspiring spellcaster.";
3244             }
3245             else if (Hints.hints_type == HINT_MAGIC_CHAR
3246                      && is_shield(item))
3247             {
3248                 ostr << "\nNote that shields will hinder your ability to "
3249                         "cast spells; the larger the shield, the bigger "
3250                         "the penalty.";
3251             }
3252             else if (Hints.hints_type == HINT_RANGER_CHAR
3253                      && is_shield(item))
3254             {
3255                 ostr << "\nNote that many ranged weapons are two handed and so "
3256                         "cannot be used with a shield.";
3257             }
3258
3259             if (!item_type_known(item)
3260                 && (is_artefact(item)
3261                     || get_equip_desc(item) != ISFLAG_NO_DESC))
3262             {
3263                 ostr << "\n\nWeapons and armour that have unusual descriptions "
3264                      << "like this are much more likely to be of higher "
3265                      << "enchantment or have special properties, good or bad.";
3266
3267                 Hints.hints_events[HINT_SEEN_RANDART] = false;
3268             }
3269             if (wearable)
3270             {
3271                 if (item_known_cursed(item))
3272                 {
3273                     ostr << "\nA cursed piece of armour, once worn, cannot be "
3274                             "removed again until the curse has been lifted by "
3275                             "reading a scroll of remove curse or enchant "
3276                             "armour.";
3277                 }
3278                 if (gives_resistance(item))
3279                 {
3280                     ostr << "\n\nThis armour offers its wearer protection from "
3281                             "certain sources. For an overview of your"
3282                             " resistances (among other things) type <w>%</w>"
3283 #ifdef USE_TILE