From 054c713cec71c3010b1ac3eb0848f2b601db982f Mon Sep 17 00:00:00 2001 From: j-p-e-g Date: Wed, 15 Aug 2007 21:32:33 +0000 Subject: More stuff for the tutorial: - Enhanced handling of spellbooks and artefacts. - Feature descriptions now cover altars. - Added monster descriptions (out of depth and brands). Also: - space-only inscription counts as no inscription - ring of teleportation autoID's if no teleport randart In my last commit I forgot to mention that the descriptions were suggested by Richard Gould, and I'd like to give credit where credit is due. :) git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@2005 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/source/describe.cc | 12 +- crawl-ref/source/direct.cc | 5 + crawl-ref/source/item_use.cc | 19 ++- crawl-ref/source/itemprop.cc | 76 +++++++++++ crawl-ref/source/itemprop.h | 1 + crawl-ref/source/output.cc | 13 +- crawl-ref/source/spl-book.cc | 37 ++++++ crawl-ref/source/spl-book.h | 1 + crawl-ref/source/tutorial.cc | 301 ++++++++++++++++++++++++++++++++++++------- crawl-ref/source/tutorial.h | 10 +- 10 files changed, 422 insertions(+), 53 deletions(-) (limited to 'crawl-ref/source') diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc index 9adc69245b..61d04151fc 100644 --- a/crawl-ref/source/describe.cc +++ b/crawl-ref/source/describe.cc @@ -3388,10 +3388,10 @@ void describe_item( item_def &item, bool allow_inscribe ) if (Options.tutorial_left) { - gotoxy(1, wherey() + 2); tutorial_describe_item(item); } - if (allow_inscribe) + // Don't ask if there aren't enough rows left + if (allow_inscribe && wherey() <= get_number_of_lines() - 3) { gotoxy(1, wherey() + 2); formatted_string::parse_string("Do you wish to inscribe this item? ").display(); @@ -3606,7 +3606,7 @@ void describe_monsters(monsters& mons) case MONS_GUARDIAN_NAGA: description << getLongDescription("naga") << "$These nagas are often used as guardians " - "by powerful creatures.$"; + "by powerful creatures.$"; break; case MONS_GREATER_NAGA: description << getLongDescription("naga") @@ -3731,6 +3731,12 @@ void describe_monsters(monsters& mons) clrscr(); print_description(description.str()); + if (Options.tutorial_left) + { + gotoxy(1, wherey() + 2); + tutorial_describe_monster(static_cast(&mons)); + } + if (getch() == 0) getch(); } // end describe_monsters diff --git a/crawl-ref/source/direct.cc b/crawl-ref/source/direct.cc index 36ab799592..9521b7f6ea 100644 --- a/crawl-ref/source/direct.cc +++ b/crawl-ref/source/direct.cc @@ -1708,6 +1708,11 @@ static void describe_cell(int mx, int my) #if DEBUG_DIAGNOSTICS stethoscope(i); #endif + if (Options.tutorial_left && tutorial_monster_interesting(&menv[i])) + { + std::string msg = "(Press v for more information.)"; + print_formatted_paragraph(msg, 80); + } } #if (!DEBUG_DIAGNOSTICS) diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc index eeab91d2cc..73a9300e2d 100644 --- a/crawl-ref/source/item_use.cc +++ b/crawl-ref/source/item_use.cc @@ -2232,7 +2232,6 @@ void jewellery_wear_effects(item_def &item) case RING_SUSTENANCE: case RING_SLAYING: case RING_SEE_INVISIBLE: - case RING_TELEPORTATION: case RING_WIZARDRY: case RING_REGENERATION: case RING_TELEPORT_CONTROL: @@ -2286,6 +2285,15 @@ void jewellery_wear_effects(item_def &item) ident = ID_KNOWN_TYPE; break; + case RING_TELEPORTATION: + if (!scan_randarts( RAP_CAN_TELEPORT )) + { + mpr("You feel slightly jumpy."); + ident = ID_KNOWN_TYPE; + break; + } + break; + case AMU_RAGE: mpr("You feel a brief urge to hack something to bits."); ident = ID_KNOWN_TYPE; @@ -2899,6 +2907,15 @@ void inscribe_item() mpr( "Inscribe with what? ", MSGCH_PROMPT ); if (!cancelable_get_line(buf, sizeof buf)) { + // strip spaces from the end + for (int i = strlen(buf) - 1; i >= 0; i--) + { + if (isspace( buf[i] )) + buf[i] = 0; + else + break; + } + you.inv[item_slot].inscription = std::string(buf); you.wield_change = true; } diff --git a/crawl-ref/source/itemprop.cc b/crawl-ref/source/itemprop.cc index 65f87c3a50..46dea61357 100644 --- a/crawl-ref/source/itemprop.cc +++ b/crawl-ref/source/itemprop.cc @@ -1918,6 +1918,82 @@ int property( const item_def &item, int prop_type ) return (0); } +bool gives_ability( const item_def &item ) +{ + if (!item_type_known(item)) + return false; + + switch (item.base_type) + { + case OBJ_WEAPONS: + { + // unwielded weapon + item_def *weap = you.slot_item(EQ_WEAPON); + if (!weap || (*weap).slot != item.slot) + return false; + break; + } + case OBJ_JEWELLERY: + { + if (item.sub_type < NUM_RINGS) + { + // unworn ring + item_def *lring = you.slot_item(EQ_LEFT_RING); + item_def *rring = you.slot_item(EQ_RIGHT_RING); + if ((!lring || (*lring).slot != item.slot) + && (!rring || (*rring).slot != item.slot)) + { + return false; + } + + if (item.sub_type == RING_TELEPORTATION + || item.sub_type == RING_LEVITATION + || item.sub_type == RING_INVISIBILITY) + { + return true; + } + } + else + { + // unworn amulet + item_def *amul = you.slot_item(EQ_AMULET); + if (!amul || (*amul).slot != item.slot) + return false; + + if (item.sub_type == AMU_RAGE) + return true; + } + break; + } + case OBJ_ARMOUR: + { + const equipment_type eq = get_armour_slot(item); + if (eq == EQ_NONE) + return false; + + // unworn armour + item_def *arm = you.slot_item(eq); + if (!arm || (*arm).slot != item.slot) + return false; + break; + } + default: + return false; + } + + if (!is_random_artefact(item)) + return false; + + // check for evokable randart properties + for (int rap = RAP_INVISIBLE; rap <= RAP_MAPPING; rap++) + { + if (randart_wpn_property( item, rap )) + return true; + } + + return false; +} + int item_mass( const item_def &item ) { int unit_mass = 0; diff --git a/crawl-ref/source/itemprop.h b/crawl-ref/source/itemprop.h index 49e64b20c0..eb12110809 100644 --- a/crawl-ref/source/itemprop.h +++ b/crawl-ref/source/itemprop.h @@ -146,6 +146,7 @@ bool can_cut_meat( const item_def &item ); // generic item property functions: bool is_tool( const item_def &item ); int property( const item_def &item, int prop_type ); +bool gives_ability( const item_def &item ); int item_mass( const item_def &item ); size_type item_size( const item_def &item ); diff --git a/crawl-ref/source/output.cc b/crawl-ref/source/output.cc index db0d566a9d..decafc3ccd 100644 --- a/crawl-ref/source/output.cc +++ b/crawl-ref/source/output.cc @@ -1274,8 +1274,17 @@ void print_overview_screen() { if (e_order[i] == EQ_BODY_ARMOUR || e_order[i] == EQ_HELMET) { - snprintf(buf, sizeof buf, - "%-7s: (ill-fitting)", slot); + if (!you_tran_can_wear(e_order[i])) + { + snprintf(buf, sizeof buf, "%-7s: " + "(currently unavailable)", + slot); + } + else + { snprintf(buf, sizeof buf, + "%-7s: (ill-fitting)", + slot); + } } else { diff --git a/crawl-ref/source/spl-book.cc b/crawl-ref/source/spl-book.cc index 0d4309d7cf..f4734d2f79 100644 --- a/crawl-ref/source/spl-book.cc +++ b/crawl-ref/source/spl-book.cc @@ -1131,6 +1131,43 @@ bool undead_cannot_memorise(spell_type spell, char being) return false; } // end undead_cannot_memorise() +bool player_can_memorize(item_def &book) +{ + if (book.base_type != OBJ_BOOKS || book.sub_type == BOOK_MANUAL) + return false; + + if (!player_spell_levels()) + return false; + + for (int j = 0; j < SPELLBOOK_SIZE; j++) + { + const spell_type stype = which_spell_in_book(book.book_number(), j); + + if (stype == SPELL_NO_SPELL) + continue; + + // easiest spell already too difficult + if (spell_difficulty(stype) > you.experience_level + || player_spell_levels() < spell_levels_required(stype)) + { + return false; + } + + bool knowsSpell = false; + for (int i = 0; i < 25 && !knowsSpell; i++) + { + knowsSpell = (you.spells[i] == stype); + } + + // don't already know this spell + if (!knowsSpell) + { + return true; + } + } + return false; +} + bool learn_spell(void) { int chance = 0; diff --git a/crawl-ref/source/spl-book.h b/crawl-ref/source/spl-book.h index edcb8a89f9..54eb3fa0b8 100644 --- a/crawl-ref/source/spl-book.h +++ b/crawl-ref/source/spl-book.h @@ -40,6 +40,7 @@ int read_book( item_def &item, read_book_action_type action ); /* *********************************************************************** * called from: acr * *********************************************************************** */ +bool player_can_memorize(item_def &book); bool learn_spell(void); spell_type which_spell_in_book(int sbook_type, int spl); diff --git a/crawl-ref/source/tutorial.cc b/crawl-ref/source/tutorial.cc index fd5642bcb0..a9df4e5746 100644 --- a/crawl-ref/source/tutorial.cc +++ b/crawl-ref/source/tutorial.cc @@ -15,12 +15,16 @@ #include "menu.h" #include "message.h" #include "misc.h" +#include "mon-pick.h" +#include "mon-util.h" +#include "monstuff.h" #include "newgame.h" #include "output.h" #include "player.h" #include "randart.h" #include "religion.h" #include "skills2.h" +#include "spl-book.h" #include "spl-util.h" #include "stuff.h" #include "view.h" @@ -821,40 +825,39 @@ static std::string colour_to_tag(int col, bool closed = false) return tag; } -void tutorial_first_monster(const monsters& mon) +void tutorial_first_monster(const monsters &mon) { if (!Options.tutorial_events[TUT_SEEN_MONSTER]) return; + // crude hack: + // if the first monster is sleeping wake it + // (highlighting is an unnecessary complication) + if (get_mons_colour(&mon) != (&mon)->colour) + { + noisy(1, mon.x, mon.y); + viewwindow(true, false); + } + unsigned ch; unsigned short col; get_mons_glyph(&mon, &ch, &col); - if (ch == ' ') // happens if monster standing on dropped corpse or item - return; - std::string text = "That "; text += colour_to_tag(col); text += ch; text += " is a monster, usually depicted by a letter. Some typical " "early monsters look like r, g, " - "b or K. "; - - if (get_mons_colour(&mon) != (&mon)->colour) - learned_something_new(TUT_MONSTER_BRAND); - else - { - text += "You can gain information about it by pressing x " - "and moving the cursor on the monster."; - } - text += "\nTo attack this monster with your wielded weapon, just move into " + "b or K. You can gain information " + "about it by pressing x and moving the cursor on the " + "monster." + "\nTo attack this monster with your wielded weapon, just move into " "it."; print_formatted_paragraph(text, get_tutorial_cols(), MSGCH_TUTORIAL); if (Options.tutorial_type == TUT_RANGER_CHAR) { - more(); text = "However, as a hunter you will want to deal with it using your " "bow. If you have a look at your bow with v, you'll " "find an explanation of how to do this. First wield " @@ -863,7 +866,6 @@ void tutorial_first_monster(const monsters& mon) } else if (Options.tutorial_type == TUT_MAGIC_CHAR) { - more(); text = "However, as a conjurer you will want to deal with it using magic. " "If you have a look at your spellbook with v, you'll " "find an explanation of how to do this."; @@ -875,7 +877,7 @@ void tutorial_first_monster(const monsters& mon) Options.tut_just_triggered = 1; } -void tutorial_first_item(const item_def& item) +void tutorial_first_item(const item_def &item) { if (!Options.tutorial_events[TUT_SEEN_FIRST_OBJECT] || Options.tut_just_triggered) @@ -887,6 +889,9 @@ void tutorial_first_item(const item_def& item) unsigned short col; get_item_glyph(&item, &ch, &col); + if (ch == ' ') // happens if monster standing on dropped corpse or item + return; + std::string text = "That "; text += colour_to_tag(col); text += ch; @@ -1077,6 +1082,9 @@ void learned_something_new(tutorial_event_type seen_what, int x, int y) object = env.show[ex][ey]; colour = env.show_col[ex][ey]; get_item_symbol( object, &ch, &colour ); + if (ch == ' ' || colour == BLACK) + colour = LIGHTCYAN; + text << "Oops... you just triggered a trap. An unwary adventurer will " "occasionally stumble into one of these nasty constructions " "depicted by " << colour_to_tag(colour) << "^. They " @@ -1391,12 +1399,12 @@ formatted_string tut_abilities_info() 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 + or -. " + result = "then be taken to target mode with the nearest monster or previous " + "target already targetted. You can also cycle through all hostile " + "monsters in sight with + or -. " "Once you're aiming at the correct monster, simply hit " "f, Enter or . to shoot at it. " - "If you miss, "; + "If you miss, "; if (spells) result += "Zap"; @@ -1408,6 +1416,13 @@ static std::string tut_target_mode(bool spells = false) return (result); } +static std::string tut_abilities() +{ + return ("To do this enter the ability menu with a, and then " + "choose the corresponding ability. Note that such an attempt of " + "activation, especially by the untrained, is likely to fail."); +} + static std::string tut_throw_stuff(item_def &item) { std::string result; @@ -1419,7 +1434,7 @@ static std::string tut_throw_stuff(item_def &item) result += " "; result += item_base_name(item); result += (item.quantity > 1? "s" : ""); - result += ". "; + result += ". You'll "; result += tut_target_mode(); return (result); @@ -1436,10 +1451,20 @@ void tutorial_describe_item(item_def &item) // for identified artefacts don't give all this information // (The screen is likely to overflow.) if (is_artefact(item) && item_type_known(item)) + { + // exception: you can activate it + if (gives_ability(item)) + { + ostr << "When wielded, some weapons (such as this one) " + "offer abilities that can be evoked. "; + ostr << tut_abilities(); + break; + } return; + } item_def *weap = you.slot_item(EQ_WEAPON); - bool wielded = (*weap).slot == item.slot; + bool wielded = (weap && (*weap).slot == item.slot); bool long_text = false; if (!wielded) @@ -1485,7 +1510,8 @@ void tutorial_describe_item(item_def &item) if (is_range_weapon(item)) { ostr << "To attack a monster, you only need to " - "fire the appropriate type of ammunition. "; + "fire the appropriate type of ammunition. " + "You'll "; ostr << tut_target_mode(); } else @@ -1539,7 +1565,7 @@ void tutorial_describe_item(item_def &item) << (item.quantity > 1 ? "these" : "this") << " " << item.name(DESC_BASENAME) << (item.quantity > 1? "s" : "") - << ". "; + << ". You'll "; ostr << tut_target_mode(); } else @@ -1555,8 +1581,25 @@ void tutorial_describe_item(item_def &item) break; case OBJ_ARMOUR: - ostr << "You can wear pieces of armour with W and take " - "them off again with T. "; + { + bool wearable = true; + if (you.species == SP_CENTAUR && item.sub_type == ARM_BOOTS) + { + ostr << "As a Centaur you cannot wear boots."; + wearable = false; + } + else if (you.species == SP_MINOTAUR && item.sub_type == ARM_HELMET + && get_helmet_type(item) != THELM_CAP + && get_helmet_type(item) != THELM_WIZARD_HAT) + { + ostr << "As a Minotaur you cannot wear helmets."; + wearable = false; + } + else + { + ostr << "You can wear pieces of armour with W and take " + "them off again with T."; + } if (!item_type_known(item) && (is_artefact(item) || get_equip_desc( item ) != ISFLAG_NO_DESC)) @@ -1569,15 +1612,21 @@ void tutorial_describe_item(item_def &item) Options.tutorial_events[TUT_SEEN_RANDART] = 0; } - if (item_known_cursed( item )) + if (item_known_cursed( item ) && wearable) { 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."; } + if (is_artefact(item) && gives_ability(item)) + { + ostr << "\nWhen worn, some types of armour (such as this one) " + "offer abilities that can be evoked. "; + ostr << tut_abilities(); + } Options.tutorial_events[TUT_SEEN_ARMOUR] = 0; break; - + } case OBJ_WANDS: ostr << "The magic within can be unleashed by zapping it."; Options.tutorial_events[TUT_SEEN_WAND] = 0; @@ -1604,6 +1653,7 @@ void tutorial_describe_item(item_def &item) break; case OBJ_JEWELLERY: + { ostr << "Jewellery can be Put on or Removed " "again. "; @@ -1614,9 +1664,15 @@ void tutorial_describe_item(item_def &item) "finally lifted when he or she reads a scroll of remove " "curse."; } + if (gives_ability(item)) + { + ostr << "\nWhen worn, some types of jewellery (such as this one) " + "offer abilities that can be evoked. "; + ostr << tut_abilities(); + } Options.tutorial_events[TUT_SEEN_JEWELLERY] = 0; break; - + } case OBJ_POTIONS: ostr << "Use q to quaff this potion. "; Options.tutorial_events[TUT_SEEN_POTION] = 0; @@ -1637,12 +1693,12 @@ void tutorial_describe_item(item_def &item) "this will drain said pool, so only use this manual " "if you think you need the skill in question."; } - else + else // it's a spellbook { - ostr << "A spellbook! You could Memorize some " - "spells and then cast them with Z. "; if (you.religion == GOD_TROG) { + ostr << "A spellbook! You could Memorize some " + "spells and then cast them with Z. "; ostr << "\nAs a worshipper of " << god_name(GOD_TROG) << ", though, you might instead wish to burn this " @@ -1654,19 +1710,34 @@ void tutorial_describe_item(item_def &item) } else if (!you.skills[SK_SPELLCASTING]) { + ostr << "A spellbook! You could Memorize some " + "spells and then cast them with Z. "; ostr << "\nFor now, however, that will have to wait " "until you've learned the basics of Spellcasting " "by reading lots of scrolls."; } - else + else // actually can cast spells { - ostr << "Do this as follows: Type Z, then " - "choose the spell you want to cast (with " - "?), for example a, " - "which is the first spell you know, " - << spell_title(get_spell_by_letter('a')) - << ". "; - ostr << tut_target_mode(true); + if (player_can_memorize(item)) + { + ostr << "Such a highlighted spell " + "can be Memorized right away. "; + } + else + { + ostr << "You cannot memorize any " + << (you.spell_no ? "more " : "") + << "spells right now. This will change as you " + "grow in levels and Spellcasting proficiency. "; + } + + if (you.spell_no) + { + ostr << "\n\nTo do magic, type Z and " + "choose a spell (check with ?). " + "For attack spells you'll "; + ostr << tut_target_mode(true); + } } } } @@ -1725,11 +1796,17 @@ void tutorial_describe_item(item_def &item) std::string broken = ostr.str(); linebreak_string2(broken, get_tutorial_cols()); + gotoxy(1, wherey() + 2); formatted_string::parse_block(broken, false).display(); -} +} // tutorial_describe_item -bool tutorial_feat_interesting(int feat) +bool tutorial_feat_interesting(dungeon_feature_type feat) { + if (feat >= DNGN_ALTAR_ZIN && feat <= DNGN_ALTAR_BEOGH) + return true; + if (feat >= DNGN_ENTER_ORCISH_MINES && feat <= DNGN_ENTER_SHOALS) + return true; + switch (feat) { case DNGN_CLOSED_DOOR: @@ -1754,7 +1831,7 @@ bool tutorial_feat_interesting(int feat) } } -void tutorial_describe_feature(int feat) +void tutorial_describe_feature(dungeon_feature_type feat) { std::ostringstream ostr; ostr << ""; @@ -1826,6 +1903,86 @@ void tutorial_describe_feature(int feat) "you will usually be unable to return right away."; Options.tutorial_events[TUT_SEEN_ESCAPE_HATCH]; break; + + case DNGN_ALTAR_ZIN: + case DNGN_ALTAR_SHINING_ONE: + case DNGN_ALTAR_KIKUBAAQUDGHA: + case DNGN_ALTAR_YREDELEMNUL: + case DNGN_ALTAR_XOM: + case DNGN_ALTAR_VEHUMET: + case DNGN_ALTAR_OKAWARU: + case DNGN_ALTAR_MAKHLEB: + case DNGN_ALTAR_SIF_MUNA: + case DNGN_ALTAR_TROG: + case DNGN_ALTAR_NEMELEX_XOBEH: + case DNGN_ALTAR_ELYVILON: + case DNGN_ALTAR_LUGONU: + case DNGN_ALTAR_BEOGH: + { + god_type altar_god = grid_altar_god(feat); + + if (you.religion == GOD_NO_GOD) + { + ostr << "This is your chance to join a religion! In general, the " + "gods will help their followers, bestowing powers of all " + "sorts upon them, but many of them demand a life of " + "dedication, constant tributes or entertainment in return. " + "\nYou can get information about " + << god_name(altar_god) + << " by pressing p while standing on " + "the altar. Before taking up the responding faith you'll " + "be asked for confirmation."; + } + else + { + if (you.religion == altar_god) + { + ostr << "If " + << god_name(you.religion) + << " likes to have items or corpses sacrificed on altars, " + "here you can do this by dropping them, " + "then praying. As a follower, pressing " + "^ allows you to check " + << god_name(you.religion) + << "'s likes and dislikes at any time."; + } + else + { + ostr << god_name(you.religion) + << " probably won't like it if you switch allegiance, " + "but having a look won't hurt: to get information on "; + ostr << god_name(altar_god); + ostr << ", press p while standing on the " + "altar. Before taking up the responding faith (and " + "abandoning your current one!) you'll be asked for " + "confirmation." + "\nTo see your current standing with " + << god_name(you.religion) + << " press ^."; + } + } + Options.tutorial_events[TUT_SEEN_ALTAR]; + break; + } + case DNGN_ENTER_ORCISH_MINES: + case DNGN_ENTER_HIVE: + case DNGN_ENTER_LAIR: + case DNGN_ENTER_SLIME_PITS: + case DNGN_ENTER_VAULTS: + case DNGN_ENTER_CRYPT: + case DNGN_ENTER_HALL_OF_BLADES: + case DNGN_ENTER_ZOT: + case DNGN_ENTER_TEMPLE: + case DNGN_ENTER_SNAKE_PIT: + case DNGN_ENTER_ELVEN_HALLS: + case DNGN_ENTER_TOMB: + case DNGN_ENTER_SWAMP: + case DNGN_ENTER_SHOALS: + ostr << "An entryway into one of the many dungeon branches in Crawl. "; + if (feat != DNGN_ENTER_TEMPLE) + ostr << "Beware, sometimes these can be deadly!"; + break; + default: return; } @@ -1833,4 +1990,60 @@ void tutorial_describe_feature(int feat) std::string broken = ostr.str(); linebreak_string2(broken, get_tutorial_cols()); formatted_string::parse_block(broken, false).display(); +} // tutorial_describe_feature + +bool tutorial_monster_interesting(const monsters *mons) +{ + // highlighted in some way + if (get_mons_colour(mons) != mons->colour) + return true; + + // monster is (seriously) out of depth + if (you.level_type == LEVEL_DUNGEON && + mons_level(mons->type) >= you.your_level + Options.ood_interesting) + { + return true; + } + return false; } + +void tutorial_describe_monster(const monsters *mons) +{ + std::ostringstream ostr; + ostr << ""; + + int level_diff + = mons_level(mons->type) - (you.your_level + Options.ood_interesting); + + if (you.level_type == LEVEL_DUNGEON && level_diff >= 0) + { + ostr << "This kind of monster is usually only encountered " + << (level_diff > 5 ? "much " : "") + << "deeper in the dungeon, so it's probably " + << (level_diff > 5 ? "extremely" : "very") + << " dangerous!\n\n"; + } + + if (mons->has_ench(ENCH_BERSERK)) + ostr << "A berserking monster is bloodthirsty and fighting madly.\n"; + + if (mons_friendly(mons)) + { + ostr << "Friendly monsters will follow you around and attempt to aid " + "you in battle."; + } + else if (Options.stab_brand != CHATTR_NORMAL + && mons_looks_stabbable(mons)) + { + ostr << "Apparently it has not noticed you - yet."; + } + else if (Options.may_stab_brand != CHATTR_NORMAL + && mons_looks_distracted(mons)) + { + ostr << "Apparently it has been distracted by something."; + } + + std::string broken = ostr.str(); + linebreak_string2(broken, get_tutorial_cols()); + formatted_string::parse_block(broken, false).display(); +} // tutorial_describe_monster diff --git a/crawl-ref/source/tutorial.h b/crawl-ref/source/tutorial.h index 2763b08650..a4824ab89b 100644 --- a/crawl-ref/source/tutorial.h +++ b/crawl-ref/source/tutorial.h @@ -37,8 +37,12 @@ void tutorial_first_monster(const monsters& mon); void tutorial_first_item(const item_def& item); void learned_something_new(tutorial_event_type seen_what, int x=0, int y=0); formatted_string tut_abilities_info(); -void tutorial_describe_item(item_def& item); -bool tutorial_feat_interesting(int feat); -void tutorial_describe_feature(int feat); + +// additional information for tutorial players +void tutorial_describe_item(item_def &item); +bool tutorial_feat_interesting(dungeon_feature_type feat); +void tutorial_describe_feature(dungeon_feature_type feat); +bool tutorial_monster_interesting(const monsters *mons); +void tutorial_describe_monster(const monsters *mons); #endif -- cgit v1.2.3-54-g00ecf