as well as a few features. A bit of a clean-up and a few additions.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@2001 c06c8d41-db1a-0410-9941-cceddc491573
ESWIM76FGJL4QFLSHU6AC4D74PT7OPLQ7ZCJYWLZS5UCBAJDXYHAC
RSIUBEQUGNU4LO6KH4PKVROWQS33DAKSY4XFVGN7T3CEKSXABCSAC
U3KGUJJQWQORJIIFH3ADVNIEEX5HOX6KEOXO7DJSL7L3Z6GG3PAQC
IXVKL2SINZN4DMJSZSYFVK5YLGSBGV4KCN7GKYQL7SZQDCM3S7YQC
JWOMWPRTOIUVAA3TLIGSB4HVFINNUDTKO2TKVAUCXE26G3KTKYOAC
TZ643KHSE5CUPXFSQ7VYVOCM5MTQ7F4SENEYQX2RNFHGHLQVS3RQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
X3RDT655FEYO6XEVPIUAPEPJZAFE55KZBH2AZOLK3NGHINMVIGFQC
2N5AKUYV6EUUT254C47WSHZKD7FGN65JKJ3RCKSDP74XJIHIFMDQC
77H4BWWPPGLM3PLZH4QTAJRXIZTSDVNCOKZE223I437FN2UJ34RQC
7NDXS36TE7QVXTXJWMYSVG5UHCCLPIO4VL6NXFGTDK3ZNKE3A2IAC
XRZPPYWPWUOM4SFNI6BHKH2UKJQNLKOV6Y7XIEPEZXE5QYRT26PAC
HYS3HXTXATFPN7GLN3WBGDCP22UT2D3JBVUS7SP5FE7L54TDCJRQC
TRNEOO3YKZSUGYTJZDDXZTJFQJXY6PWKBDJXAOLY3FZAD6WHTUTQC
6HQB2N6N75R2RGKJFWRUN7WAC2PNGWQFXTII5DTRLTHZ2BOTMTVAC
RCU52DRCPWJVQ6HME4QR6V6EVQWTBKZTPWDI47UGUDAUBPOO5YNAC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
IHIJSWVOONSTA2WCHKW3YKBLETUQECFVBVLMET5SGQZ4C6U3GCUQC
ODNAIEJW732NG7USKQKCIP4R4DAEYXXJQX6LY7TIN32NKE75454QC
CRX4RG35C3DH57NIXCW4CG4X7SBMSX7TAJR2BAFQ4LQKCLYUNANQC
JVSCP4FTW2G57C6YD5HZOZXTODGZH7TR75JQGFJBEPX3LCZH236QC
QRD2M37AMG76CPGCALZQ3N2PZ3OZ3MFETMVAEHBJJVVH2BCPVGUAC
WXSNNK2RXP3DQFAEQGQUZJHFWXJC7ZKG2WURZGL566UDM4YXFSWQC
"<lightgray>b<magenta> or <brown>K<magenta>. You can gain "
"information about it by pressing <w>x<magenta>, moving the cursor "
"on the monster and then pressing <w>v<magenta>. To attack it with "
"your wielded weapon, just move into it.";
"<lightgray>b<magenta> or <brown>K<magenta>. ";
if (get_mons_colour(&mon) != (&mon)->colour)
learned_something_new(TUT_MONSTER_BRAND);
else
{
text += "You can gain information about it by pressing <w>x<magenta> "
"and moving the cursor on the monster.";
}
text += "\nTo attack this monster with your wielded weapon, just move into "
"it.";
"bow. Do this as follows: <w>wbff<magenta> where <w>wb<magenta> "
"wields the bow, and <w>ff<magenta> fires appropriate ammunition "
"(your arrows) at the nearest hostile monster. If you'd like to "
"choose a new monster you can type <w>f<magenta> followed by "
"<w>+<magenta> or <w>-<magenta> instead to cycle through the "
"available targets. Then press <w>Enter<magenta>, <w>f<magenta> "
"or <w>.<magenta> to fire. If you miss, <w>ff<magenta> fires at "
"the previous target again.";
"bow. If you have a look at your bow with <w>v<magenta>, you'll "
"find an explanation of how to do this. First <w>w<magenta>ield "
"it, then follow the instructions.";
"Do this as follows: <w>Za.<magenta> where <w>Za<magenta> zaps "
"the first spell you know, ";
text += spell_title(get_spell_by_letter('a'));
text += ", and <w>.<magenta> shoots at the targetted monster (the closest "
"hostile monster). To target a different monster, use <w>+<magenta> "
"or <w>-<magenta> to cycle through the available targets. "
"<w>Enter<magenta>, <w>f<magenta> or <w>.<magenta> will fire. If you "
"miss, <w>Za<magenta> followed by any of the aforementioned keys "
"will fire at it again.";
"If you have a look at your spellbook with <w>v<magenta>, you'll "
"find an explanation of how to do this.";
"depicted by <w>^<magenta>. They can do physical damage (with "
"darts or needles, for example) or have other, more magical "
"effects, like teleportation.";
"depicted by " << colour_to_tag(colour) << "^<magenta>. They "
"can do physical damage (with darts or needles, for example) "
"or have other, more magical effects, like teleportation.";
text << "One of your skills just got raised. Type <w>m<magenta> to take "
"a look at your skills screen.";
text << "One of your skills just got raised. To view or manage your "
"skill set, type <w>m<magenta>.";
}
// a short explanation of Crawl's target mode
// and the most important commands
static std::string tut_target_mode(bool spells = false)
{
std::string result;
result = "You'll then find yourself in target mode with the nearest monster "
"or previous target already targetted. You can also cycle through "
"all hostile monsters in sight with <w>+<magenta> or <w>-<magenta>. "
"Once you're aiming at the correct monster, simply hit "
"<w>f<magenta>, <w>Enter<magenta> or <w>.<magenta> to shoot at it. "
"If you miss, <w> ";
if (spells)
result += "Zap";
else
result += "ff";
result += "<magenta> fires at the same target again.";
return (result);
}
static std::string tut_throw_stuff(item_def &item)
{
std::string result;
result = "To do this, type <w>t<magenta> to throw, then <w>";
result += item.slot;
result += "<magenta> for ";
result += (item.quantity > 1 ? "these" : "this");
result += " ";
result += item_base_name(item);
result += (item.quantity > 1? "s" : "");
result += ". ";
result += tut_target_mode();
return (result);
}
void tutorial_describe_item(item_def &item)
{
std::ostringstream ostr;
ostr << "<magenta>";
switch (item.base_type)
{
case OBJ_WEAPONS:
{
// for identified artefacts don't give all this information
// (The screen is likely to overflow.)
if (is_artefact(item) && item_type_known(item))
return;
item_def *weap = you.slot_item(EQ_WEAPON);
bool wielded = (*weap).slot == item.slot;
bool long_text = false;
if (!wielded)
{
ostr << "You can wield this weapon with <w>w<magenta>, or use "
"<w>'<magenta> to switch between the weapons in slot "
"a and b. (Use <w>=<magenta> to adjust item slots.)";
// weapon skill used by this weapon and the best weapon skill
int curr_wpskill, best_wpskill;
// maybe this is a launching weapon
if (is_range_weapon(item))
{
// then only compare with other launcher skills
curr_wpskill = range_skill(item);
best_wpskill = best_skill(SK_SLINGS, SK_DARTS, 99);
}
else
{
// compare with other melee weapons
curr_wpskill = weapon_skill(item);
best_wpskill = best_skill(SK_SHORT_BLADES, SK_STAVES, 99);
// maybe unarmed is better
if (you.skills[SK_UNARMED_COMBAT] > you.skills[best_wpskill])
best_wpskill = SK_UNARMED_COMBAT;
}
if (you.skills[curr_wpskill] + 2 < you.skills[best_wpskill])
{
ostr << "\nOn second look you've been training in <w>"
<< skill_name(best_wpskill)
<< "<magenta> for a while, so maybe you should "
"continue training that rather than <w>"
<< skill_name(curr_wpskill)
<< "<magenta>. (Type <w>m<magenta> to see the skill "
"management screen for the actual numbers.)";
long_text = true;
}
}
else // wielded weapon
{
if (is_range_weapon(item))
{
ostr << "To attack a monster, you only need to "
"<w>f<magenta>ire the appropriate type of ammunition. ";
ostr << tut_target_mode();
}
else
{
ostr << "To attack a monster, you can simply walk into it.";
}
}
if (is_throwable(item, you.body_size()) && !long_text)
{
ostr << "\n\nSome weapons (including this one), can also be used "
"for ranged combat. ";
ostr << tut_throw_stuff(item);
long_text = true;
}
if (!item_type_known(item) &&
(is_artefact(item) || get_equip_desc( item ) != ISFLAG_NO_DESC))
{
ostr << "\n\nWeapons and armour that have unusual descriptions "
<< "like this are much more likely to be of higher "
<< "enchantment or have special properties, good or bad. "
<< "The rarer the description, the greater the potential "
<< "value of an item.";
Options.tutorial_events[TUT_SEEN_RANDART] = 0;
}
if (item_known_cursed( item ) && !long_text)
{
ostr << "\n\nOnce wielded, a cursed weapon won't leave your hands "
"again until the curse has been lifted by reading a "
"scroll of remove curse.";
if (!wielded && is_throwable(item, you.body_size()))
ostr << " (Throwing it is safe, though.)";
Options.tutorial_events[TUT_YOU_CURSED] = 0;
}
Options.tutorial_events[TUT_SEEN_WEAPON] = 0;
break;
}
case OBJ_MISSILES:
if (item.sub_type == MI_JAVELIN || item.sub_type == MI_SLING_BULLET
|| item.sub_type == MI_DART || item.sub_type == MI_STONE)
{
ostr << item.name(DESC_CAP_YOUR)
<< " can be thrown without the use of a launcher. ";
ostr << tut_throw_stuff(item);
}
else if (is_launched(&you, you.weapon(), item))
{
ostr << "As you're already wielding the appropriate launcher, "
"you can simply <w>f<magenta>ire "
<< (item.quantity > 1 ? "these" : "this")
<< " " << item.name(DESC_BASENAME)
<< (item.quantity > 1? "s" : "")
<< ". ";
ostr << tut_target_mode();
}
else
{
ostr << "To shoot "
<< (item.quantity > 1 ? "these" : "this")
<< " " << item.name(DESC_BASENAME)
<< (item.quantity > 1? "s" : "")
<< ", first you need to <w>w<magenta>ield an appropriate "
"launcher.";
}
Options.tutorial_events[TUT_SEEN_MISSILES] = 0;
break;
case OBJ_ARMOUR:
ostr << "You can wear pieces of armour with <w>W<magenta> and take "
"them off again with <w>T<magenta>. ";
if (!item_type_known(item) &&
(is_artefact(item) || get_equip_desc( item ) != ISFLAG_NO_DESC))
{
ostr << "\n\nWeapons and armour that have unusual descriptions "
<< "like this are much more likely to be of higher "
<< "enchantment or have special properties, good or bad. "
<< "The rarer the description, the greater the potential "
<< "value of an item.";
Options.tutorial_events[TUT_SEEN_RANDART] = 0;
}
if (item_known_cursed( item ))
{
ostr << "\nA cursed piece of armour, once worn, cannot be removed "
"again until the curse has been lifted by reading a "
"scroll of remove curse.";
}
Options.tutorial_events[TUT_SEEN_ARMOUR] = 0;
break;
case OBJ_WANDS:
ostr << "The magic within can be unleashed by <w>z<magenta>apping it.";
Options.tutorial_events[TUT_SEEN_WAND] = 0;
break;
case OBJ_FOOD:
ostr << "Food can simply be <w>e<magenta>aten. ";
if (item.sub_type == FOOD_CHUNK)
{
ostr << "Note that most species refuse to eat raw meat unless "
"really hungry. ";
if (item.special < 100)
{
ostr << "Even fewer can safely digest rotten meat, and you're "
"probably not part of this group.";
}
}
Options.tutorial_events[TUT_SEEN_FOOD] = 0;
break;
case OBJ_SCROLLS:
ostr << "Type <w>r<magenta> to read this scroll. ";
Options.tutorial_events[TUT_SEEN_SCROLL] = 0;
break;
case OBJ_JEWELLERY:
ostr << "Jewellery can be <w>P<magenta>ut on or <w>R<magenta>emoved "
"again. ";
if (item_known_cursed( item ))
{
ostr << "\nA cursed piece of jewellery will cling to its "
"unfortunate wearer's neck or fingers until the curse is "
"finally lifted when he or she reads a scroll of remove "
"curse.";
}
Options.tutorial_events[TUT_SEEN_JEWELLERY] = 0;
break;
case OBJ_POTIONS:
ostr << "Use <w>q<magenta> to quaff this potion. ";
Options.tutorial_events[TUT_SEEN_POTION] = 0;
break;
case OBJ_BOOKS:
if (!item_ident(item, ISFLAG_KNOW_TYPE))
{
ostr << "It's a book, you can <w>r<magenta>ead it. ";
}
else
{
if (item.sub_type == BOOK_MANUAL)
{
ostr << "A manual can greatly help you in training a skill. "
"To use it, <w>r<magenta>ead it while your experience "
"pool (the number in brackets) is full. Note that "
"this will drain said pool, so only use this manual "
"if you think you need the skill in question.";
}
else
{
ostr << "A spellbook! You could <w>M<magenta>emorize some "
"spells and then cast them with <w>Z<magenta>. ";
if (you.religion == GOD_TROG)
{
ostr << "\nAs a worshipper of "
<< god_name(GOD_TROG)
<< ", though, you might instead wish to burn this "
"tome of hated magic, by using the corresponding "
"<w>a<magenta>bility. "
"Note that this only works on books that are "
"lying on the floor and not on your current "
"square. ";
}
else if (!you.skills[SK_SPELLCASTING])
{
ostr << "\nFor now, however, that will have to wait "
"until you've learned the basics of Spellcasting "
"by reading lots of scrolls.";
}
else
{
ostr << "Do this as follows: Type <w>Z<magenta>, then "
"choose the spell you want to cast (with "
"<w>?<magenta>), for example <w>a<magenta>, "
"which is the first spell you know, "
<< spell_title(get_spell_by_letter('a'))
<< ". ";
ostr << tut_target_mode(true);
}
}
}
Options.tutorial_events[TUT_SEEN_SPBOOK] = 0;
break;
case OBJ_CORPSES:
ostr << "Corpses lying on the floor can be <w>D<magenta>issected "
"with a sharp implement to produce chunks for food "
"(though they may not be healthy)";
if (god_likes_butchery(you.religion))
{
ostr << ", or as a sacrifice to "
<< god_name(you.religion)
<< " (while <w>p<magenta>raying)";
}
ostr << ". ";
if (item.special < 100)
ostr << "Rotten corpses won't be of any use to you, though, so "
"you might just as well <w>d<magenta>rop this.";
Options.tutorial_events[TUT_SEEN_CARRION] = 0;
break;
case OBJ_STAVES:
ostr << "Staffs have to be <w>w<magenta>ielded to be of use. "
"There are staves that help spellcasters, and others that "
"allow you to harness spells hidden within.";
if (!item_ident(item, ISFLAG_KNOW_TYPE))
{
ostr << "\n\nTo find out what this staff might do, you have to "
"wield it, then <w>E<magenta>voke it to see if anything "
"interesting happens. If nothing happens, it's probably "
"an ancient staff capable of enhancing certain spell "
"schools while suppressing others. ";
if (you.spell_no)
{
ostr << "You can find out which one, by casting spells while "
"wielding this staff. Eventually, the staff might "
"react to a spell of its school and identify itself.";
}
}
Options.tutorial_events[TUT_SEEN_STAFF] = 0;
break;
case OBJ_MISCELLANY:
ostr << "Miscellanous items sometimes harbour magical powers. Try "
"<w>w<magenta>ielding and <w>E<magenta>voking it.";
Options.tutorial_events[TUT_SEEN_MISC] = 0;
break;
default:
return;
}
std::string broken = ostr.str();
linebreak_string2(broken, get_tutorial_cols());
formatted_string::parse_block(broken, false).display();
bool tutorial_feat_interesting(int feat)
{
switch (feat)
{
case DNGN_CLOSED_DOOR:
case DNGN_OPEN_DOOR:
case DNGN_ORCISH_IDOL:
case DNGN_GRANITE_STATUE:
case DNGN_SILVER_STATUE:
case DNGN_ORANGE_CRYSTAL_STATUE:
case DNGN_TRAP_MAGICAL:
case DNGN_TRAP_MECHANICAL:
case DNGN_STONE_STAIRS_DOWN_I:
case DNGN_STONE_STAIRS_DOWN_II:
case DNGN_STONE_STAIRS_DOWN_III:
case DNGN_STONE_STAIRS_UP_I:
case DNGN_STONE_STAIRS_UP_II:
case DNGN_STONE_STAIRS_UP_III:
case DNGN_ROCK_STAIRS_DOWN:
case DNGN_ROCK_STAIRS_UP:
return true;
default:
return false;
}
}
void tutorial_describe_feature(int feat)
{
std::ostringstream ostr;
ostr << "<magenta>";
switch (feat)
{
case DNGN_CLOSED_DOOR:
ostr << "You can open it by walking into it.";
Options.tutorial_events[TUT_SEEN_DOOR];
break;
case DNGN_OPEN_DOOR:
ostr << "You can close an open door if you stand next to it while "
"the doorway's empty, and then press <w>c<magenta> followed "
"by the direction of the door.";
Options.tutorial_events[TUT_SEEN_DOOR];
break;
case DNGN_ORCISH_IDOL:
case DNGN_GRANITE_STATUE:
case DNGN_SILVER_STATUE:
case DNGN_ORANGE_CRYSTAL_STATUE:
ostr << "Beware! Sometimes such a statue actually has a mind of its own.";
break;
case DNGN_TRAP_MAGICAL:
case DNGN_TRAP_MECHANICAL:
ostr << "These nasty constructions can do physical damage (with "
"darts or needles, for example) or have other, more magical "
"effects. ";
if (feat == DNGN_TRAP_MECHANICAL)
{
ostr << "You can attempt to deactivate the mechanical type by "
"standing next to it and then pressing <w>Shift<magenta> "
"and the direction of the trap. Note that this usually "
"causes the trap to go off, so it can be quite a "
"dangerous task.";
}
Options.tutorial_events[TUT_SEEN_TRAP];
break;
case DNGN_STONE_STAIRS_DOWN_I:
case DNGN_STONE_STAIRS_DOWN_II:
case DNGN_STONE_STAIRS_DOWN_III:
ostr << "You can enter the next (deeper) level by following them "
"down (<w>><magenta>). To get back to this level again, "
"press <w><<<magenta> while standing on the upstairs.";
Options.tutorial_events[TUT_SEEN_STAIRS];
break;
case DNGN_STONE_STAIRS_UP_I:
case DNGN_STONE_STAIRS_UP_II:
case DNGN_STONE_STAIRS_UP_III:
if (you.your_level < 1)
{
ostr << "These stairs lead out of the dungeon. Following them "
"will end the game. The only way to win is to transport "
"the fabled orb of Zot outside.";
}
else
{
ostr << "You can enter the previous (lower) level by following "
"these down (<w><<<magenta>). To get back to this level "
"again, press <w>><magenta> while standing on the "
"downstairs.";
}
Options.tutorial_events[TUT_SEEN_STAIRS];
break;
case DNGN_ROCK_STAIRS_DOWN:
case DNGN_ROCK_STAIRS_UP:
ostr << "Escape hatches can be used to quickly leave a level with "
"<w><<<magenta> and <w>><magenta>, respectively. Note that "
"you will usually be unable to return right away.";
Options.tutorial_events[TUT_SEEN_ESCAPE_HATCH];
break;
default:
return;
}
std::string broken = ostr.str();
linebreak_string2(broken, get_tutorial_cols());
formatted_string::parse_block(broken, false).display();
}
TUT_YOU_CURSED,
TUT_YOU_HUNGRY,
TUT_YOU_STARVING,
TUT_MULTI_PICKUP, // 30
TUT_HEAVY_LOAD,
TUT_ROTTEN_FOOD,
TUT_NEED_HEALING,
TUT_YOU_CURSED,
TUT_YOU_HUNGRY,
TUT_YOU_STARVING, // 35
TUT_YOU_MUTATED,
TUT_POSTBERSERK,
// warning
TUT_RUN_AWAY, // 38
TUT_RETREAT_CASTER,
TUT_WIELD_WEAPON,
TUT_NEED_HEALING,
mpr(feature_desc.c_str());
if (Options.tutorial_left && tutorial_feat_interesting(grd[mx][my]))
{
feature_desc += " (Press <w>v<lightgray> for more information.)";
print_formatted_paragraph(feature_desc, 80);
}
else
mpr(feature_desc.c_str());