diff options
author | Robert Vollmert <rvollmert@gmx.net> | 2010-04-28 12:36:13 +0200 |
---|---|---|
committer | Robert Vollmert <rvollmert@gmx.net> | 2010-04-28 13:19:41 +0200 |
commit | fe707db904bc4746f166f38aa3adb54b0e13f0b6 (patch) | |
tree | dbf1a2fe929e7cf7280d1bf7ae2a06cbf0686be5 /crawl-ref/source/tutorial.cc | |
parent | a23089eb55c683ff65ff4b78b65819396110ba0e (diff) | |
download | crawl-ref-fe707db904bc4746f166f38aa3adb54b0e13f0b6.tar.gz crawl-ref-fe707db904bc4746f166f38aa3adb54b0e13f0b6.zip |
Rename old "tutorial" to "hints".
This is to mostly everything inside crawl-ref/source.
I may have missed other places.
Diffstat (limited to 'crawl-ref/source/tutorial.cc')
-rw-r--r-- | crawl-ref/source/tutorial.cc | 5114 |
1 files changed, 0 insertions, 5114 deletions
diff --git a/crawl-ref/source/tutorial.cc b/crawl-ref/source/tutorial.cc deleted file mode 100644 index 6ca214a618..0000000000 --- a/crawl-ref/source/tutorial.cc +++ /dev/null @@ -1,5114 +0,0 @@ -/* - * File: tutorial.cc - * Summary: A tutorial mode as an introduction on how to play Dungeon Crawl. - * Written by: j-p-e-g - * - * Created on 2007-01-11. - */ - -#include "AppHdr.h" - -#include "cio.h" - -#include <cstring> -#include <sstream> - -#include "tutorial.h" - -#include "abl-show.h" -#include "artefact.h" -#include "cloud.h" -#include "colour.h" -#include "coordit.h" -#include "command.h" -#include "decks.h" -#include "describe.h" -#include "files.h" -#include "food.h" -#include "format.h" -#include "fprop.h" -#include "invent.h" -#include "itemname.h" -#include "itemprop.h" -#include "items.h" -#include "kills.h" -#include "macro.h" -#include "menu.h" -#include "message.h" -#include "misc.h" -#include "mon-behv.h" -#include "mon-pick.h" -#include "mon-util.h" -#include "mutation.h" -#include "newgame.h" -#include "options.h" -#include "jobs.h" -#include "player.h" -#include "random.h" -#include "religion.h" -#include "shopping.h" -#include "showsymb.h" -#include "skills2.h" -#include "species.h" -#include "spl-book.h" -#include "state.h" -#include "stuff.h" -#include "env.h" -#include "tags.h" -#include "terrain.h" -#ifdef USE_TILE - #include "tiles.h" -#endif -#include "travel.h" -#include "view.h" -#include "viewchar.h" -#include "viewgeom.h" - -static species_type _get_tutorial_species(unsigned int type); -static job_type _get_tutorial_job(unsigned int type); -static bool _tutorial_feat_interesting(dungeon_feature_type feat); -static void _tutorial_describe_disturbance(int x, int y); -static void _tutorial_describe_cloud(int x, int y); -static void _tutorial_describe_feature(int x, int y); -static bool _water_is_disturbed(int x, int y); - -//#define TUTORIAL_DEBUG -#define TUTORIAL_VERSION 11 - -static int _get_tutorial_cols() -{ -#ifdef USE_TILE - return crawl_view.msgsz.x; -#else - int ncols = get_number_of_cols(); - return (ncols > 80? 80 : ncols); -#endif -} - -tutorial_state Tutorial; - -void save_tutorial(writer& outf) -{ - marshallLong( outf, TUTORIAL_VERSION); - marshallShort( outf, Tutorial.tutorial_type); - for (long i = 0; i < TUT_EVENTS_NUM; ++i) - marshallBoolean( outf, Tutorial.tutorial_events[i] ); -} - -void load_tutorial(reader& inf) -{ - Tutorial.tutorial_left = 0; - - int version = unmarshallLong(inf); - if (version != TUTORIAL_VERSION) - return; - - Tutorial.tutorial_type = unmarshallShort(inf); - for (long i = 0; i < TUT_EVENTS_NUM; ++i) - { - Tutorial.tutorial_events[i] = unmarshallBoolean(inf); - Tutorial.tutorial_left += Tutorial.tutorial_events[i]; - } -} - -// Override init file definition for some options. -void init_tutorial_options() -{ - if (!Tutorial.tutorial_left) - return; - - // Clear possible debug messages before messing - // with messaging options. - mesclr(true); -// Options.clear_messages = true; - Options.auto_list = true; - Options.show_more = true; - Options.small_more = false; - -#ifdef USE_TILE - Options.tile_tag_pref = TAGPREF_TUTORIAL; -#endif -} - -// Tutorial selection screen and choice. -bool pick_tutorial() -{ - clrscr(); - - cgotoxy(1,1); - formatted_string::parse_string( - "<white>You must be new here indeed!</white>" - "\n\n" - "<cyan>You can be:</cyan>" - "\n").display(); - - textcolor( LIGHTGREY ); - - for (int i = 0; i < TUT_TYPES_NUM; i++) - print_tutorial_menu(i); - - formatted_string::parse_string( - "\n" - "<brown>SPACE - Back to background selection; " - "Bksp - Back to species selection; X - Quit" - "\n* - Random tutorial" - "</brown>\n").display(); - - while (true) - { - char keyn = getch_ck(); - - // Random choice. - if (keyn == '*' || keyn == '+' || keyn == '!' || keyn == '#') - keyn = 'a' + random2(TUT_TYPES_NUM); - - // Choose character for tutorial game and set starting values. - if (keyn >= 'a' && keyn <= 'a' + TUT_TYPES_NUM - 1) - { - Tutorial.tutorial_type = keyn - 'a'; - you.species = _get_tutorial_species(Tutorial.tutorial_type); - you.char_class = _get_tutorial_job(Tutorial.tutorial_type); - - // Activate all triggers. - // This is rather backwards: If (true) an event still needs to be - // triggered, if (false) the relevant message was already printed. - Tutorial.tutorial_events.init(true); - Tutorial.tutorial_left = TUT_EVENTS_NUM; - - // Used to compare which fighting means was used most often. - // XXX: This gets reset with every save, which seems odd. - // On the other hand, it's precisely between saves that - // players are most likely to forget these. - Tutorial.tut_spell_counter = 0; - Tutorial.tut_throw_counter = 0; - Tutorial.tut_melee_counter = 0; - Tutorial.tut_berserk_counter = 0; - - // Store whether explore, stash search or travelling was used. - // XXX: Also not stored across save games. - Tutorial.tut_explored = true; - Tutorial.tut_stashes = true; - Tutorial.tut_travel = true; - - // For occasional healing reminders. - Tutorial.tut_last_healed = 0; - - // Did the player recently see a monster turn invisible? - Tutorial.tut_seen_invisible = 0; - - Options.game.fully_random = false; - Options.game.book = SBT_RANDOM; - Options.game.weapon = WPN_HAND_AXE; // easiest choice for fighters - - crawl_state.type = GAME_TYPE_TUTORIAL; - - return (true); - } - - if (keyn == CK_BKSP || keyn == ' ' || keyn == ESCAPE) - { - // In this case, undo previous choices. - you.species = SP_UNKNOWN; - you.char_class = JOB_UNKNOWN; - Options.game.species = SP_UNKNOWN; - Options.game.job = JOB_UNKNOWN; - } - - switch (keyn) - { - case CK_BKSP: - case ESCAPE: - return (false); - case ' ': - return (false); - case 'X': - cprintf("\nGoodbye!"); - end(0); - return (false); - } - } - return (false); -} - -void tutorial_load_game() -{ - if (!Tutorial.tutorial_left) - return; - - learned_something_new(TUT_LOAD_SAVED_GAME); - - // Reinitialise counters for explore, stash search and travelling. - Tutorial.tut_explored = Tutorial.tutorial_events[TUT_AUTO_EXPLORE]; - Tutorial.tut_stashes = true; - Tutorial.tut_travel = true; -} - -void print_tutorial_menu(unsigned int type) -{ - char letter = 'a' + type; - char desc[100]; - - switch (type) - { - case TUT_BERSERK_CHAR: - strcpy(desc, "(Melee oriented character with divine support)"); - break; - case TUT_MAGIC_CHAR: - strcpy(desc, "(Magic oriented character)"); - break; - case TUT_RANGER_CHAR: - strcpy(desc, "(Ranged fighter)"); - break; - default: // no further choices - strcpy(desc, "(erroneous character)"); - break; - } - - cprintf("%c - %s %s %s\n", - letter, species_name(_get_tutorial_species(type), 1).c_str(), - get_job_name(_get_tutorial_job(type)), desc); -} - -static species_type _get_tutorial_species(unsigned int type) -{ - switch (type) - { - case TUT_BERSERK_CHAR: - return SP_MINOTAUR; - case TUT_MAGIC_CHAR: - return SP_DEEP_ELF; - case TUT_RANGER_CHAR: - return SP_CENTAUR; - default: - // Use something fancy for debugging. - return SP_KENKU; - } -} - -static job_type _get_tutorial_job(unsigned int type) -{ - switch (type) - { - case TUT_BERSERK_CHAR: - return JOB_BERSERKER; - case TUT_MAGIC_CHAR: - return JOB_CONJURER; - case TUT_RANGER_CHAR: - return JOB_HUNTER; - default: - // Use something fancy for debugging. - return JOB_NECROMANCER; - } -} - -// Converts all secret doors in a fixed radius around the player's starting -// position into normal closed doors. -// FIXME: Ideally, we'd need to zap secret doors that block the way -// between entrance and exit. -void tutorial_zap_secret_doors() -{ - for (radius_iterator ri(you.pos(), 25, true, false); ri; ++ri) - if (grd(*ri) == DNGN_SECRET_DOOR) - grd(*ri) = DNGN_CLOSED_DOOR; -} - -// Prints the tutorial welcome screen. -static void _tut_print_starting_info(unsigned int width) -{ - std::string text; - - text = "<white>Welcome to Dungeon Crawl!</white>\n\n"; - text += "Your object is to lead a <w>" - + species_name(you.species, 1) + " " + you.class_name - + - "</w> safely through the depths of the dungeon, retrieving the " - "fabled Orb of Zot and returning it to the surface. " - "In the beginning, however, let discovery be your " - "main goal. Try to delve as deeply as possible but beware; " - "death lurks around every corner.\n\n" - "For the moment, just remember the following keys " - "and their functions:\n" - " <white>%?</white> - shows the items and the commands\n" - " <white>%</white> - saves the game, to be resumed later " - "(but note that death is permanent)\n" - " <white>%</white> - examines something in your vicinity\n\n" - "This tutorial will help you play Crawl without reading any " - "documentation. If you feel intrigued, there is more information " - "available in the following files from the docs/ folder (all of " - "which can also be read in-game):" - "\n" - " <lightblue>quickstart.txt</lightblue> - " - "A very short guide to Crawl.\n" - " <lightblue>crawl_manual.txt</lightblue> - " - "This contains all details on species, magic, skills, etc.\n" - " <lightblue>options_guide.txt</lightblue> - " - "Crawl's interface is highly configurable. This document \n" - " explains all the options.\n" - "\n" - "Press <white>Space</white> to proceed to the basics " - "(the screen division and movement).\n" - "Press <white>Esc</white> to fast forward to the game start."; - - insert_commands(text, CMD_DISPLAY_COMMANDS, CMD_SAVE_GAME, CMD_LOOK_AROUND, 0); - linebreak_string2(text, width); - display_tagged_block(text); -} - -#ifdef TUTORIAL_DEBUG -static std::string _tut_debug_list(int event) -{ - switch (event) - { - case TUT_SEEN_FIRST_OBJECT: - return "seen first object"; - case TUT_SEEN_POTION: - return "seen first potion"; - case TUT_SEEN_SCROLL: - return "seen first scroll"; - case TUT_SEEN_WAND: - return "seen first wand"; - case TUT_SEEN_SPBOOK: - return "seen first spellbook"; - case TUT_SEEN_WEAPON: - return "seen first weapon"; - case TUT_SEEN_MISSILES: - return "seen first missiles"; - case TUT_SEEN_ARMOUR: - return "seen first armour"; - case TUT_SEEN_RANDART: - return "seen first random artefact"; - case TUT_SEEN_FOOD: - return "seen first food"; - case TUT_SEEN_CARRION: - return "seen first corpse"; - case TUT_SEEN_GOLD: - return "seen first pile of gold"; - case TUT_SEEN_JEWELLERY: - return "seen first jewellery"; - case TUT_SEEN_MISC: - return "seen first misc. item"; - case TUT_SEEN_MONSTER: - return "seen first monster"; - case TUT_SEEN_ZERO_EXP_MON: - return "seen first zero experience monster"; - case TUT_SEEN_TOADSTOOL: - return "seen first toadstool"; - case TUT_SEEN_STAIRS: - return "seen first stairs"; - case TUT_SEEN_ESCAPE_HATCH: - return "seen first escape hatch"; - case TUT_SEEN_BRANCH: - return "seen first branch entrance"; - case TUT_SEEN_PORTAL: - return "seen first portal vault entrance"; - case TUT_SEEN_TRAP: - return "encountered a trap"; - case TUT_SEEN_ALTAR: - return "seen an altar"; - case TUT_SEEN_SHOP: - return "seen a shop"; - case TUT_SEEN_DOOR: - return "seen a closed door"; - case TUT_FOUND_SECRET_DOOR: - return "found a secret door"; - case TUT_KILLED_MONSTER: - return "killed first monster"; - case TUT_NEW_LEVEL: - return "gained a new level"; - case TUT_SKILL_RAISE: - return "raised a skill"; - case TUT_YOU_ENCHANTED: - return "caught an enchantment"; - case TUT_YOU_SICK: - return "became sick"; - case TUT_YOU_POISON: - return "were poisoned"; - case TUT_YOU_ROTTING: - return "were rotting"; - case TUT_YOU_CURSED: - return "had something cursed"; - case TUT_YOU_HUNGRY: - return "felt hungry"; - case TUT_YOU_STARVING: - return "were starving"; - case TUT_MAKE_CHUNKS: - return "learned about chunks"; - case TUT_OFFER_CORPSE: - return "learned about sacrifice"; - case TUT_MULTI_PICKUP: - return "read about pickup menu"; - case TUT_HEAVY_LOAD: - return "were encumbered"; - case TUT_ROTTEN_FOOD: - return "carried rotten food"; - case TUT_NEED_HEALING: - return "needed healing"; - case TUT_NEED_POISON_HEALING: - return "needed healing for poison"; - case TUT_INVISIBLE_DANGER: - return "encountered an invisible foe"; - case TUT_NEED_HEALING_INVIS: - return "had to heal near an unseen monster"; - case TUT_ABYSS: - return "was cast into the Abyss"; - case TUT_POSTBERSERK: - return "learned about Berserk after-effects"; - case TUT_RUN_AWAY: - return "were told to run away"; - case TUT_RETREAT_CASTER: - return "were told to retreat as a caster"; - case TUT_SHIFT_RUN: - return "learned about shift-run"; - case TUT_MAP_VIEW: - return "learned about the level map"; - case TUT_AUTO_EXPLORE: - return "learned about auto-explore"; - case TUT_DONE_EXPLORE: - return "explored a level"; - case TUT_AUTO_EXCLUSION: - return "learned about exclusions"; - case TUT_YOU_MUTATED: - return "caught a mutation"; - case TUT_NEW_ABILITY_GOD: - return "gained a divine ability"; - case TUT_NEW_ABILITY_MUT: - return "gained a mutation-granted ability"; - case TUT_NEW_ABILITY_ITEM: - return "gained an item-granted ability"; - case TUT_WIELD_WEAPON: - return "wielded an unsuitable weapon"; - case TUT_FLEEING_MONSTER: - return "made a monster flee"; - case TUT_MONSTER_BRAND: - return "learned about colour brandings"; - case TUT_MONSTER_FRIENDLY: - return "seen first friendly monster"; - case TUT_MONSTER_SHOUT: - return "experienced first shouting monster"; - case TUT_CONVERT: - return "converted to a god"; - case TUT_GOD_DISPLEASED: - return "piety ran low"; - case TUT_EXCOMMUNICATE: - return "excommunicated by a god"; - case TUT_SPELL_MISCAST: - return "spell miscast"; - case TUT_SPELL_HUNGER: - return "spell casting caused hunger"; - case TUT_GLOWING: - return "player glowing from contamination"; - case TUT_STAIR_BRAND: - return "saw stairs with objects on it"; - case TUT_HEAP_BRAND: - return "saw heap of objects"; - case TUT_TRAP_BRAND: - return "saw trap with objects on it"; - case TUT_YOU_RESIST: - return "resisted some magic"; - case TUT_CAUGHT_IN_NET: - return "were caught in a net"; - case TUT_LOAD_SAVED_GAME: - return "restored a saved game"; - case TUT_GAINED_MAGICAL_SKILL: - return "gained a new magical skill"; - case TUT_CHOOSE_STAT: - return "could choose a stat"; - case TUT_CAN_BERSERK: - return "were told to Berserk"; - case TUT_YOU_SILENCE: - return "experienced silence"; - default: - return "faced a bug"; - } -} - -// Lists all triggerable events and whether they actually were triggered -// at some point, at game start or reload. -static formatted_string _tutorial_debug() -{ - std::string result; - bool lbreak = false; - snprintf(info, INFO_SIZE, "Tutorial Debug Screen"); - - int i = _get_tutorial_cols()/2-1 - strlen(info) / 2; - result += std::string(i, ' '); - result += "<white>"; - result += info; - result += "</white>\n\n"; - - result += "<lightblue>"; - for (i = 0; i < TUT_EVENTS_NUM; i++) - { - snprintf(info, INFO_SIZE, "%d: %s (%s)", - i, _tut_debug_list(i).c_str(), - Tutorial.tutorial_events[i] ? "true" : "false"); - - result += info; - - // Break text into 2 columns where possible. - if (strlen(info) >= _get_tutorial_cols()/2 || lbreak) - { - result += "\n"; - lbreak = false; - } - else - { - result += std::string(_get_tutorial_cols()/2-1 - strlen(info), ' '); - lbreak = true; - } - } - result += "</lightblue>\n\n"; - - snprintf(info, INFO_SIZE, "tutorial_left: %d\n", Tutorial.tutorial_left); - result += info; - result += "\n"; - - snprintf(info, INFO_SIZE, "You are a %s %s, and the tutorial will reflect " - "that.", species_name(you.species, 1), you.class_name); - - result += info; - - return formatted_string::parse_string(result); -} -#endif // debug - -#ifndef USE_TILE -static void _tutorial_map_intro() -{ - std::string result; - - result = "<"; - result += colour_to_str(channel_to_colour(MSGCH_TUTORIAL)); - result += ">"; - result += "What you see here is the typical Crawl screen. The upper left " - "map shows your hero as the <w>@</w> in the center. The parts " - "of the map you remember but cannot currently see will be greyed " - "out."; - result += "</"; - result += colour_to_str(channel_to_colour(MSGCH_TUTORIAL)); - result += ">\n"; - - mpr(result); -} -#endif - -static void _tutorial_stats_intro() -{ - std::ostringstream istr; - -#ifdef USE_TILE - istr << "To the upper right, important properties of the character are " - "displayed. The most basic one is Health, shown as " - "<w>Health: " << you.hp << "/" << you.hp_max << "</w> " - "and meaning current out of maximum health points. When Health " - "drops to zero, you die. " - "<w>Magic: " << you.magic_points << "/" << you.max_magic_points - << "</w> represents your energy for casting spells, although other " - "actions often draw from Magic, too.\n" - "<w>Str</w>ength, <w>Int</w>elligence, <w>Dex</w>terity below " - "provide an all-around account of the character's attributes. " - "Don't worry about the rest for now.\n"; - - mpr(istr.str(), MSGCH_TUTORIAL, 0); -#else - // Note: must fill up everything to override the map - istr << "<" - << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) - << ">" - << "To the right, important properties \n" - "of the character are displayed. The \n" - "most basic one is Health, shown as \n" - "<w>Health: " << you.hp << "/" << you.hp_max << "</w> " - "and meaning current \n" - "out of maximum health points. When \n" - "Health drops to zero, you die. \n" - "<w>Magic: " << you.magic_points << "/" << you.max_magic_points - << "</w> represents your energy \n" - "for casting spells, although other \n" - "actions often draw from Magic, too. \n" - "<w>Str</w>ength, <w>Int</w>elligence, <w>Dex</w>terity \n" - "below provide an all-around account \n" - "of the character's attributes. \n" - "Don't worry about the rest for now. \n" - << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">" - " \n" - "<lightgrey> --more-- " - "Press <white>Escape</white> to skip the basics.</lightgrey>\n" - " \n" - " \n"; - - display_tagged_block(istr.str()); -#endif -} - -static void _tutorial_message_intro() -{ - std::string result; - - result = "This lower " -#ifdef USE_TILE - "left " -#endif - "part of the screen is reserved for messages. " - "Everything related to the tutorial is shown in this colour. "; - - if (!Options.clear_messages) - { - result += "Old messages will be displayed until new ones roll in, so " - "you will often see messages from several turns ago. In the " - "message window, the beginning of a new turn is marked with " - "messages getting prefixed with <w>_</w>. "; - } - - result += "If you missed something, previous messages can be read again " - "with <w>%</w>" -#ifdef USE_TILE - " or by <w>clicking into the message area</w>" -#endif - ".\n"; - insert_commands(result, CMD_REPLAY_MESSAGES, 0); - -#ifdef USE_TILE - mpr(result, MSGCH_TUTORIAL, 0); - - result = "To the bottom right of the screen is a display of items. The " - "top part is reserved for your inventory, the remaining slots " - "may be filled with items beneath you on the floor.\n" - "If you click on the tabs to the left of this display you can " - "switch to a spell display for currently memorised spells to " - "cast them, or to a display of not yet memorised spells " - "included in books you are carrying, so as to commit them to " - "memory.\n"; -#endif - - mpr(result, MSGCH_TUTORIAL, 0); -} - -static void _tutorial_movement_info() -{ - std::string text = - "To move your character, use the numpad; try Numlock both on and off. " - "If your system has no number pad, or if you are familiar with the vi " - "keys, movement is also possible with <w>%%%%%%%%</w>. " -#ifdef USE_TILE - "You can also move by clicking somewhere on the map. If this is " - "considered safe, i.e. there are no monsters around, you'll move " - "towards the chosen square." -#endif - "\n" - "A basic command list can be found under <w>?\?</w>, and the most " - "important commands will be explained to you as it becomes necessary." - "\n"; - - insert_commands(text, CMD_MOVE_LEFT, CMD_MOVE_DOWN, CMD_MOVE_UP, - CMD_MOVE_RIGHT, CMD_MOVE_UP_LEFT, CMD_MOVE_UP_RIGHT, - CMD_MOVE_DOWN_LEFT, CMD_MOVE_DOWN_RIGHT, 0); - mpr(text, MSGCH_TUTORIAL, 0); -} - -// copied from display_mutations and adapted -void tut_starting_screen() -{ - int MAX_INFO = 4; -#ifdef TUTORIAL_DEBUG - MAX_INFO = 5; // add tutorial_debug -#endif - char ch = 0; - - int i; - for (i = 0; i <= MAX_INFO; i++) - { -#ifndef USE_TILE - // Map window (starts at 1) or message window (starts at 18). - // FIXME: This should be done more cleanly using the - // crawl_view settings! - int y_pos = (i == 1 || i == 3) ? 18 : 1; - - cgotoxy(1, y_pos); -#endif - if (i == 0) - clrscr(); - - int width = _get_tutorial_cols(); -#ifdef USE_TILE - // use a more sensible screen width - if (width < 80 && width < crawl_view.msgsz.x + crawl_view.hudsz.x) - width = crawl_view.msgsz.x + crawl_view.hudsz.x; - if (width > 80) - width = 80; -#endif - if (i == 0) - _tut_print_starting_info(width); - else if (i == 1) - { -#ifdef USE_TILE - // Skip map explanation for Tiles. - continue; -#else - _tutorial_map_intro(); -#endif - } - else if (i == 2) - _tutorial_stats_intro(); - else if (i == 3) - _tutorial_message_intro(); - else if (i == 4) - _tutorial_movement_info(); - else - { -#ifdef TUTORIAL_DEBUG - clrscr(); - #ifndef USE_TILE - cgotoxy(1,y_pos); - #endif - _tutorial_debug().display(); -#else - continue; -#endif - } - - if (i < MAX_INFO) - { - if (i < 2) - { -#ifndef USE_TILE - ch = getch_ck(); -#else - mouse_control mc(MOUSE_MODE_MORE); - ch = getchm(); -#endif - } - redraw_screen(); - if (ch == ESCAPE) - break; - } - } - mpr("Introduction finished, you can start playing now."); - mpr("Again, press <w>?</w> for the help, and <w>Ctrl-P</w> to reread the message history."); -} - -// Called each turn from _input. Better name welcome. -void tutorial_new_turn() -{ - if (Tutorial.tutorial_left) - { - Tutorial.tut_just_triggered = false; - - if (you.attribute[ATTR_HELD]) - learned_something_new(TUT_CAUGHT_IN_NET); - else if (i_feel_safe() && you.level_type != LEVEL_ABYSS) - { - // We don't want those "Whew, it's safe to rest now" messages - // if you were just cast into the Abyss. Right? - - if (2 * you.hp < you.hp_max - || 2 * you.magic_points < you.max_magic_points) - { - tutorial_healing_reminder(); - } - else if (!you.running - && Tutorial.tutorial_events[TUT_SHIFT_RUN] - && you.num_turns >= 200 - && you.hp == you.hp_max - && you.magic_points == you.max_magic_points) - { - learned_something_new(TUT_SHIFT_RUN); - } - else if (!you.running - && Tutorial.tutorial_events[TUT_MAP_VIEW] - && you.num_turns >= 500 - && you.hp == you.hp_max - && you.magic_points == you.max_magic_points) - { - learned_something_new(TUT_MAP_VIEW); - - learned_something_new(TUT_MAP_VIEW); - } - else if (!you.running - && Tutorial.tutorial_events[TUT_AUTO_EXPLORE] - && you.num_turns >= 700 - && you.hp == you.hp_max - && you.magic_points == you.max_magic_points) - { - learned_something_new(TUT_AUTO_EXPLORE); - } - } - else - { - if (2*you.hp < you.hp_max) - learned_something_new(TUT_RUN_AWAY); - - if (Tutorial.tutorial_type == TUT_MAGIC_CHAR && you.magic_points < 1) - learned_something_new(TUT_RETREAT_CASTER); - } - } -} - -// Once a tutorial character dies, offer some playing hints. -void tutorial_death_screen() -{ - Tutorial.tutorial_left = 0; - std::string text; - - mpr( "Condolences! Your character's premature death is a sad, but " - "common occurrence in Crawl. Rest assured that with diligence and " - "playing experience your characters will last longer.", - MSGCH_TUTORIAL); - - mpr( "Perhaps the following advice can improve your playing style:", - MSGCH_TUTORIAL); - more(); - - if (Tutorial.tutorial_type == TUT_MAGIC_CHAR - && Tutorial.tut_spell_counter < Tutorial.tut_melee_counter ) - { - text = "As a Conjurer your main weapon should be offensive magic. Cast " - "spells more often! Remember to rest when your Magic is low."; - } - else if (you.religion == GOD_TROG && Tutorial.tut_berserk_counter <= 3 - && !you.berserk() && !you.duration[DUR_EXHAUSTED]) - { - text = "Don't forget to go berserk when fighting particularly " - "difficult foes. It is risky, but makes you faster and beefier."; - - if (you.hunger_state <= HS_HUNGRY) - { - text += " Berserking is impossible while hungry or worse, so make " - "sure to keep some food with you that you can eat when you " - "need to go berserk."; - } - } - else if (Tutorial.tutorial_type == TUT_RANGER_CHAR - && 2*Tutorial.tut_throw_counter < Tutorial.tut_melee_counter ) - { - text = "Your bow and arrows are extremely powerful against distant " - "monsters. Be sure to collect all arrows lying around in the " - "dungeon."; - } - else - { - int hint = random2(6); - - bool skip_first_hint = false; - // If a character has been unusually busy with projectiles and spells - // give some other hint rather than the first one. - if (hint == 0 && Tutorial.tut_throw_counter + Tutorial.tut_spell_counter - >= Tutorial.tut_melee_counter) - { - hint = random2(5) + 1; - skip_first_hint = true; - } - // FIXME: The hints below could be somewhat less random, so that e.g. - // the message for fighting several monsters in a corridor only happens - // if there's more than one monster around and you're not in a corridor, - // or the one about using consumable objects only if you actually have - // any (useful or unidentified) scrolls/wands/potions. - - if (hint == 5) - { - std::vector<monsters*> visible = - get_nearby_monsters(false, true, true, false); - - if (visible.size() < 2) - { - if (skip_first_hint) - hint = random2(4) + 1; - else - hint = random2(5); - } - } - - switch (hint) - { - case 0: - text = "Always consider using projectiles, wands or spells before " - "engaging monsters in close combat."; - break; - - case 1: - text = "Learn when to run away from things you can't handle - this " - "is important! It is often wise to skip a particularly " - "dangerous level. But don't overdo this as monsters will " - "only get harder the deeper you delve."; - break; - - case 2: - text = "Rest between encounters, if possible in an area already " - "explored and cleared of monsters. In Crawl, searching and " - "resting are one and the same. To search for one turn, " - "press <w>s</w>, <w>.</w>, <w>delete</w> or " - "<w>keypad-5</w>. Pressing <w>5</w> or " - "<w>shift-and-keypad-5</w>" -#ifdef USE_TILE - ", or <w>clicking into the stat area</w>" -#endif - " will let you rest for a longer time (you will stop " - "resting after 100 turns, or when fully healed)."; - break; - - case 3: - text = "Remember to use those scrolls, potions or wands you've " - "found. Very often, you cannot expect to identify " - "everything with the scroll only. Learn to improvise: " - "identify through usage."; - break; - - case 4: - text = "If a particular encounter feels overwhelming don't " - "forget to use emergency items early on. A scroll of " - "teleportation or a potion of speed can really save your " - "bacon."; - break; - - case 5: - text = "Never fight more than one monster, if you can help it. " - "Always back into a corridor so that they are forced to " - "fight you one on one."; - break; - - default: - text = "Sorry, no hint this time, though there should have been " - "one."; - } - } - mpr(text, MSGCH_TUTORIAL, 0); - more(); - - mpr( "See you next game!", MSGCH_TUTORIAL); - - Tutorial.tutorial_events.init(false); -} - -// If a character survives until Xp 7, the tutorial is declared finished -// and they get a more advanced playing hint, depending on what they might -// know by now. -void tutorial_finished() -{ - std::string text; - - Tutorial.tutorial_left = 0; - text = "Congrats! You survived until the end of this tutorial - be sure " - "to try the other ones as well. Note that the command help screen " - "(<w>%?</w>) will look very different from now on. Here's a last " - "playing hint:"; - insert_commands(text, CMD_DISPLAY_COMMANDS, 0); - - mpr(text, MSGCH_TUTORIAL, 0); - more(); - - if (Tutorial.tut_explored) - { - text = "Walking around and exploring levels gets easier by using " - "auto-explore (<w>%</w>). Crawl will let you automatically " - "move to and pick up interesting items."; -#ifdef USE_TILE - text += "\nAutoexploration can also be started by <w>left-clicking</w> " - "on the minimap while the <w>Control</w> key is pressed."; -#endif - insert_commands(text, CMD_EXPLORE, 0); - } - else if (Tutorial.tut_travel) - { - text = "There is a convenient way for travelling between far away " - "dungeon levels: press <w>%</w> or <w>G</w> and enter " - "the desired destination. If your travel gets interrupted, " - "issuing <w>% Enter</w> or <w>G Enter</w> will continue " - "it."; - insert_commands(text, CMD_INTERLEVEL_TRAVEL, CMD_INTERLEVEL_TRAVEL, 0); - } - else if (Tutorial.tut_stashes) - { - text = "You can search among all items existing in the dungeon with " - "the <w>%</w> command. For example, " - "<w>% \"knife\"</w> will list all knives. You can then " - "travel to one of the spots. It is even possible to enter " - "words like <w>\"shop\"</w> or <w>\"altar\"</w>."; - insert_commands(text, CMD_SEARCH_STASHES, CMD_SEARCH_STASHES, 0); - } - else - { - int hint = random2(4); - switch (hint) - { - case 0: - text = "The game keeps an automated logbook for your characters. " - "Use <w>%:</w> to read it. You can enter notes manually " - "with the <w>%</w> command. Once your character perishes, " - "two morgue files are left in the <w>morgue/</w> " - "directory. The one ending in .txt contains a copy of " - "your logbook. During play, you can create a dump file " - "with <w>%</w>."; - insert_commands(text, CMD_DISPLAY_COMMANDS, CMD_MAKE_NOTE, - CMD_CHARACTER_DUMP, 0); - break; - - case 1: - text = "Crawl has a macro function built in: press <w>~m</w> " - "to define a macro by first specifying a trigger key " - "(say, <w>F1</w>) and a command sequence, for example " - "<w>za+.</w>. The latter will make the <w>F1</w> " - "key always zap the spell in slot a at the nearest " - "monster. For more information on macros, type <w>%~</w>."; - insert_commands(text, CMD_DISPLAY_COMMANDS, 0); - break; - - case 2: - text = "The interface can be greatly customised. All options are " - "explained in the file <w>options_guide.txt</w> which " - "can be found in the <w>docs</w> directory. The options " - "themselves are set in <w>init.txt</w> or " - "<w>.crawlrc</w>. Crawl will complain if it can't find " - "either file."; - break; - - case 3: - text = "You can ask other Crawl players for advice and help " - "on the <w>##crawl</w> IRC (Internet Relay Chat) " - "channel on freenode (<w>irc.freenode.net</w>)."; - break; - - default: - text = "Oops... No hint for now. Better luck next time!"; - } - } - mpr(text, MSGCH_TUTORIAL, 0); - more(); - - Tutorial.tutorial_events.init(false); - - // Unlink tutorial file. - const std::string basename = get_savedir_filename(you.your_name, "", ""); - const std::string tmpname = basename + ".tut"; - unlink( tmpname.c_str() ); -} - -// Occasionally remind religious characters of sacrifices. -void tutorial_dissection_reminder(bool healthy) -{ - if (Tutorial.tut_just_triggered || !Tutorial.tutorial_left) - return; - - // When hungry, give appropriate message or at least don't suggest - // sacrifice. - if (you.hunger_state < HS_SATIATED && healthy) - { - learned_something_new(TUT_MAKE_CHUNKS); - return; - } - - if (!god_likes_fresh_corpses(you.religion)) - return; - - if (Tutorial.tutorial_events[TUT_OFFER_CORPSE]) - learned_something_new(TUT_OFFER_CORPSE); - else if (one_chance_in(8)) - { - Tutorial.tut_just_triggered = true; - - std::string text; - text += "If you don't want to eat it, consider offering this " - "corpse up under <w>p</w>rayer as a sacrifice to "; - text += god_name(you.religion); -#ifdef USE_TILE - text += ". You can also chop up any corpse that shows in the floor " - "part of your inventory tiles by clicking on it with your " - "<w>left mouse button</w>"; -#endif - - text += ". Whenever you view a corpse while in tutorial mode you can " - "reread this information."; - - mpr(text, MSGCH_TUTORIAL, 0); - - - if (is_resting()) - stop_running(); - } -} - -// Occasionally remind injured characters of resting. -void tutorial_healing_reminder() -{ - if (!Tutorial.tutorial_left) - return; - - if (you.duration[DUR_POISONING] && 2*you.hp < you.hp_max) - { - if (Tutorial.tutorial_events[TUT_NEED_POISON_HEALING]) - learned_something_new(TUT_NEED_POISON_HEALING); - } - else if (Tutorial.tut_seen_invisible > 0 - && you.num_turns - Tutorial.tut_seen_invisible <= 20) - { - // If we recently encountered an invisible monster, we need a - // special message. - learned_something_new(TUT_NEED_HEALING_INVIS); - // If that one was already displayed, don't print a reminder. - } - else - { - if (Tutorial.tutorial_events[TUT_NEED_HEALING]) - learned_something_new(TUT_NEED_HEALING); - else if (you.num_turns - Tutorial.tut_last_healed >= 50 - && !you.duration[DUR_POISONING]) - { - if (Tutorial.tut_just_triggered) - return; - - Tutorial.tut_just_triggered = 1; - - std::string text; - text = "Remember to rest between fights and to enter unexplored " - "terrain with full hitpoints and magic. Ideally you " - "should retreat into areas you've already explored and " - "cleared of monsters; resting on the edge of the explored " - "terrain increases the chances of your rest being " - "interrupted by wandering monsters. For resting, press " - "<w>5</w> or <w>Shift-numpad 5</w>" -#ifdef USE_TILE - ", or <w>click on the stat area</w> with your mouse" -#endif - "."; - - if (you.hp < you.hp_max && you.religion == GOD_TROG - && !you.berserk() && !you.duration[DUR_EXHAUSTED] - && you.hunger_state >= HS_SATIATED) - { - text += "\nAlso, berserking might help you not to lose so many " - "hitpoints in the first place. To use your abilities type " - "<w>a</w>."; - } - mpr(text, MSGCH_TUTORIAL, 0); - - - if (is_resting()) - stop_running(); - } - Tutorial.tut_last_healed = you.num_turns; - } -} - -// Give a message if you see, pick up or inspect an item type for the -// first time. -void taken_new_item(unsigned char item_type) -{ - switch (item_type) - { - case OBJ_WANDS: - learned_something_new(TUT_SEEN_WAND); - break; - case OBJ_SCROLLS: - learned_something_new(TUT_SEEN_SCROLL); - break; - case OBJ_JEWELLERY: - learned_something_new(TUT_SEEN_JEWELLERY); - break; - case OBJ_POTIONS: - learned_something_new(TUT_SEEN_POTION); - break; - case OBJ_BOOKS: - learned_something_new(TUT_SEEN_SPBOOK); - break; - case OBJ_FOOD: - learned_something_new(TUT_SEEN_FOOD); - break; - case OBJ_CORPSES: - learned_something_new(TUT_SEEN_CARRION); - break; - case OBJ_WEAPONS: - learned_something_new(TUT_SEEN_WEAPON); - break; - case OBJ_ARMOUR: - learned_something_new(TUT_SEEN_ARMOUR); - break; - case OBJ_MISSILES: - learned_something_new(TUT_SEEN_MISSILES); - break; - case OBJ_MISCELLANY: - learned_something_new(TUT_SEEN_MISC); - break; - case OBJ_STAVES: - learned_something_new(TUT_SEEN_STAFF); - break; - case OBJ_GOLD: - learned_something_new(TUT_SEEN_GOLD); - break; - default: // nothing to be done - return; - } -} - -// Give a special message if you gain a skill you didn't have before. -void tut_gained_new_skill(int skill) -{ - if (!Tutorial.tutorial_left) - return; - - learned_something_new(TUT_SKILL_RAISE); - - switch (skill) - { - // Special cases first. - case SK_FIGHTING: - case SK_ARMOUR: - case SK_STEALTH: - case SK_STABBING: - case SK_TRAPS_DOORS: - case SK_UNARMED_COMBAT: - case SK_INVOCATIONS: - case SK_EVOCATIONS: - case SK_DODGING: - case SK_SHIELDS: - case SK_THROWING: - case SK_SPELLCASTING: - { - mpr(get_skill_description(skill), MSGCH_TUTORIAL, 0); - stop_running(); - break; - } - // Only one message for all magic skills (except Spellcasting). - case SK_CONJURATIONS: - case SK_ENCHANTMENTS: - case SK_SUMMONINGS: - case SK_NECROMANCY: - case SK_TRANSLOCATIONS: - case SK_TRANSMUTATIONS: - case SK_FIRE_MAGIC: - case SK_ICE_MAGIC: - case SK_AIR_MAGIC: - case SK_EARTH_MAGIC: - case SK_POISON_MAGIC: - learned_something_new(TUT_GAINED_MAGICAL_SKILL); - break; - - // Melee skills. - case SK_SHORT_BLADES: - case SK_LONG_BLADES: - case SK_AXES: - case SK_MACES_FLAILS: - case SK_POLEARMS: - case SK_STAVES: - learned_something_new(TUT_GAINED_MELEE_SKILL); - break; - - // Ranged skills. - case SK_SLINGS: - case SK_BOWS: - case SK_CROSSBOWS: - learned_something_new(TUT_GAINED_RANGED_SKILL); - break; - - default: - break; - } -} - -#ifndef USE_TILE -// As safely as possible, colourize the passed glyph. -// Handles quoting "<", MBCS-ing unicode, and -// making DEC characters safe if not properly printable. -static std::string _colourize_glyph(int col, unsigned ch) -{ - glyph g; - g.col = col; - g.ch = ch; - return glyph_to_tagstr(g); -} -#endif - -static bool _mons_is_highlighted(const monsters *mons) -{ - return (mons->friendly() - && Options.friend_brand != CHATTR_NORMAL - || mons_looks_stabbable(mons) - && Options.stab_brand != CHATTR_NORMAL - || mons_looks_distracted(mons) - && Options.may_stab_brand != CHATTR_NORMAL); -} - -static bool _advise_use_wand() -{ - for (int i = 0; i < ENDOFPACK; i++) - { - item_def &obj(you.inv[i]); - - if (!obj.is_valid()) - continue; - - if (obj.base_type != OBJ_WANDS) - continue; - - // Wand type unknown, might be useful. - if (!item_type_known(obj)) - return (true); - - // Empty wands are no good. - if (obj.plus2 == ZAPCOUNT_EMPTY - || item_ident(obj, ISFLAG_KNOW_PLUSES) && obj.plus <= 0) - { - continue; - } - - // Can it be used to fight? - switch (obj.sub_type) - { - case WAND_FLAME: - case WAND_FROST: - case WAND_SLOWING: - case WAND_MAGIC_DARTS: - case WAND_PARALYSIS: - case WAND_FIRE: - case WAND_COLD: - case WAND_CONFUSION: - case WAND_FIREBALL: - case WAND_TELEPORTATION: - case WAND_LIGHTNING: - case WAND_ENSLAVEMENT: - case WAND_DRAINING: - case WAND_RANDOM_EFFECTS: - case WAND_DISINTEGRATION: - return (true); - } - } - - return (false); -} - -void tutorial_monster_seen(const monsters &mon) -{ - if (mons_class_flag(mon.type, M_NO_EXP_GAIN)) - { - tutorial_event_type et = mon.type == MONS_TOADSTOOL ? - TUT_SEEN_TOADSTOOL : TUT_SEEN_ZERO_EXP_MON; - - if (Tutorial.tutorial_events[et]) - { - if (Tutorial.tut_just_triggered) - return; - - learned_something_new(et, mon.pos()); - return; - } - - // Don't do TUT_SEEN_MONSTER for zero exp monsters. - if (Tutorial.tutorial_events[TUT_SEEN_MONSTER]) - return; - } - - if (!Tutorial.tutorial_events[TUT_SEEN_MONSTER]) - { - if (Tutorial.tut_just_triggered) - return; - - if (_mons_is_highlighted(&mon)) - learned_something_new(TUT_MONSTER_BRAND, mon.pos()); - if (mon.friendly()) - learned_something_new(TUT_MONSTER_FRIENDLY, mon.pos()); - - if (you.religion == GOD_TROG && !you.berserk() - && !you.duration[DUR_EXHAUSTED] && you.hunger_state >= HS_SATIATED - && one_chance_in(4)) - { - learned_something_new(TUT_CAN_BERSERK); - } - return; - } - - stop_running(); - - Tutorial.tutorial_events[TUT_SEEN_MONSTER] = false; - Tutorial.tutorial_left--; - Tutorial.tut_just_triggered = true; - - std::string text = "That "; -#ifdef USE_TILE - // need to highlight monster - const coord_def gc = mon.pos(); - tiles.place_cursor(CURSOR_TUTORIAL, gc); - tiles.add_text_tag(TAG_TUTORIAL, &mon); - - text += "monster is a "; - text += mon.name(DESC_PLAIN).c_str(); - text += ". Examples for typical early monsters are rats, giant newts, " - "kobolds, or goblins. You can gain information about any monster " - "by hovering your mouse over its tile, and read the monster " - "description by clicking on it with your <w>right mouse button</w>." -#else - text += glyph_to_tagstr(get_mons_glyph(&mon)); - text += " is a monster, usually depicted by a letter. Some typical " - "early monsters look like <brown>r</brown>, <green>l</green>, " - "<brown>K</brown> or <lightgrey>g</lightgrey>. "; - - if (crawl_view.mlistsz.y > 0) - { - text += "Your console settings allowing, you'll always see a " - "list of monsters somewhere on the screen.\n"; - } - text += "You can gain information about it by pressing <w>x</w> and " - "moving the cursor on the monster, and read the monster " - "description by then pressing <w>v</w>. " -#endif - "\nTo attack this monster with your wielded weapon, just move " - "into it. "; - -#ifdef USE_TILE - text += "Note that as long as there's a non-friendly monster in view you " - "won't be able to automatically move to distant squares, to avoid " - "death by misclicking."; -#endif - - mpr(text, MSGCH_TUTORIAL, 0); - - if (Tutorial.tutorial_type == TUT_RANGER_CHAR) - { - text = "However, as a hunter you will want to deal with it using your " - "bow. If you have a look at your bow from your " - "<w>i</w>nventory, you'll find an explanation of how to do " - "this. "; - - if (!you.weapon() - || you.weapon()->base_type != OBJ_WEAPONS - || you.weapon()->sub_type != WPN_BOW) - { - text += "First <w>w</w>ield it, then follow the instructions."; - -#ifdef USE_TILE - text += "\nAs a short-cut you can also <w>right-click</w> on your " - "bow to read its description, and <w>left-click</w> to wield " - "it."; -#endif - } -#ifdef USE_TILE - else - { - text += "Clicking with your <w>right mouse button</w> on your bow " - "will also let you read its description."; - } -#endif - - - mpr(text, MSGCH_TUTORIAL, 0); - - } - else if (Tutorial.tutorial_type == TUT_MAGIC_CHAR) - { - text = "However, as a conjurer you will want to deal with it using " - "magic. If you have a look at your spellbook from your " - "<w>i</w>nventory, you'll find an explanation of how to do " - "this."; - -#ifdef USE_TILE - text += "\nAs a short-cut you can also <w>right-click</w> on your " - "book in your inventory to read its description."; -#endif - mpr(text, MSGCH_TUTORIAL, 0); - - } -} - -void tutorial_first_item(const item_def &item) -{ - // Happens if monster is standing on dropped corpse or item. - if (monster_at(item.pos)) - return; - - if (!Tutorial.tutorial_events[TUT_SEEN_FIRST_OBJECT] - || Tutorial.tut_just_triggered) - { - // NOTE: Since a new player might not think to pick up a - // corpse (and why should they?), TUT_SEEN_CARRION is done when a - // corpse is first seen. - if (!Tutorial.tut_just_triggered - && item.base_type == OBJ_CORPSES - && !monster_at(item.pos)) - { - learned_something_new(TUT_SEEN_CARRION, item.pos); - } - return; - } - - stop_running(); - - Tutorial.tutorial_events[TUT_SEEN_FIRST_OBJECT] = false; - Tutorial.tutorial_left--; - Tutorial.tut_just_triggered = true; - - std::string text = "That "; -#ifndef USE_TILE - text += glyph_to_tagstr(get_item_glyph(&item)); - text += " "; -#else - const coord_def gc = item.pos; - tiles.place_cursor(CURSOR_TUTORIAL, gc); - tiles.add_text_tag(TAG_TUTORIAL, item.name(DESC_CAP_A), gc); -#endif - - text += "is an item. If you move there and press <w>g</w> or " - "<w>,</w> you will pick it up. " -#ifndef USE_TILE - "Generally, items are shown by non-letter symbols like " - "<w>%?!\"=()[</w>. " -#else - "You can also pick up an item by clicking on your <w>left mouse " - "button</w> while standing on its square. " -#endif - "Also, several types of objects will usually be picked up " - "automatically. " -#ifdef USE_TILE - "(In Tiles, these will be marked with a green frame around them.)" -#endif - "\nOnce it is in your inventory, you can drop it again with " -#ifdef USE_TILE - "a <w>left mouse click</w> while pressing the <w>Shift key</w>. " - "Whenever you <w>right-click</w> on an item" -#else - "<w>d</w>. Any time you look at an item in your <w>i</w>nventory" -#endif - ", you can read about its properties and its description."; - - mpr(text, MSGCH_TUTORIAL, 0); -} - -static void _new_god_conduct() -{ - std::ostringstream text; - - const std::string new_god_name = god_name(you.religion); - - text << "You've just converted to worshipping <w>" << new_god_name - << "</w>. "; - - if (you.religion == GOD_XOM) - { - // Xom is a special case. - text << "You can keep Xom happy by keeping him amused; you do " - "absolutely not want this god to grow bored with you!\n" - "If you keep Xom amused he'll treat you like a plaything, " - "randomly helping and harming you for his own amusement; " - "otherwise he'll treat you like a disfavoured plaything."; - - mpr(text.str(), MSGCH_TUTORIAL, 0); - - return; - } - - if (is_good_god(you.religion)) - { - // For the good gods, piety grows over time. - text << "From now on, " << new_god_name << " will watch over you and " - "judge your behaviour. Thus, your actions will greatly " - "influence your piety (divine favour). If your piety runs out "; - } - else - { - text << "Your piety (divine favour) will gradually decrease over time, " - "and if it runs out "; - } - - text << new_god_name << " will excommunicate you and punish you. " - "You can prevent this, however, and even gain enough piety to get " - "powers and divine gifts, by doing things to please " - << new_god_name << ". But don't panic: you start out with a decent " - "amount of piety, so any danger of excommunication is far off.\n"; - - mpr(text.str(), MSGCH_TUTORIAL, 0); - - text.str(""); - - text << "\nYou can check your god's likes and dislikes, as well as your " - "current standing and divine abilities, by typing <w>^</w>" -#ifdef USE_TILE - " (alternatively press <w>Shift</w> while " - "<w>right-clicking</w> on your avatar)" -#endif - "."; - - mpr(text.str(), MSGCH_TUTORIAL, 0); - -} - -// If the player is wielding a cursed non-slicing weapon then butchery -// isn't currently possible. -static bool _cant_butcher() -{ - const item_def *wpn = you.weapon(); - - if (!wpn || wpn->base_type != OBJ_WEAPONS) - return false; - - return (wpn->cursed() && !can_cut_meat(*wpn)); -} - -static int _num_butchery_tools() -{ - int num = 0; - - for (int i = 0; i < ENDOFPACK; ++i) - { - const item_def& tool(you.inv[i]); - - if (tool.is_valid() - && tool.base_type == OBJ_WEAPONS - && can_cut_meat( tool )) - { - num++; - } - } - - return (num); -} - -static std::string _describe_portal(const coord_def &gc) -{ - const std::string desc = feature_description(gc); - - std::ostringstream text; - - // Ziggurat entrances can rarely appear as early as DL 3. - if (desc.find("zig") != std::string::npos) - { - text << "is a portal to a set of special levels filled with very " - "tough monsters; you probably shouldn't even think of going " - "in here. Additionally, entering a ziggurat takes a lot of " - "gold, a lot more than you'd have right now; don't bother " - "saving gold up for it, since at this point your gold is " - "better spent at shops buying items which can help you " - "survive." - - "\n\nIf you <w>still</w> want to enter (and somehow have " - "gathered enough gold to do so) "; - } - // For the sake of completeness, though it's very unlikely that a - // player will find a bazaar entrance before reahing XL 7. - else if (desc.find("bazaar") != std::string::npos) - { - text << "is a portal to an inter-dimensional bazaar filled with " - "shops. It will disappear if you don't enter it soon, " - "so hurry. To enter "; - } - // The sewers can appear from DL 3 to DL 6. - else - { - text << "is a portal to a special level where you'll have to fight " - "your way back to the exit through some tougher than average " - "monsters (the monsters around the portal should give a " - "good indication as to how tough), but with the reward of " - "some good loot. There's no penalty for skipping it, but if " - "you do skip it the portal will disappear, so you have to " - "decide now if you want to risk it. To enter "; - } - - text << "stand over the portal and press <w>></w>. To return find " -#ifdef USE_TILE - "a similar looking portal tile " -#else - "another <w>\\</w> (though NOT the ancient stone arch you'll start " - "out on) " -#endif - "and press <w><<</w>."; - -#ifdef USE_TILE - text << "\nAlternatively, clicking on your <w>left mouse button</w> " - "while pressing the <w>Shift key</w> will let you enter any " - "portal you're standing on."; -#endif - - return (text.str()); -} - -#define DELAY_EVENT \ -{ \ - Tutorial.tutorial_events[seen_what] = true; \ - Tutorial.tutorial_left++; \ - return; \ -} - -// Really rare or important events should get a comment even if -// learned_something_new() was already triggered this turn. -// NOTE: If put off, the SEEN_<feature> variant will be triggered the -// next turn, so they may be rare but aren't urgent. -static bool _rare_tutorial_event(tutorial_event_type event) -{ - switch (event) - { - case TUT_FOUND_SECRET_DOOR: - case TUT_KILLED_MONSTER: - case TUT_NEW_LEVEL: - case TUT_YOU_ENCHANTED: - case TUT_YOU_SICK: - case TUT_YOU_POISON: - case TUT_YOU_ROTTING: - case TUT_YOU_CURSED: - case TUT_YOU_HUNGRY: - case TUT_YOU_STARVING: - case TUT_GLOWING: - case TUT_CAUGHT_IN_NET: - case TUT_YOU_SILENCE: - case TUT_NEED_POISON_HEALING: - case TUT_INVISIBLE_DANGER: - case TUT_NEED_HEALING_INVIS: - case TUT_ABYSS: - case TUT_RUN_AWAY: - case TUT_RETREAT_CASTER: - case TUT_YOU_MUTATED: - case TUT_NEW_ABILITY_GOD: - case TUT_NEW_ABILITY_MUT: - case TUT_NEW_ABILITY_ITEM: - case TUT_CONVERT: - case TUT_GOD_DISPLEASED: - case TUT_EXCOMMUNICATE: - case TUT_GAINED_MAGICAL_SKILL: - case TUT_GAINED_MELEE_SKILL: - case TUT_GAINED_RANGED_SKILL: - case TUT_CHOOSE_STAT: - case TUT_AUTO_EXCLUSION: - return (true); - default: - return (false); - } -} - -// Here most of the tutorial messages for various triggers are handled. -void learned_something_new(tutorial_event_type seen_what, coord_def gc) -{ - // Already learned about that. - if (!Tutorial.tutorial_events[seen_what]) - return; - - // Don't trigger twice in the same turn. - if (Tutorial.tut_just_triggered && !_rare_tutorial_event(seen_what)) - return; - - std::ostringstream text; - std::vector<command_type> cmd; - -#ifndef USE_TILE - const coord_def e = grid2show(gc); -#endif - - Tutorial.tut_just_triggered = true; - Tutorial.tutorial_events[seen_what] = false; - Tutorial.tutorial_left--; - - switch (seen_what) - { - case TUT_SEEN_POTION: - text << "You have picked up your first potion" -#ifndef USE_TILE - " ('<w>!</w>'). Use " -#else - ". Simply click on it with your <w>left mouse button</w>, or " - "press " -#endif - "<w>%</w> to quaff it.\n" - "Note that potion effects might be good or bad. For the bad " - "ones, you might want to wait until you're a bit tougher, but " - "at the same time it would be nice to know the good ones when " - "you need them. Ah, decisions, decisions..."; - cmd.push_back(CMD_QUAFF); - break; - - case TUT_SEEN_SCROLL: - text << "You have picked up your first scroll" -#ifndef USE_TILE - " ('<w>?</w>'). Type " -#else - ". Simply click on it with your <w>left mouse button</w>, or " - "type " -#endif - "<w>%</w> to read it, though you might want to wait until " - "there's a monster around or you have some more items, so the " - "scroll can actually have an effect."; - cmd.push_back(CMD_READ); - break; - - case TUT_SEEN_WAND: - text << "You have picked up your first wand" -#ifndef USE_TILE - " ('<w>/</w>'). Type " -#else - ". Simply click on it with your <w>left mouse button</w>, or " - "type " -#endif - "<w>%</w> to evoke it."; - cmd.push_back(CMD_EVOKE); - break; - - case TUT_SEEN_SPBOOK: - text << "You have picked up a book "; -#ifndef USE_TILE - text << "('<w>"; - - text << static_cast<char>(get_item_symbol(SHOW_ITEM_BOOK)) - << "'</w>) " - << "that you can read by typing <w>%</w>. " - "If it's a spellbook you'll then be able to memorise spells " - "via <w>%</w> and cast a memorised spell with <w>%</w>."; - cmd.push_back(CMD_READ); - cmd.push_back(CMD_MEMORISE_SPELL); - cmd.push_back(CMD_CAST_SPELL); -#else - text << ". You can read it doing a <w>right click</w> with your " - "mouse, and memorise spells with a <w>left click</w>. "; -#endif - - if (you.religion == GOD_TROG) - { - text << "\nAs a worshipper of " - << god_name(GOD_TROG) - << ", though, you might instead wish to burn those tomes " - "of hated magic by using the corresponding " - "<w>%</w>bility."; - cmd.push_back(CMD_USE_ABILITY); - } - else if (!you.skills[SK_SPELLCASTING]) - { - text << "\nHowever, first you will have to get accustomed to " - "spellcasting by reading lots of scrolls."; - } - text << "\nDuring the tutorial you can reread this information at " - "any time by " -#ifndef USE_TILE - "having a look in your <w>%</w>nventory at the item in " - "question."; - cmd.push_back(CMD_DISPLAY_INVENTORY); -#else - "clicking on it with your <w>right mouse button</w>."; -#endif - break; - - case TUT_SEEN_WEAPON: - text << "This is the first weapon " -#ifndef USE_TILE - "('<w>)</w>') " -#endif - "you've picked up. Use <w>%</w> " -#ifdef USE_TILE - "or click on it with your <w>left mouse button</w> " -#endif - "to wield it, but be aware that this weapon " - "might train a different skill from your current one. You can " - "view the weapon's properties from your <w>%</w>nventory" -#ifdef USE_TILE - " or by <w>right-clicking</w> on it" -#endif - "."; - - cmd.push_back(CMD_WIELD_WEAPON); - cmd.push_back(CMD_DISPLAY_INVENTORY); - - if (Tutorial.tutorial_type == TUT_BERSERK_CHAR) - { - text << "\nAs you're already trained in Axes you should stick " - "with these. Checking other axes' enchantments and " - "attributes can be worthwhile."; - } - else if (Tutorial.tutorial_type == TUT_MAGIC_CHAR) - { - text << "\nAs a spellslinger you don't need a weapon to fight. " - "However, you should still carry at least one knife, " - "dagger, sword or axe so that you can chop up corpses."; - } - break; - - case TUT_SEEN_MISSILES: - text << "This is the first stack of missiles " -#ifndef USE_TILE - "('<w>(</w>') " -#endif - "you've picked up. Missiles like darts and throwing nets " - "can be thrown by hand, but other missiles like arrows and " - "needles require a launcher and training in using it to be " - "really effective. " -#ifdef USE_TILE - "<w>Right-clicking</w> on " -#else - "Selecting " -#endif - "the item in your <w>%</w>nventory will give more " - "information about both missiles and launcher."; - - cmd.push_back(CMD_DISPLAY_INVENTORY); - - if (Tutorial.tutorial_type == TUT_RANGER_CHAR) - { - text << "\nAs you're already trained in Bows you should stick " - "with arrows and collect more of them in the dungeon."; - } - else if (Tutorial.tutorial_type == TUT_MAGIC_CHAR) - { - text << "\nHowever, as a spellslinger, you don't really need " - "another type of ranged attack, unless there's another " - "effect in addition to damage."; - } - else - { - text << "\nFor now you might be best off with sticking to darts " - "or stones for ranged attacks."; - } - break; - - case TUT_SEEN_ARMOUR: - text << "This is the first piece of armour " -#ifndef USE_TILE - "('<w>[</w>') " -#endif - "you've picked up. " -#ifdef USE_TILE - "You can click on it to wear it, and click a second time to " - "take it off again. Doing a <w>right mouse click</w> will " - "show you its properties."; -#else - "Use <w>%</w> to wear it and <w>%</w> to take it off again. " - "You can view its properties from your <w>%</w>nventory."; - cmd.push_back(CMD_WEAR_ARMOUR); - cmd.push_back(CMD_REMOVE_ARMOUR); - cmd.push_back(CMD_DISPLAY_INVENTORY); -#endif - - if (you.species == SP_CENTAUR || you.species == SP_MINOTAUR) - { - text << "\nNote that as a " << species_name(you.species, 1) - << " you will be unable to wear " - << (you.species == SP_CENTAUR ? "boots" : "helmets") - << "."; - } - break; - - case TUT_SEEN_RANDART: - text << "Weapons 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."; - break; - - case TUT_SEEN_FOOD: - text << "You have picked up some food" -#ifndef USE_TILE - " ('<w>percent</w>')" -#endif - ". You can eat it by typing <w>%</w>" -#ifdef USE_TILE - " or by clicking on it with your <w>left mouse button</w>" -#endif - ". However, it is usually best to conserve rations and fruit " - "until you are hungry or even starving."; - - cmd.push_back(CMD_EAT); - break; - - case TUT_SEEN_CARRION: - // NOTE: This is called when a corpse is first seen as well as when - // first picked up, since a new player might not think to pick - // up a corpse. - // TODO: Specialcase skeletons and rotten corpses! - - if (gc.x <= 0 || gc.y <= 0) - text << "Ah, a corpse!"; - else - { - int i = you.visible_igrd(gc); - if (i == NON_ITEM) - text << "Ah, a corpse!"; - else - { - text << "That "; -#ifndef USE_TILE - std::string glyph = glyph_to_tagstr(get_item_glyph(&mitm[i])); - const std::string::size_type found = glyph.find("%"); - if (found != std::string::npos) - glyph.replace(found, 1, "percent"); - text << glyph; - text << " "; -#else - tiles.place_cursor(CURSOR_TUTORIAL, gc); - tiles.add_text_tag(TAG_TUTORIAL, mitm[i].name(DESC_CAP_A), gc); -#endif - - text << "is a corpse."; - } - } - - text << " When a corpse is lying on the ground, you " - "can <w>%</w>hop it up with a sharp implement"; - cmd.push_back(CMD_BUTCHER); - - if (_cant_butcher()) - { - text << " (though unfortunately you can't do that right now, " - "since the cursed weapon you're wielding can't slice up " - "meat, and you can't let go of it to wield one that " - "can)"; - } - else if (_num_butchery_tools() == 0) - { - text << " (but you currently possess nothing which can do this, " - "so you should pick up the first knife, dagger, sword " - "or axe you find)"; - } - text << ". Once hungry you can then <w>%</w>at the resulting chunks " - "(though they may not be healthful)."; - cmd.push_back(CMD_EAT); - -#ifdef USE_TILE - text << " With tiles, you can also chop up any corpse that shows up in " - "the floor part of your inventory region, simply by doing a " - "<w>left mouse click</w> while pressing <w>Shift</w>, and " - "then eat the resulting chunks with <w>Shift + right mouse " - "click</w>."; -#endif - - if (god_likes_fresh_corpses(you.religion)) - { - text << "\nYou can also offer corpses to " - << god_name(you.religion) - << " by <w>%</w>raying over them. Note that the gods will not " - "accept rotting flesh."; - cmd.push_back(CMD_PRAY); - } - text << "\nDuring the tutorial you can reread this information at " - "any time by selecting the item in question in your " - "<w>%</w>nventory."; - cmd.push_back(CMD_DISPLAY_INVENTORY); - break; - - case TUT_SEEN_JEWELLERY: - text << "You have picked up a a piece of jewellery, either a ring" -#ifndef USE_TILE - << " ('<w>=</w>')" -#endif - << " or an amulet" -#ifndef USE_TILE - << " ('<w>\"</w>')" - << ". Type <w>%</w> to put it on and <w>%</w> to remove " - "it. You can view its properties from your <w>%</w>nventory" -#else - << ". You can click on it to put it on, and click a second time " - "remove it again. By clicking on it with your <w>right mouse " - "button</w> you can view its properties" -#endif - << ", though often magic is necessary to reveal its true " - "nature."; - cmd.push_back(CMD_WEAR_JEWELLERY); - cmd.push_back(CMD_REMOVE_JEWELLERY); - cmd.push_back(CMD_DISPLAY_INVENTORY); - break; - - case TUT_SEEN_MISC: - text << "This is a curious object indeed. You can play around with " - "it to find out what it does by " -#ifdef USE_TILE - "clicking on it to e<w>%</w>oke " -#else - "e<w>%</w>oking " -#endif - "it. Some items need to be <w>%</w>ielded first before you can " - "e<w>%</w>oke them. As usual, selecting it from your " - "<w>%</w>nventory might give you more information."; - cmd.push_back(CMD_EVOKE); - cmd.push_back(CMD_WIELD_WEAPON); - cmd.push_back(CMD_EVOKE_WIELDED); - cmd.push_back(CMD_DISPLAY_INVENTORY); - break; - - case TUT_SEEN_STAFF: - text << "You have picked up a magic staff or a rod" -#ifndef USE_TILE - ", both of which are represented by '<w>"; - - text << static_cast<char>(get_item_symbol(SHOW_ITEM_STAVE)) - << "</w>'" -#endif - ". Both must be <w>%</w>ielded to be of use. " - "Magicians use staves to increase their power in certain " - "spell schools. By contrast, a rod allows the casting of " - "certain spells even without magic knowledge simply by " - "e<w>%</w>oking it. For the latter the power depends on " - "your Evocations skill."; - cmd.push_back(CMD_WIELD_WEAPON); - cmd.push_back(CMD_EVOKE_WIELDED); - -#ifdef USE_TILE - text << " Both wielding and evoking a wielded item can be achieved " - "by clicking on it with your <w>left mouse button</w>."; -#endif - text << "\nDuring the tutorial you can reread this information at " - "any time by selecting the item in question in your " - "<w>%</w>nventory."; - cmd.push_back(CMD_DISPLAY_INVENTORY); - break; - - case TUT_SEEN_GOLD: - text << "You have picked up your first pile of gold" -#ifndef USE_TILE - " ('<yellow>$</yellow>')" -#endif - ". Unlike all other objects in Crawl it doesn't show up in " - "your inventory, takes up no space in your inventory, weighs " - "nothing and can't be dropped. Gold can be used to buy " - "items from shops, and can also be sacrificed to some gods. "; - - if (!Options.show_gold_turns) - { - text << "Whenever you pick up some gold, your current amount will " - "be mentioned. If you'd like to check your wealth at other " - "times, you can press <w>%</w>. It will also be " - "listed on the <w>%</w> screen."; - cmd.push_back(CMD_LIST_GOLD); - cmd.push_back(CMD_RESISTS_SCREEN); - } - break; - - case TUT_SEEN_STAIRS: - // Don't give this information during the first turn, to give - // the player time to have a look around. - if (you.num_turns < 1) - DELAY_EVENT; - - text << "These "; -#ifndef USE_TILE - // Is a monster blocking the view? - if (monster_at(gc)) - DELAY_EVENT; - - text << glyph_to_tagstr(get_show_glyph(env.show(e))) << " "; -#else - tiles.place_cursor(CURSOR_TUTORIAL, gc); - tiles.add_text_tag(TAG_TUTORIAL, "Stairs", gc); -#endif - text << "are some downstairs. You can enter the next (deeper) " - "level by following them down (<w>%</w>). To get back to " - "this level again, press <w>%</w> while standing on the " - "upstairs."; - cmd.push_back(CMD_GO_DOWNSTAIRS); - cmd.push_back(CMD_GO_UPSTAIRS); - -#ifdef USE_TILE - text << "\nAlternatively, clicking on your <w>left mouse button</w> " - "while pressing the <w>Shift key</w> will let you follow any " - "stairs you're standing on."; -#endif - break; - - case TUT_SEEN_ESCAPE_HATCH: - if (you.num_turns < 1) - DELAY_EVENT; - - // monsters standing on stairs - if (monster_at(gc)) - DELAY_EVENT; - - text << "These "; -#ifndef USE_TILE - text << glyph_to_tagstr(get_show_glyph(env.show(e))); - text << " "; -#else - tiles.place_cursor(CURSOR_TUTORIAL, gc); - tiles.add_text_tag(TAG_TUTORIAL, "Escape hatch", gc); -#endif - text << "are some kind of escape hatch. You can use them to " - "quickly leave a level with <w>%</w> and <w>%</w>, " - "respectively " -#ifdef USE_TILE - "(or by using your <w>left mouse button</w> in combination " - "with the <w>Shift key</w>)" -#endif - ", but will usually be unable to return right away."; - cmd.push_back(CMD_GO_UPSTAIRS); - cmd.push_back(CMD_GO_DOWNSTAIRS); - break; - - case TUT_SEEN_BRANCH: - text << "This "; -#ifndef USE_TILE - // Is a monster blocking the view? - if (monster_at(gc)) - DELAY_EVENT; - - // FIXME: Branch entrance character is not being colored yellow. - text << glyph_to_tagstr(get_show_glyph(env.show(e))) << " "; -#else - tiles.place_cursor(CURSOR_TUTORIAL, gc); - tiles.add_text_tag(TAG_TUTORIAL, "Branch stairs", gc); -#endif - text << "is the entrance to a different branch of the dungeon, " - "which might have different terrain, level layout and " - "monsters from the current main branch you're in. Branches " - "can range from being up to ten levels deep to having only " - "a single level. They can also contain entrances to other " - "branches." - - "\n\nThe first three branches you'll encounter are the " - "Temple, the Orcish Mines and the Lair. While the Mines " - "and the Lair can be dangerous for the new adventurer, " - "the Temple is completely safe and contains a number of " - "altars at which you might convert to a new god."; - break; - - case TUT_SEEN_PORTAL: - // Delay in the unlikely event that a player still in tutorial mode - // creates a portal with a Trowel card, since a portal vault - // entry's description doesn't seem to get set properly until - // after the vault is done being placed. - if (you.pos() == gc) - DELAY_EVENT; - - text << "This "; -#ifndef USE_TILE - // Is a monster blocking the view? - if (monster_at(gc)) - DELAY_EVENT; - - text << glyph_to_tagstr(get_show_glyph(env.show(e))) << " "; -#else - tiles.place_cursor(CURSOR_TUTORIAL, gc); - tiles.add_text_tag(TAG_TUTORIAL, "Portal", gc); -#endif - text << _describe_portal(gc); - break; - - case TUT_STAIR_BRAND: - // Monster or player standing on stairs. - if (actor_at(gc)) - DELAY_EVENT; - -#ifdef USE_TILE - text << "A small question mark on a stair tile signifies that there " - "are items in that position that you may want to check out."; -#else - text << "If any items are covering stairs or an escape hatch, then " - "that will be indicated by highlighting the <w><<</w> or " - "<w>></w> symbol, instead of hiding the stair symbol with " - "an item glyph."; -#endif - break; - - case TUT_HEAP_BRAND: - // Monster or player standing on heap. - if (actor_at(gc)) - DELAY_EVENT; - -#ifdef USE_TILE - text << "A small question mark on an item tile signifies that there " - "is at least one other item in the same heap that you may want " - "to check out."; - break; -#else - text << "If two or more items are on a single square, then the square " - "will be highlighted, and the symbol for the item on the top " - "of the heap will be shown."; -#endif - break; - - case TUT_TRAP_BRAND: -#ifdef USE_TILE - // Tiles show both the trap and the item heap. - return; -#else - // Monster or player standing on trap. - if (actor_at(gc)) - DELAY_EVENT; - - text << "If any items are covering a trap, then that will be " - "indicated by highlighting the <w>^</w> symbol, instead of " - "hiding the trap symbol with an item glyph."; -#endif - break; - - case TUT_SEEN_TRAP: - if (you.pos() == gc) - text << "Oops... you just triggered a trap. "; - else - text << "You just discovered a trap. "; - - text << "An unwary adventurer will occasionally stumble into one " - "of these nasty constructions"; -#ifndef USE_TILE - { - glyph g = get_show_glyph(env.show(e)); - - if (g.ch == ' ' || g.col == BLACK) - g.col = LIGHTCYAN; - - text << " depicted by " << _colourize_glyph(g.col, '^'); - } -#endif - text << ". They can do physical damage (with darts or needles, for " - "example) or have other, more magical effects, like " - "teleportation. The mechanical variant can be disarmed with " - "<w>Ctrl + direction</w> " -#ifdef USE_TILE - "or with <w>Ctrl + leftclick</w> " -#endif - "while standing next to it."; - break; - - case TUT_SEEN_ALTAR: - text << "That "; -#ifndef USE_TILE - text << glyph_to_tagstr(get_show_glyph(env.show(e))) << " "; -#else - { - tiles.place_cursor(CURSOR_TUTORIAL, gc); - std::string altar = "An altar to "; - altar += god_name(feat_altar_god(grd(gc))); - tiles.add_text_tag(TAG_TUTORIAL, altar, gc); - } -#endif - text << "is an altar. " -#ifdef USE_TILE - "By <w>rightclicking</w> on it with your mouse " -#else - "If you target the altar with <w>x</w>, then press <w>v</w> " -#endif - "you can get a short description.\n" - "Press <w>%</w> while standing on the square to join the faith " - "or read some information about the god in question. Before " - "taking up the corresponding faith you'll be asked for " - "confirmation."; - cmd.push_back(CMD_PRAY); - - if (you.religion == GOD_NO_GOD - && Tutorial.tutorial_type == TUT_MAGIC_CHAR) - { - text << "\n\nThe best god for an unexperienced conjurer is " - "probably Vehumet, though Sif Muna is a good second " - "choice."; - } - break; - - case TUT_SEEN_SHOP: -#ifdef USE_TILE - tiles.place_cursor(CURSOR_TUTORIAL, gc); - tiles.add_text_tag(TAG_TUTORIAL, shop_name(gc), gc); -#endif - text << "That " -#ifndef USE_TILE - << _colourize_glyph(YELLOW, get_screen_glyph(gc)) << " " -#endif - "is a shop. You can enter it by typing <w>%</w> or <w>%</w>" -#ifdef USE_TILE - ", or by pressing <w>Shift</w> and clicking on it with your " - "<w>left mouse button</w> " -#endif - "while standing on the square."; - cmd.push_back(CMD_GO_UPSTAIRS); - cmd.push_back(CMD_GO_DOWNSTAIRS); - - text << "\n\nIf there's anything you want which you can't afford yet " - "you can select those items and press <w>@</w> to put them " - "on your shopping list. The game will then remind you when " - "you gather enough gold to buy the items on your list."; - break; - - case TUT_SEEN_DOOR: - if (you.num_turns < 1) - DELAY_EVENT; - -#ifdef USE_TILE - tiles.place_cursor(CURSOR_TUTORIAL, gc); - tiles.add_text_tag(TAG_TUTORIAL, "Closed door", gc); -#endif - - text << "That " -#ifndef USE_TILE - << _colourize_glyph(WHITE, get_screen_glyph(gc)) << " " -#endif - "is a closed door. You can open it by walking into it. " - "Sometimes it is useful to close a door. Do so by pressing " - "<w>%</w> while standing next to it. If there are several " - "doors, you will then be prompted for a direction. " - "Alternatively, you can also use <w>Ctrl-Direction</w>."; - cmd.push_back(CMD_CLOSE_DOOR); - -#ifdef USE_TILE - text << "\nIn Tiles, the same can be achieved by clicking on an " - "adjacent door square."; -#endif - if (!Tutorial.tut_explored) - { - text << "\nTo avoid accidentally opening a door you'd rather " - "remain closed during travel or autoexplore, you can mark " - "it with an exclusion from the map view (<w>%</w>) with " - "<w>ee</w> while your cursor is on the grid in question. " - "Such an exclusion will prevent autotravel from ever " - "entering that grid until you remove the exclusion with " - "another press of <w>%e</w>."; - cmd.push_back(CMD_DISPLAY_MAP); - cmd.push_back(CMD_DISPLAY_MAP); - } - break; - - case TUT_FOUND_SECRET_DOOR: -#ifdef USE_TILE - tiles.place_cursor(CURSOR_TUTORIAL, gc); - tiles.add_text_tag(TAG_TUTORIAL, "Secret door", gc); -#endif - text << "That "; -#ifndef USE_TILE - text << _colourize_glyph(WHITE, get_screen_glyph(gc)) << " "; -#endif - if (grd(gc) == DNGN_SECRET_DOOR) - text << "is"; - else - text << "was"; - - text << " a secret door. You can actively try to find secret doors " - "by searching. To search for one turn, press <w>s</w>, " - "<w>.</w>, <w>delete</w> or <w>keypad-5</w>. Pressing " - "<w>5</w> or <w>shift-and-keypad-5</w> " -#ifdef USE_TILE - ", or clicking into the stat area " -#endif - "will search 100 times, stopping early if you find any " - "secret doors or traps, or when your HP or MP fully " - "recovers.\n\n" - - "If you can't find all three (or any) of the down stairs " - "on a level, you should try searching for secret doors, since " - "the missing stairs might be in sections of the level blocked " - "off by them. If you really can't find any secret doors, then " - "the missing stairs are probably in sections of the level " - "totally disconnected from the section you're searching."; - break; - - case TUT_KILLED_MONSTER: - text << "Congratulations, your character just gained some experience " - "by killing this monster! Every action will use up some of " - "it to train certain skills. For example, fighting monsters "; - - if (Tutorial.tutorial_type == TUT_BERSERK_CHAR) - { - text << "in melee battle will raise your Axes and Fighting " - "skills."; - } - else if (Tutorial.tutorial_type == TUT_RANGER_CHAR) - text << "using bow and arrows will raise your Bows skill."; - else // if (Tutorial.tutorial_type == TUT_MAGIC_CHAR) - { - text << "with offensive magic will raise your Conjurations and " - "Spellcasting skills."; - } - - if (you.religion == GOD_TROG) - { - text << " Also, kills of living creatures are automatically " - "dedicated to " - << god_name(you.religion) - << "."; - } - break; - - case TUT_NEW_LEVEL: - text << "Well done! Reaching a new experience level is always a nice " - "event: you get more health and magic points, and " - "occasionally increases to your attributes (strength, " - "dexterity, intelligence)."; - - if (Tutorial.tutorial_type == TUT_MAGIC_CHAR) - { - text << "\nAlso, new experience levels let you learn more spells " - "(the Spellcasting skill also does this). For now, you " - "should try to memorise the second spell of your " - "starting book with <w>%a</w>, which can then be zapped " - "with <w>%b</w>."; -#ifdef USE_TILE - text << " Memorising is also possible by doing a <w>left " - "click</w> on the book in your inventory, or by clicking " - "on the <w>spellbook tab</w> to the left of your " - "inventory."; -#endif - cmd.push_back(CMD_MEMORISE_SPELL); - cmd.push_back(CMD_CAST_SPELL); - } - break; - - case TUT_SKILL_RAISE: - text << "One of your skills just got raised. You can train your skills " - "or pick up new ones by performing the corresponding actions. " - "To view or manage your skill set, type <w>%</w>."; - cmd.push_back(CMD_DISPLAY_SKILLS); - break; - - case TUT_GAINED_MAGICAL_SKILL: - text << "Being skilled in a magical \"school\" makes it easier to " - "learn and cast spells of this school. Many spells belong to " - "a combination of several schools, in which case the average " - "skill in these schools will decide on spellcasting success " - "and power."; - break; - - case TUT_GAINED_MELEE_SKILL: - text << "Being skilled with a particular type of weapon will make it " - "easier to fight with all weapons of this type, and make you " - "deal more damage with them. It is generally recommended to " - "concentrate your efforts on one or two weapon types to become " - "more powerful in them. Some weapons are closely related, and " - "being trained in one will ease training the other. This is " - "true for the following pairs: Short Blades/Long Blades, " - "Axes/Polearms, Polearms/Staves, and Axes/Maces."; - break; - - case TUT_GAINED_RANGED_SKILL: - text << "Being skilled in a particular type of ranged attack will let " - "you deal more damage when using the appropriate weapons. It " - "is usually best to concentrate on one type of ranged attack " - "(including spells), and to add another one as back-up."; - break; - - case TUT_CHOOSE_STAT: - text << "Every third level you may choose what stat to invest in, " - "Strength, Dexterity, or Intelligence. <w>Strength</w> " - "influences the amount you can carry, and increases the damage " - "you deal in melee. <w>Dexterity</w> increases your evasion " - "and thus influences your chance of dodging attacks or traps. " - "<w>Intelligence</w> increases your success in casting spells " - "and decreases the amount by which you hunger when you do so.\n" - "Note that it is generally recommended to raise all your " - "stats to a minimum of 8, so as to prevent death by stat loss."; - break; - - case TUT_YOU_ENCHANTED: - text << "Enchantments of all types can befall you temporarily. " - "Brief descriptions of these appear at the lower end of the " - "stats area. Press <w>%</w> for more details. A list of all " - "possible enchantments is in the manual (<w>%5</w>)."; - cmd.push_back(CMD_DISPLAY_CHARACTER_STATUS); - cmd.push_back(CMD_DISPLAY_COMMANDS); - break; - - case TUT_YOU_SICK: - // Hack: reset tut_just_triggered, to force recursive calling of - // learned_something_new(). - Tutorial.tut_just_triggered = false; - learned_something_new(TUT_YOU_ENCHANTED); - Tutorial.tut_just_triggered = true; - text << "Chunks and corpses that are described as " - "<brown>contaminated</brown> can make you sick when eating " - "them. However, there's only a chance of this happening and " - "food is sufficiently rare in the dungeon that you'll " - "sometimes have to dine on them anyway and hope for the best.\n" - "While sick, your hitpoints won't regenerate and sometimes " - "an attribute may decrease. It wears off with time ("; - - if (!i_feel_safe()) - text << "find a quiet corner and "; - - text << "wait with <w>%</w>" -#ifdef USE_TILE - " or by clicking into the stats area" -#endif - "), or you could <w>%</w>uaff a potion of healing.\n" - "Note that if a chunk makes you sick, you won't get any " - "nutrition out of it."; - - cmd.push_back(CMD_REST); - cmd.push_back(CMD_QUAFF); - break; - - case TUT_YOU_POISON: - // Hack: reset tut_just_triggered, to force recursive calling of - // learned_something_new(). - Tutorial.tut_just_triggered = false; - learned_something_new(TUT_YOU_ENCHANTED); - Tutorial.tut_just_triggered = true; - text << "Poison will slowly reduce your HP. It wears off with time ("; - - if (!i_feel_safe()) - text << "find a quiet corner and "; - - text << "wait with <w>%</w>" -#ifdef USE_TILE - "or by clicking onto the stats area" -#endif - "), or you could <w>%</w>uaff a potion of healing. "; - cmd.push_back(CMD_REST); - cmd.push_back(CMD_QUAFF); - break; - - case TUT_YOU_ROTTING: - // Hack: Reset tut_just_triggered, to force recursive calling of - // learned_something_new(). - Tutorial.tut_just_triggered = false; - learned_something_new(TUT_YOU_ENCHANTED); - Tutorial.tut_just_triggered = true; - - text << "Ugh, your flesh is rotting! Not only does this slowly " - "reduce your HP, it also slowly reduces your <w>maximum</w> " - "HP (your usual maximum HP will be indicated by a number in " - "parentheses).\n" - "While you can wait it out, you'll probably want to stop " - "rotting as soon as possible by <w>%</w>uaffing a potion of " - "healing, since the longer you wait the more your maximum HP " - "will be reduced. Once you've stopped rotting you can restore " - "your maximum HP to normal by drinking potions of healing and " - "heal wounds while fully healed."; - cmd.push_back(CMD_QUAFF); - break; - - case TUT_YOU_CURSED: - text << "Curses are comparatively harmless, but they do mean that " - "you cannot remove cursed equipment and will have to suffer " - "the (possibly) bad effects until you find and read a scroll " - "of remove curse (though if you're wielding a cursed " - "non-slicing weapon, you'll be unable to <w>%</w>hop up " - "corpses into chunks). Weapons and armour can also be " - "uncursed using the appropriate enchantment scrolls."; - cmd.push_back(CMD_BUTCHER); - break; - - case TUT_YOU_HUNGRY: - text << "There are two ways to overcome hunger: food you started " - "with or found, and self-made chunks from corpses. To get the " - "latter, all you need to do is <w>%</w>hop up a corpse " - "with a sharp implement. "; - cmd.push_back(CMD_BUTCHER); - - if (_cant_butcher()) - { - text << "Unfortunately you can't butcher corpses right now, " - "since the cursed weapon you're wielding can't slice up " - "meat, and you can't let go of it to wield one that " - "can."; - } - else - { - const int num = _num_butchery_tools(); - if (num == 0) - { - text << "However, you currently possess no sharp implements, " - "so you should pick up the first knife, dagger, sword " - "or axe you find. "; - } - else if (Tutorial.tutorial_type != TUT_MAGIC_CHAR) - text << "Your starting weapon will do nicely. "; - else if (num == 1) - text << "The slicing weapon you picked up will do nicely. "; - else - { - text << "One of the slicing weapons you picked up will do " - "nicely. "; - } - } - - text << "Try to dine on chunks in order to save permanent food."; - - if (Tutorial.tutorial_type == TUT_BERSERK_CHAR) - text << "\nNote that you cannot Berserk while hungry or worse."; - break; - - case TUT_YOU_STARVING: - text << "You are now suffering from terrible hunger. You'll need to " - "<w>%</w>at something quickly, or you'll die. The safest " - "way to deal with this is to simply eat something from your " - "inventory, rather than wait for a monster to leave a corpse. " - "In a pinch, potions and fountains also can provide some " - "nutrition, though not as much as food."; - cmd.push_back(CMD_EAT); - - if (Tutorial.tutorial_type == TUT_MAGIC_CHAR) - text << "\nNote that you cannot cast spells while starving."; - break; - - case TUT_MULTI_PICKUP: - text << "There's a more comfortable way to pick up several items at " - "the same time. If you press <w>%</w> or <w>g</w> " -#ifdef USE_TILE - ", or click your left mouse button " -#endif - "twice you can choose items from a menu" -#ifdef USE_TILE - ", either by pressing their letter, or by clicking the " - "corresponding lines in the menu" -#endif - ".\nThis takes fewer keystrokes but has no influence on the " - "number of turns needed. Multi-pickup will be interrupted by " - "monsters or other dangerous events."; - cmd.push_back(CMD_PICKUP); - break; - - case TUT_HEAVY_LOAD: - if (you.burden_state != BS_UNENCUMBERED) - { - text << "It is not usually a good idea to run around encumbered; " - "it slows you down and increases your hunger."; - } - else - { - text << "Sadly, your inventory is limited to 52 items, and it " - "appears your knapsack is full."; - } - - text << " However, this is easy enough to rectify: simply " - "<w>%</w>rop some of the stuff you don't need or that's too " - "heavy to lug around permanently."; - cmd.push_back(CMD_DROP); - -#ifdef USE_TILE - text << " In the drop menu you can then comfortably select which " - "items to drop by pressing their inventory letter, or by " - "clicking on them."; -#endif - - if (Tutorial.tut_stashes) - { - text << "\n\nYou can easily find items you've left on the floor " - "with the <w>%</w> command, which will let you " - "seach for all known items in the dungeon. For example, " - "<w>% \"knife\"</w> will list all knives. You can " - "can then travel to one of the spots."; - Tutorial.tut_stashes = false; - cmd.push_back(CMD_SEARCH_STASHES); - cmd.push_back(CMD_SEARCH_STASHES); - } - - text << "\n\nBe warned that items that you leave on the floor can " - "be picked up and used by monsters."; - break; - - case TUT_ROTTEN_FOOD: - text << "One or more of the chunks or corpses you carry has started " - "to rot. Few species can digest these safely, so you might " - "just as well <w>%</w>rop them now. When selecting items from " - "a menu, there's a shortcut (<w>&</w>) to select all corpses, " - "skeletons, and rotten chunks in your inventory at once."; - cmd.push_back(CMD_DROP); - break; - - case TUT_MAKE_CHUNKS: - text << "How lucky! That monster left a corpse which you can now " - "<w>%</w>hop up"; - cmd.push_back(CMD_BUTCHER); - - if (_cant_butcher()) - { - text << "(or which you <w>could</w> chop up if it weren't for " - "the fact that you can't let go of your cursed " - "non-chopping weapon)"; - } - else if (_num_butchery_tools() == 0) - { - text << "(or which you <w>could</w> chop up if you had a " - "chopping weapon; you should pick up the first knife, " - "dagger, sword or axe you find if you want to be able " - "to chop up corpses)"; - } - text << ". One or more chunks will appear that you can then " - "<w>%</w>at. Beware that some chunks may be, sometimes or " - "always, hazardous. You can find out whether that might be the " - "case by "; - cmd.push_back(CMD_EAT); - -#ifdef USE_TILE - text << "hovering your mouse over the corpse or chunk."; -#else - text << "<w>v</w>iewing a corpse or chunk on the floor or by having a " - "look at it in your <w>%</w>nventory."; - cmd.push_back(CMD_DISPLAY_INVENTORY); -#endif - break; - - case TUT_OFFER_CORPSE: - if (!god_likes_fresh_corpses(you.religion) - || you.hunger_state < HS_SATIATED) - { - return; - } - - text << "Hey, that monster left a corpse! If you don't need it for " - "food or other purposes, you can sacrifice it to " - << god_name(you.religion) - << " by <w>%</w>raying over it to offer it. "; - cmd.push_back(CMD_PRAY); - break; - - case TUT_SHIFT_RUN: - text << "Walking around takes fewer keystrokes if you press " - "<w>Shift-direction</w> or <w>/ direction</w>. " - "That will let you run until a monster comes into sight or " - "your character sees something interesting."; - break; - - case TUT_MAP_VIEW: - text << "As you explore a level, orientation can become difficult. " - "Press <w>%</w> to bring up the level map. Typing <w>?</w> " - "shows the list of level map commands. " - "Most importantly, moving the cursor to a spot and pressing " - "<w>.</w> or <w>Enter</w> lets your character move there on " - "its own."; - cmd.push_back(CMD_DISPLAY_MAP); - -#ifdef USE_TILE - text << "\nAlso, clicking on the right-side minimap with your " - "<w>right mouse button</w> will zoom into that dungeon area. " - "Clicking with the <w>left mouse button</w> instead will let " - "you move there."; -#endif - break; - - case TUT_AUTO_EXPLORE: - if (!Tutorial.tut_explored) - return; - - text << "Fully exploring a level and picking up all the interesting " - "looking items can be tedious. To save on this tedium you " - "can press <w>%</w> to auto-explore, which will " - "automatically explore unmapped regions, automatically pick " - "up interesting items, and stop if a monster or interesting " - "dungeon feature (stairs, altar, etc.) is encountered."; - cmd.push_back(CMD_EXPLORE); - Tutorial.tut_explored = false; - break; - - case TUT_DONE_EXPLORE: - // XXX: You'll only get this message if you're using auto exploration. - text << "Hey, you've finished exploring the dungeon on this level! " - "You can search for stairs from the level map (<w>%</w>) " - "by pressing <w>></w>. The cursor will jump to the nearest " - "staircase, and by pressing <w>.</w> or <w>Enter</w> your " - "character can move there, too. "; - cmd.push_back(CMD_DISPLAY_MAP); - - if (Tutorial.tutorial_events[TUT_SEEN_STAIRS]) - { - text << "In rare cases, you may have found no downstairs at all. " - "Try searching for secret doors in suspicious looking " - "spots; use <w>s</w>, <w>.</w> or for 100 turns with " - "<w>%</w> " -#ifdef USE_TILE - "(or alternatively click into the stat area) " -#endif - "to do so."; - } - else - { - text << "Each level of Crawl has three " -#ifndef USE_TILE - "white " -#endif - "up and three " -#ifndef USE_TILE - "white " -#endif - "down stairs. Unexplored parts can often be accessed via " - "another level or through secret doors. To find the " - "latter, search the adjacent squares of walls for one " - "turn with <w>.</w> or <w>s</w>, or for 100 turns with " - "<w>%</w> or <w>Shift-numpad 5</w>" -#ifdef USE_TILE - ", or by clicking on the stat area" -#endif - ".\n\n"; - } - cmd.push_back(CMD_REST); - break; - - case TUT_AUTO_EXCLUSION: - // In the highly unlikely case the player encounters a - // hostile statue or oklob plant during the tutorial... - if (Tutorial.tut_explored) - { - // Hack: Reset tut_just_triggered, to force recursive calling of - // learned_something_new(). - Tutorial.tut_just_triggered = false; - learned_something_new(TUT_AUTO_EXPLORE); - Tutorial.tut_just_triggered = true; - } - text << "\nTo prevent autotravel or autoexplore taking you into " - "dangerous territory, you can set travel exclusions by " - "entering the map view (<w>%</w>) and then toggling the " - "exclusion radius on the monster position with <w>e</w>. " - "To make this easier some immobile monsters listed in the " - "<w>auto_exclude</w> option (such as this one) are considered " - "dangerous enough to warrant an automatic setting of an " - "exclusion. It will be automatically cleared if you manage to " - "kill the monster. You could also manually remove the " - "exclusion with <w>%ee</w> but unless you remove this monster " - "from the auto_exclude list, the exclusion will be reset the " - "next turn."; - cmd.push_back(CMD_DISPLAY_MAP); - cmd.push_back(CMD_DISPLAY_MAP); - break; - - case TUT_NEED_HEALING: - text << "If you're low on hitpoints or magic and there's no urgent " - "need to move, you can rest for a bit. Ideally, you should " - "retreat to an area you've already explored and cleared " - "of monsters before resting, since resting on the edge of " - "the explored terrain increases the risk of rest being " - "interrupted by a wandering monster. Press <w>%</w> or " - "<w>shift-numpad-5</w>" -#ifdef USE_TILE - ", or click into the stat area" -#endif - " to do so."; - cmd.push_back(CMD_REST); - break; - - case TUT_NEED_POISON_HEALING: - text << "Your poisoning could easily kill you, so now would be a " - "good time to <w>%</w>uaff a potion of heal wounds or, " - "better yet, a potion of healing. If you have seen neither " - "of these so far, try unknown ones in your inventory. Good " - "luck!"; - cmd.push_back(CMD_QUAFF); - break; - - case TUT_INVISIBLE_DANGER: - text << "Fighting against a monster you cannot see is difficult. " - "Your best bet is probably a strategic retreat, be it via " - "teleportation or by getting off the level. " - "Or else, luring the monster into a corridor should at least " - "make it easier for you to hit it."; - - // To prevent this text being immediately followed by the next one... - Tutorial.tut_last_healed = you.num_turns - 30; - break; - - case TUT_NEED_HEALING_INVIS: - text << "You recently noticed an invisible monster, so unless you " - "killed it or left the scene resting might not be safe. If you " - "still need to replenish your hitpoints or magic, you'll have " - "to quaff an appropriate potion. For normal resting you will " - "first have to get away from the danger."; - - Tutorial.tut_last_healed = you.num_turns; - break; - - case TUT_CAN_BERSERK: - // Don't print this information if the player already knows it. - if (Tutorial.tut_berserk_counter) - return; - - text << "Against particularly difficult foes, you should use your " - "Berserk <w>%</w>bility. Berserk will last longer if you " - "kill a lot of monsters."; - cmd.push_back(CMD_USE_ABILITY); - break; - - case TUT_POSTBERSERK: - text << "Berserking is extremely exhausting! It burns a lot of " - "nutrition, and afterwards you are slowed down and " - "occasionally even pass out. Press <w>%</w> to see your " - "current exhaustion status."; - cmd.push_back(CMD_DISPLAY_CHARACTER_STATUS); - break; - - case TUT_RUN_AWAY: - text << "Whenever you've got only a few hitpoints left and you're in " - "danger of dying, check your options carefully. Often, " - "retreat or use of some item might be a viable alternative " - "to fighting on."; - - if (you.species == SP_CENTAUR) - { - text << " As a four-legged centaur you are particularly quick - " - "running is an option!"; - } - - text << " If retreating to another level, keep in mind that monsters " - "may follow you if they're standing right next to you when " - "you start climbing or descending the stairs. And even if " - "you've managed to shake them off, they'll still be there when " - "you come back, so you might want to use a different set of " - "stairs when you return."; - - if (you.religion == GOD_TROG && !you.berserk() - && !you.duration[DUR_EXHAUSTED] - && you.hunger_state >= HS_SATIATED) - { - text << "\nAlso, with " - << apostrophise(god_name(you.religion)) - << " support you can use your Berserk ability (<w>%</w>) " - "to temporarily gain more hitpoints and greater " - "strength. "; - cmd.push_back(CMD_USE_ABILITY); - } - break; - - case TUT_RETREAT_CASTER: - text << "Without magical power you're unable to cast spells. While " - "melee is a possibility, that's not where your strengths " - "lie, so retreat (if possible) might be the better option."; - - if (_advise_use_wand()) - { - text << "\n\nOr you could e<w>%</w>oke a wand to deal damage."; - cmd.push_back(CMD_EVOKE); - } - break; - - case TUT_YOU_MUTATED: - text << "Mutations can be obtained from several sources, among them " - "potions, spell miscasts, and overuse of strong enchantments " - "like invisibility. The only reliable way to get rid of " - "mutations is with potions of cure mutation. There are about " - "as many harmful as beneficial mutations, and most of them " - "have three levels. Check your mutations with <w>%</w>."; - cmd.push_back(CMD_DISPLAY_MUTATIONS); - break; - - case TUT_NEW_ABILITY_GOD: - switch (you.religion) - { - // Gods where first granted ability is active. - case GOD_KIKUBAAQUDGHA: case GOD_YREDELEMNUL: case GOD_NEMELEX_XOBEH: - case GOD_ZIN: case GOD_OKAWARU: case GOD_SIF_MUNA: - case GOD_TROG: case GOD_ELYVILON: case GOD_LUGONU: - text << "You just gained a divine ability. Press <w>%</w> to " - "take a look at your abilities or to use one of them."; - cmd.push_back(CMD_USE_ABILITY); - break; - - // Gods where first granted ability is passive. - default: - text << "You just gained a divine ability. Press <w>%</w> " -#ifdef USE_TILE - "or press <w>Shift</w> and <w>right-click</w> on the " - "player tile " -#endif - "to take a look at your abilities."; - cmd.push_back(CMD_DISPLAY_RELIGION); - break; - } - break; - - case TUT_NEW_ABILITY_MUT: - text << "That mutation granted you a new ability. Press <w>%</w> to " - "take a look at your abilities or to use one of them."; - cmd.push_back(CMD_USE_ABILITY); - break; - - case TUT_NEW_ABILITY_ITEM: - text << "That item you just equipped granted you a new ability " - "(un-equipping the item will remove the ability). " - "Press <w>%</w> to take a look at your abilities or to " - "use one of them."; - cmd.push_back(CMD_USE_ABILITY); - break; - - case TUT_CONVERT: - _new_god_conduct(); - break; - - case TUT_GOD_DISPLEASED: - text << "Uh-oh, " << god_name(you.religion) << " is growing " - "displeased because your piety is running low. Possibly this " - "is the case because you're committing heretic acts"; - - if (!is_good_god(you.religion)) - { - // Piety decreases over time for non-good gods. - text << ", because " << god_name(you.religion) << " finds your " - "worship lacking, or a combination of the two"; - } - text << ". If your piety goes to zero, then you'll be excommunicated. " - "Better get cracking on raising your piety, and/or stop " - "annoying your god. "; - - text << "In any case, you'd better reread the religious description. " - "To do so, type <w>%</w>" -#ifdef USE_TILE - " or press <w>Shift</w> and <w>right-click</w> on your avatar" -#endif - "."; - cmd.push_back(CMD_DISPLAY_RELIGION); - break; - - case TUT_EXCOMMUNICATE: - { - const god_type new_god = (god_type) gc.x; - const int old_piety = gc.y; - - god_type old_god = GOD_NO_GOD; - for (int i = 0; i < MAX_NUM_GODS; i++) - if (you.worshipped[i] > 0) - { - old_god = (god_type) i; - break; - } - - const std::string old_god_name = god_name(old_god); - const std::string new_god_name = god_name(new_god); - - if (new_god == GOD_NO_GOD) - { - if (old_piety < 1) - { - text << "Uh-oh, " << old_god_name << " just excommunicated you " - "for running out of piety (your divine favour went " - "to nothing). Maybe you repeatedly violated the " - "religious rules, or maybe you failed to please your " - "deity often enough, or some combination of the two. " - "If you can find an altar dedicated to " - << old_god_name; - } - else - { - text << "Should you decide that abandoning " << old_god_name - << "wasn't such a smart move after all, and you'd like to " - "return to your old faith, you'll have to find an " - "altar dedicated to " << old_god_name << " where"; - } - text << " you can re-convert, and all will be well. Otherwise " - "you'll have to weather this god's displeasure until all " - "divine wrath is spent."; - - } - else - { - bool angry = false; - if (is_good_god(old_god)) - { - if (is_good_god(new_god)) - { - text << "Fortunately, it seems that " << old_god_name << - " didn't mind your converting to " << new_god_name - << ". "; - - if (old_piety > 30) - text << "You even kept some of your piety! "; - - text << "Note that this kind of alliance only exists " - "between the three good gods, so don't expect this " - "to be the norm."; - } - else if (!god_hates_your_god(old_god)) - { - text << "Fortunately, it seems that " << old_god_name << - " didn't mind your converting to " << new_god_name - << ". That's because " << old_god_name << " is one of " - "the good gods who generally are rather forgiving " - "about change of faith - unless you switch over to " - "the path of evil, in which case their retribution " - "can be nasty indeed!"; - } - else - { - text << "Looks like " << old_god_name << " didn't " - "appreciate your converting to " << new_god_name - << "! But really, changing from one of the good gods " - "to an evil one, what did you expect!? For any god " - "not on the opposing side of the faith, " - << old_god_name << " would have been much more " - "forgiving. "; - - angry = true; - } - } - else - { - text << "Looks like " << old_god_name << " didn't appreciate " - "your converting to " << new_god_name << "! (Actually, " - "only the three good gods will sometimes be forgiving " - "about this kind of faithlessness.) "; - - angry = true; - } - - if (angry) - { - text << "Unfortunately, while converting back would appease " - << old_god_name << ", it would annoy " << new_god_name - << ", so you're stuck with having to suffer the wrath of " - "one god or another."; - } - } - - break; - } - - case TUT_WIELD_WEAPON: - { - int wpn = you.equip[EQ_WEAPON]; - if (wpn != -1 - && you.inv[wpn].base_type == OBJ_WEAPONS - && you.inv[wpn].cursed()) - { - // Don't trigger if the wielded weapon is cursed. - Tutorial.tutorial_events[seen_what] = true; - Tutorial.tutorial_left++; - return; - } - - if (Tutorial.tutorial_type == TUT_RANGER_CHAR && wpn != -1 - && you.inv[wpn].base_type == OBJ_WEAPONS - && you.inv[wpn].sub_type == WPN_BOW) - { - text << "You can easily switch between weapons in slots a and " - "b by pressing <w>%</w>."; - cmd.push_back(CMD_WEAPON_SWAP); - } - else - { - text << "You can easily switch back to your weapon in slot a by " - "pressing <w>%</w>. To change the slot of an item, type " - "<w>%i</w> and choose the appropriate slots."; - cmd.push_back(CMD_WEAPON_SWAP); - cmd.push_back(CMD_ADJUST_INVENTORY); - } - break; - } - case TUT_FLEEING_MONSTER: - if (Tutorial.tutorial_type != TUT_BERSERK_CHAR) - return; - - text << "Now that monster is scared of you! Note that you do not " - "absolutely have to follow it. Rather, you can let it run " - "away. Sometimes, though, it can be useful to attack a " - "fleeing creature by throwing something after it. If you " - "have any darts or stones in your <w>%</w>nventory, you can " - "look at one of them to read an explanation of how to do this."; - cmd.push_back(CMD_DISPLAY_INVENTORY); - break; - - case TUT_MONSTER_BRAND: -#ifdef USE_TILE - tiles.place_cursor(CURSOR_TUTORIAL, gc); - if (const monsters *m = monster_at(gc)) - tiles.add_text_tag(TAG_TUTORIAL, m->name(DESC_CAP_A), gc); -#endif - text << "That monster looks a bit unusual. You might wish to examine " - "it a bit more closely by " -#ifdef USE_TILE - "hovering your mouse over its tile."; -#else - "pressing <w>%</w> and moving the cursor onto its square."; - cmd.push_back(CMD_LOOK_AROUND); -#endif - break; - - case TUT_MONSTER_FRIENDLY: - { - const monsters *m = monster_at(gc); - - if (!m) - DELAY_EVENT; - -#ifdef USE_TILE - tiles.place_cursor(CURSOR_TUTORIAL, gc); - tiles.add_text_tag(TAG_TUTORIAL, m->name(DESC_CAP_A), gc); -#endif - text << "That monster is friendly to you and will attack your " - "enemies, though you'll get only half the experience for " - "monsters killed by allies of what you'd get for killing them " - "yourself. You can command your allies by pressing <w>%</w> " - "to talk to them."; - cmd.push_back(CMD_SHOUT); - - if (!mons_att_wont_attack(m->attitude)) - { - text << "\nHowever, it is only <w>temporarily</w> friendly, and " - "will become dangerous again when this friendliness " - "wears off."; - } - break; - } - - case TUT_MONSTER_SHOUT: - { - const monsters* m = monster_at(gc); - - if (!m) - DELAY_EVENT; - - // "Shouts" from zero experience monsters are boring, ignore - // them. - if (mons_class_flag(m->type, M_NO_EXP_GAIN)) - { - Tutorial.tutorial_events[TUT_MONSTER_SHOUT] = true; - return; - } - - const bool vis = you.can_see(m); - -#ifdef USE_TILE - if (vis) - { - tiles.place_cursor(CURSOR_TUTORIAL, gc); - tiles.add_text_tag(TAG_TUTORIAL, m->name(DESC_CAP_A), gc); - } -#endif - if (!vis) - { - text << "Uh-oh, some monster noticed you, either one that's " - "around a corner or one that's invisible. Plus, the " - "noise it made will alert other monsters in the " - "vicinity, who will come to check out what the commotion " - "was about."; - } - else if (mons_shouts(m->type, false) == S_SILENT) - { - text << "Uh-oh, that monster noticed you! Fortunately, it " - "didn't make any noise, but many monsters do make " - "noise when they notice you, which alerts other monsters " - "in the area, who will come to check out what the " - "commotion was about."; - } - else - { - text << "Uh-oh, that monster noticed you! Plus, the " - "noise it made will alert other monsters in the " - "vicinity, who will come to check out what the commotion " - "was about."; - } - break; - } - - case TUT_MONSTER_LEFT_LOS: - { - const monsters* m = monster_at(gc); - - if (!m || !you.can_see(m)) - DELAY_EVENT; - - text << m->name(DESC_CAP_THE, true) << " didn't vanish, but merely " - "moved onto a square which you can't currently see. It's still " - "nearby, unless something happens to it in the short amount of " - "time it's out of sight."; - break; - } - - case TUT_SEEN_MONSTER: - case TUT_SEEN_FIRST_OBJECT: - // Handled in special functions. - break; - - case TUT_SEEN_TOADSTOOL: - { - const monsters* m = monster_at(gc); - - if (!m || !you.can_see(m)) - DELAY_EVENT; - - text << "Sometimes toadstools will grow on decaying corpses, and " - "will wither away soon after appearing. Worshippers of " - "Fedhas Madash, the plant god, can make use of them, " - "but to everyone else they're just ugly dungeon decoration."; - break; - } - - case TUT_SEEN_ZERO_EXP_MON: - { - const monsters* m = monster_at(gc); - - if (!m || !you.can_see(m)) - DELAY_EVENT; - - text << "That "; -#ifdef USE_TILE - // need to highlight monster - tiles.place_cursor(CURSOR_TUTORIAL, gc); - tiles.add_text_tag(TAG_TUTORIAL, m); - - text << "is a "; -#else - text << glyph_to_tagstr(get_mons_glyph(m)) << " is a "; -#endif - text << m->name(DESC_PLAIN).c_str() << ". "; - - text << "While <w>technically</w> a monster, it's more like " - "dungeon furniture, since it's harmless and doesn't move. " - "If it's in your way you can attack and kill it like other " - "monsters, but you won't get any experience for doing so. "; - break; - } - - case TUT_ABYSS: - text << "Uh-oh, you've wound up in the Abyss! The Abyss is a special " - "place where you cannot remember or map where you've been; it " - "is filled with nasty monsters, and you're probably going to " - "die.\n"; - text << "To increase your chances of survival until you can find the " - "exit" -#ifndef USE_TILE - " (a flickering <w>\\</w>)" -#endif - ", keep moving, don't fight any of the monsters, and don't " - "bother picking up any items on the ground. If you're " - "encumbered or overburdened, then lighten up your load, and if " - "the monsters are closing in, try to use items of speed to get " - "away. Also, wherever possible, move in a direction slightly " - "off from a compass direction (for example, north-by-northwest " - "instead of north or northwest), as you're more likely to miss " - "the exit if you keep heading solely in a compass direction."; - break; - - case TUT_SPELL_MISCAST: - { - text << "You just miscast a spell. "; - - const item_def *shield = you.slot_item(EQ_SHIELD, false); - if (!player_effectively_in_light_armour() || shield) - { - text << "Wearing heavy body armour or using a shield, especially a " - "large one, can severely hamper your spellcasting " - "abilities. You can check the effect of this by comparing " - "the success rates on the <w>%\?</w> screen with and " - "without the item being worn.\n\n"; - cmd.push_back(CMD_CAST_SPELL); - } - - text << "If the spellcasting success chance is high (which can be " - "checked by entering <w>%\?</w> or <w>%</w>) then a miscast " - "merely means the spell is not working, along with a harmless " - "side effect. " - "However, for spells with a low success rate, there's a chance " - "of contaminating yourself with magical energy, plus a chance " - "of an additional harmful side effect. Normally this isn't a " - "problem, since magical contamination bleeds off over time, " - "but if you're repeatedly contaminated in a short amount of " - "time you'll mutate or suffer from other ill side effects.\n\n"; - cmd.push_back(CMD_CAST_SPELL); - cmd.push_back(CMD_DISPLAY_SPELLS); - - text << "Note that a miscast spell will still consume the full amount " - "of MP and nutrition that a successfully cast spell would."; - break; - } - case TUT_SPELL_HUNGER: - text << "The spell you just cast made you hungrier; you can see how " - "hungry spells make you by " -#ifdef USE_TILE - "examining your spells in the spell display, or by " -#endif - "entering <w>%\?!</w> or <w>%I</w>. " - "The amount of nutrition consumed increases with the level of " - "the spell and decreases depending on your intelligence stat " - "and your Spellcasting skill. If both of these are high " - "enough a spell might even not cost you any nutrition at all."; - cmd.push_back(CMD_CAST_SPELL); - cmd.push_back(CMD_DISPLAY_SPELLS); - break; - - case TUT_GLOWING: - text << "You've accumulated so much magical contamination that you're " - "glowing! You usually acquire magical contamination from using " - "some powerful magics, like invisibility, haste or potions of " - "resistance. "; - - if (you.magic_contamination < 5) - { - text << "As long as the status only shows in grey nothing will " - "actually happen as a result of it, but as you continue " - "suffusing yourself with magical contamination you'll " - "eventually start glowing for real, which "; - } - else - { - text << "This normally isn't a problem as contamination slowly " - "bleeds off on its own, but it seems that you've " - "accumulated so much contamination over a short amount of " - "time that it "; - } - text << "can have nasty effects, such as mutate you or deal direct " - "damage. In addition, glowing is going to make you much more " - "noticeable."; - break; - - case TUT_YOU_RESIST: - text << "There are many dangers in Crawl. Luckily, there are ways to " - "(at least partially) resist some of them, if you are " - "fortunate enough to find them. There are two basic variants " - "of resistances: the innate magic resistance that depends on " - "your species, grows with the experience level, and protects " - "against hostile enchantments; and the specific resistances " - "against certain types of magic and also other effects, e.g. " - "fire or draining.\n" - "You can find items in the dungeon or gain mutations that will " - "increase (or lower) one or more of your resistances. To view " - "your current set of resistances, " -#ifdef USE_TILE - "<w>right-click</w> on the player avatar."; -#else - "press <w>%</w>."; - cmd.push_back(CMD_RESISTS_SCREEN); -#endif - break; - - case TUT_CAUGHT_IN_NET: - text << "While you are held in a net, you cannot move around or engage " - "monsters in combat. Instead, any movement you take is counted " - "as an attempt to struggle free from the net. With a wielded " - "bladed weapon you will be able to cut the net faster"; - - if (Tutorial.tutorial_type == TUT_BERSERK_CHAR) - text << ", especially if you're berserking while doing so"; - - text << ". Small species may also wriggle out of a net, only damaging " - "it a bit, so as to then <w>%</w>ire it at a monster."; - cmd.push_back(CMD_FIRE); - - if (Tutorial.tutorial_type == TUT_MAGIC_CHAR) - { - text << " Note that casting spells is still very much possible, " - "as is using wands, scrolls and potions."; - } - else - { - text << " Note that using wands, scrolls and potions is still " - "very much possible."; - } - break; - - case TUT_YOU_SILENCE: - redraw_screen(); - text << "While you are silenced, you cannot cast spells, read scrolls " - "or use divine invocations. The same is true for any monster " - "within the effect radius. The field of silence (recognizable " - "by " -#ifdef USE_TILE - "the special-looking frame tiles" -#else - "different-coloured floor squares" -#endif - ") is always centered on you and will move along with you. " - "The radius will gradually shrink, eventually making you the " - "only one affected, before the effect fades entirely."; - break; - - case TUT_LOAD_SAVED_GAME: - { - text << "Welcome back! If it's been a while since you last played this " - "character, you should take some time to refresh your memory " - "of your character's progress. It is recommended to at least " - "have a look through your <w>%</w>nventory, but you should " - "also check "; - cmd.push_back(CMD_DISPLAY_INVENTORY); - - std::vector<std::string> listed; - if (you.spell_no > 0) - { - listed.push_back("your spells (<w>%?</w>)"); - cmd.push_back(CMD_CAST_SPELL); - } - if (!your_talents(false).empty()) - { - listed.push_back("your <w>%</w>bilities"); - cmd.push_back(CMD_USE_ABILITY); - } - if (Tutorial.tutorial_type != TUT_MAGIC_CHAR || how_mutated()) - { - listed.push_back("your set of mutations (<w>%</w>)"); - cmd.push_back(CMD_DISPLAY_MUTATIONS); - } - if (you.religion != GOD_NO_GOD) - { - listed.push_back("your religious standing (<w>%</w>)"); - cmd.push_back(CMD_DISPLAY_RELIGION); - } - - listed.push_back("the message history (<w>%</w>)"); - listed.push_back("the character overview screen (<w>%</w>)"); - listed.push_back("the dungeon overview screen (<w>%</w>)"); - text << comma_separated_line(listed.begin(), listed.end()) << "."; - cmd.push_back(CMD_REPLAY_MESSAGES); - cmd.push_back(CMD_RESISTS_SCREEN); - cmd.push_back(CMD_DISPLAY_OVERMAP); - - text << "\nAlternatively, you can dump all information pertaining to " - "your character into a text file with the <w>%</w> command. " - "You can then find said file in the <w>morgue/</w> folder (<w>" - << you.your_name << ".txt</w>) and read it at your leisure. Also, " - "such a file will automatically be created upon death (the " - "filename will then also contain the date) but that won't be " - "of much use to you now."; - cmd.push_back(CMD_CHARACTER_DUMP); - break; - } - default: - text << "You've found something new (but I don't know what)!"; - } - - if (!text.str().empty()) - { - std::string output = text.str(); - if (!cmd.empty()) - insert_commands(output, cmd); - mpr(output, MSGCH_TUTORIAL); - - stop_running(); - } -} - -formatted_string tut_abilities_info() -{ - std::ostringstream text; - text << "<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - std::string broken = "This screen shows your character's set of talents. " - "You can gain new abilities via certain items, through religion or by " - "way of mutations. Activation of an ability usually comes at a cost, " - "e.g. nutrition or Magic power. Press '<w>!</w>' or '<w>?</w>' to " - "toggle between ability selection and description."; - linebreak_string2(broken, _get_tutorial_cols()); - text << broken; - - text << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - - return formatted_string::parse_string(text.str(), false); -} - -// Explains the basics of the skill screen. Don't bother the player with the -// aptitude information. (Toggling is still possible, of course.) -void print_tut_skills_info() -{ - textcolor(channel_to_colour(MSGCH_TUTORIAL)); - std::ostringstream text; - text << "<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - std::string broken = "This screen shows the skill set of your character. " - "The number next to the skill is your current level, the higher the " - "better. The <cyan>cyan percent value</cyan> shows your progress " - "towards the next skill level. You can toggle which skills to train by " - "pressing their slot letters. A <darkgrey>greyish</darkgrey> skill " - "will increase at a decidedly slower rate and ease training of others. " - "Press <w>?</w> to read your skills' descriptions."; - linebreak_string2(broken, _get_tutorial_cols()); - text << broken; - text << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - - formatted_string::parse_string(text.str(), false).display(); -} - -void print_tut_skills_description_info() -{ - textcolor(channel_to_colour(MSGCH_TUTORIAL)); - std::ostringstream text; - text << "<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - std::string broken = "This screen shows the skill set of your character. " - "Press the letter of a skill to read its description, " - "or press <w>?</w> again to return to the skill " - "selection."; - - linebreak_string2(broken, _get_tutorial_cols()); - text << broken; - text << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - - formatted_string::parse_string(text.str(), false).display(); -} - -// A short explanation of Crawl's target mode and its most important commands. -static std::string _tut_target_mode(bool spells = false) -{ - std::string result; - result = "then be taken to target mode with the nearest monster or " - "previous target already targeted. You can also cycle through " - "all hostile monsters in sight with <w>+</w> or <w>-</w>. " - "Once you're aiming at the correct monster, simply hit " - "<w>f</w>, <w>Enter</w> or <w>.</w> to shoot at it. " - "If you miss, <w>"; - - command_type cmd; - if (spells) - { - result += "%ap"; - cmd = CMD_CAST_SPELL; - } - else - { - result += "%f"; - cmd = CMD_FIRE; - } - - result += "</w> fires at the same target again."; - insert_commands(result, cmd, 0); - - return (result); -} - -static std::string _tut_abilities(const item_def& item) -{ - std::string str = "To do this, "; - - std::vector<command_type> cmd; - if (!item_is_equipped(item)) - { - switch(item.base_type) - { - case OBJ_WEAPONS: - str += "first <w>%</w>ield it"; - cmd.push_back(CMD_WIELD_WEAPON); - break; - case OBJ_ARMOUR: - str += "first <w>%</w>ear it"; - cmd.push_back(CMD_WEAR_ARMOUR); - break; - case OBJ_JEWELLERY: - str += "first <w>%</w>ut it on"; - cmd.push_back(CMD_WEAR_JEWELLERY); - break; - default: - str += "<r>(BUG! this item shouldn't give an ability)</r>"; - break; - } - str += ", then "; - } - str += "enter the ability menu with <w>%</w>, and then " - "choose the corresponding ability. Note that such an attempt of " - "activation, especially by the untrained, is likely to fail."; - cmd.push_back(CMD_USE_ABILITY); - - insert_commands(str, cmd); - return (str); -} - -static std::string _tut_throw_stuff(const item_def &item) -{ - std::string result; - - result = "To do this, type <w>%</w> to fire, then <w>"; - result += item.slot; - result += "</w> for "; - result += (item.quantity > 1 ? "these" : "this"); - result += " "; - result += item_base_name(item); - result += (item.quantity > 1? "s" : ""); - result += ". You'll "; - result += _tut_target_mode(); - - insert_commands(result, CMD_FIRE, 0); - return (result); -} - -// Explains the most important commands necessary to use an item, and mentions -// special effects, etc. -// NOTE: For identified artefacts don't give all this information! -// (The screen is likely to overflow.) Artefacts need special information -// if they are evokable or grant resistances. -// In any case, check whether we still have enough space for the -// inscription prompt and answer. -void tutorial_describe_item(const item_def &item) -{ - std::ostringstream ostr; - ostr << "<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - std::vector<command_type> cmd; - - switch (item.base_type) - { - case OBJ_WEAPONS: - { - if (is_artefact(item) && item_type_known(item)) - { - if (gives_ability(item) - && wherey() <= get_number_of_lines() - 5) - { - // You can activate it. - ostr << "When wielded, some weapons (such as this one) " - "offer certain abilities you can activate. "; - ostr << _tut_abilities(item); - break; - } - else if (gives_resistance(item) - && wherey() <= get_number_of_lines() - 3) - { - - // It grants a resistance. - ostr << "\nThis weapon offers its wearer protection from " - "certain sources. For an overview of your " - "resistances (among other things) type <w>%</w>" -#ifdef USE_TILE - " or click on your avatar with the <w>right mouse " - "button</w>" -#endif - "."; - cmd.push_back(CMD_RESISTS_SCREEN); - break; - } - return; - } - - item_def *weap = you.slot_item(EQ_WEAPON, false); - bool wielded = (weap && (*weap).slot == item.slot); - bool long_text = false; - - if (!wielded) - { - ostr << "You can wield this weapon with <w>%</w>, or use " - "<w>%</w> to switch between the weapons in slot " - "a and b. (Use <w>%i</w> to adjust item slots.)"; - cmd.push_back(CMD_WIELD_WEAPON); - cmd.push_back(CMD_WEAPON_SWAP); - cmd.push_back(CMD_ADJUST_INVENTORY); - - // 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_THROWING, 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) - << "</w> for a while, so maybe you should " - "continue training that rather than <w>" - << skill_name(curr_wpskill) - << "</w>. (Type <w>%</w> to see the skill " - "management screen for the actual numbers.)"; - - cmd.push_back(CMD_DISPLAY_SKILLS); - long_text = true; - } - } - else // wielded weapon - { - if (is_range_weapon(item)) - { - ostr << "To attack a monster, "; -#ifdef USE_TILE - ostr << "if you have appropriate ammo quivered you can " - "<w>left mouse click</w> on the monster while " - "prssing the <w>Shift key</w>. Alternatively, " - "you can <w>left mouse click</w> on the tile for " - "the ammo you wish to fire, and then <w>left " - "mouse click</w> on the monster.\n\n"; - ostr << "To launch ammunition using the keyboard, "; -#endif - ostr << "you only need to " - "<w>%</w>ire the appropriate type of ammunition. " - "You'll "; - ostr << _tut_target_mode(); - cmd.push_back(CMD_FIRE); - } - else - { - ostr << "To attack a monster, you can simply walk into it."; - } - } - - if (is_throwable(&you, item) && !long_text) - { - ostr << "\n\nSome weapons (including this one), can also be " - "<w>%</w>ired. "; - cmd.push_back(CMD_FIRE); - 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."; - - Tutorial.tutorial_events[TUT_SEEN_RANDART] = false; - } - 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 or one of the " - "enchantment scrolls."; - - if (!wielded && is_throwable(&you, item)) - ostr << " (Throwing it is safe, though.)"; - - Tutorial.tutorial_events[TUT_YOU_CURSED] = false; - } - Tutorial.tutorial_events[TUT_SEEN_WEAPON] = false; - break; - } - case OBJ_MISSILES: - if (is_throwable(&you, item)) - { - ostr << item.name(DESC_CAP_YOUR) - << " can be <w>%</w>ired without the use of a launcher. "; - ostr << _tut_throw_stuff(item); - cmd.push_back(CMD_FIRE); - } - else if (is_launched(&you, you.weapon(), item)) - { - ostr << "As you're already wielding the appropriate launcher, " - "you can simply "; -#ifdef USE_TILE - ostr << "<w>left mouse click</w> on the monster you want " - "to hit while pressing the <w>Shift key</w>. " - "Alternatively, you can <w>left mouse click</w> on " - "this tile of the ammo you want to fire, and then " - "<w>left mouse click</w> on the monster you want " - "to hit.\n\n" - - "To launch this ammo using the keyboard, you can " - "simply "; -#endif - - ostr << "<w>%</w>ire " - << (item.quantity > 1 ? "these" : "this") - << " " << item.name(DESC_BASENAME) - << (item.quantity > 1? "s" : "") - << ". You'll "; - ostr << _tut_target_mode(); - cmd.push_back(CMD_FIRE); - } - else - { - ostr << "To shoot " - << (item.quantity > 1 ? "these" : "this") - << " " << item.name(DESC_BASENAME) - << (item.quantity > 1? "s" : "") - << ", first you need to <w>%</w>ield an appropriate " - "launcher."; - cmd.push_back(CMD_WIELD_WEAPON); - } - Tutorial.tutorial_events[TUT_SEEN_MISSILES] = false; - break; - - case OBJ_ARMOUR: - { - bool wearable = true; - if (you.species == SP_CENTAUR && item.sub_type == ARM_BOOTS) - { - ostr << "As a Centaur you cannot wear boots. " - "(Type <w>%</w> to see a list of your mutations and " - "innate abilities.)"; - cmd.push_back(CMD_DISPLAY_MUTATIONS); - wearable = false; - } - else if (you.species == SP_MINOTAUR && is_hard_helmet(item)) - { - ostr << "As a Minotaur you cannot wear helmets. " - "(Type <w>%</w> to see a list of your mutations and " - "innate abilities.)"; - cmd.push_back(CMD_DISPLAY_MUTATIONS); - wearable = false; - } - else if (item.sub_type == ARM_CENTAUR_BARDING) - { - ostr << "Only centaurs can wear centaur barding."; - wearable = false; - } - else if (item.sub_type == ARM_NAGA_BARDING) - { - ostr << "Only nagas can wear naga barding."; - wearable = false; - } - else - { - ostr << "You can wear pieces of armour with <w>%</w> and take " - "them off again with <w>%</w>" -#ifdef USE_TILE - ", or, alternatively, simply click on their tiles to " - "perform either action." -#endif - "."; - cmd.push_back(CMD_WEAR_ARMOUR); - cmd.push_back(CMD_REMOVE_ARMOUR); - } - - if (Tutorial.tutorial_type == TUT_MAGIC_CHAR - && get_armour_slot(item) == EQ_BODY_ARMOUR - && !is_effectively_light_armour(&item)) - { - ostr << "\nNote that body armour with high evasion penalties " - "may hinder your ability to learn and cast spells. " - "Light armour such as robes, leather armour or any " - "elven armour will be generally safe for any aspiring " - "spellcaster."; - } - else if (Tutorial.tutorial_type == TUT_MAGIC_CHAR - && is_shield(item)) - { - ostr << "\nNote that shields will hinder you ability to " - "cast spells; the larger the shield, the bigger " - "the penalty."; - } - else if (Tutorial.tutorial_type == TUT_RANGER_CHAR - && is_shield(item)) - { - ostr << "\nNote that wearing a shield will greatly decrease " - "the speed at which you can shoot arrows."; - } - - 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."; - - Tutorial.tutorial_events[TUT_SEEN_RANDART] = false; - } - if (wearable) - { - 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 or enchant " - "armour."; - } - if (gives_resistance(item)) - { - ostr << "\n\nThis armour offers its wearer protection from " - "certain sources. For an overview of your" - " resistances (among other things) type <w>%</w>" -#ifdef USE_TILE - " or click on your avatar with the <w>right mouse " - "button</w>" -#endif - "."; - cmd.push_back(CMD_RESISTS_SCREEN); - } - if (gives_ability(item)) - { - ostr << "\n\nWhen worn, some types of armour (such as " - "this one) offer certain <w>%</w>bilities you can " - "activate. "; - ostr << _tut_abilities(item); - cmd.push_back(CMD_USE_ABILITY); - } - } - Tutorial.tutorial_events[TUT_SEEN_ARMOUR] = false; - break; - } - case OBJ_WANDS: - ostr << "The magic within can be unleashed by evoking " - "(<w>%</w>) it."; - cmd.push_back(CMD_EVOKE); -#ifdef USE_TILE - ostr << " Alternatively, you can 1) <w>left mouse click</w> on " - "the monster you wish to target (or your player character " - "to target yourself) while pressing the <w>"; -#ifdef UNIX - if (!tiles.is_fullscreen()) - ostr << "Ctrl + Shift keys"; - else -#endif - ostr << "Alt key"; - ostr << "</w> and pick the wand from the menu, or 2) " - "<w>left mouse click</w> on the wand tile and then " - "<w>left mouse click</w> on your target."; -#endif - Tutorial.tutorial_events[TUT_SEEN_WAND] = false; - break; - - case OBJ_FOOD: - if (food_is_rotten(item) && !player_mutation_level(MUT_SAPROVOROUS)) - { - ostr << "You can't eat rotten chunks, so you might just as " - "well "; - if (!in_inventory(item)) - ostr << "ignore them. "; - else - { - ostr << "<w>%</w>rop this. Use <w>%&</w> to select all " - "skeletons, corpses and rotten chunks in your " - "inventory. "; - cmd.push_back(CMD_DROP); - cmd.push_back(CMD_DROP); - } - } - else - { - ostr << "Food can simply be <w>%</w>aten" -#ifdef USE_TILE - ", something you can also do by <w>left clicking</w> " - "on it" -#endif - ". "; - cmd.push_back(CMD_EAT); - - if (item.sub_type == FOOD_CHUNK) - { - ostr << "Note that most species refuse to eat raw meat " - "unless really hungry. "; - - if (food_is_rotten(item)) - { - ostr << "Even fewer can safely digest rotten meat, and " - "you're probably not part of that group."; - } - } - } - Tutorial.tutorial_events[TUT_SEEN_FOOD] = false; - break; - - case OBJ_SCROLLS: - ostr << "Type <w>%</w> to read this scroll" -#ifdef USE_TILE - "or simply click on it with your <w>left mouse button</w>" -#endif - "."; - cmd.push_back(CMD_READ); - - Tutorial.tutorial_events[TUT_SEEN_SCROLL] = false; - break; - - case OBJ_JEWELLERY: - { - ostr << "Jewellery can be <w>%</w>ut on or <w>%</w>emoved " - "again" -#ifdef USE_TILE - ", though in Tiles, either can be done by clicking on the " - "item in your inventory" -#endif - "."; - cmd.push_back(CMD_WEAR_JEWELLERY); - cmd.push_back(CMD_REMOVE_JEWELLERY); - - 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."; - } - if (gives_resistance(item)) - { - ostr << "\n\nThis " - << (item.sub_type < NUM_RINGS ? "ring" : "amulet") - << " offers its wearer protection " - "from certain sources. For an overview of your " - "resistances (among other things) type <w>%</w>" -#ifdef USE_TILE - " or click on your avatar with the <w>right mouse " - "button</w>" -#endif - "."; - cmd.push_back(CMD_RESISTS_SCREEN); - } - if (gives_ability(item)) - { - ostr << "\n\nWhen worn, some types of jewellery (such as this " - "one) offer certain <w>%</w>bilities you can activate. "; - cmd.push_back(CMD_USE_ABILITY); - ostr << _tut_abilities(item); - } - Tutorial.tutorial_events[TUT_SEEN_JEWELLERY] = false; - break; - } - case OBJ_POTIONS: - ostr << "Type <w>%</w> to quaff this potion" -#ifdef USE_TILE - "or simply click on it with your <w>left mouse button</w>" -#endif - "."; - cmd.push_back(CMD_QUAFF); - Tutorial.tutorial_events[TUT_SEEN_POTION] = false; - break; - - case OBJ_BOOKS: - if (item.sub_type == BOOK_MANUAL) - { - ostr << "A manual can greatly help you in training a skill. " - "To use it, <w>%</w>ead it while your experience " - "pool (the number in brackets) is large. Note that " - "this will drain said pool, so only use this manual " - "if you think you need the skill in question."; - cmd.push_back(CMD_READ); - } - else // It's a spellbook! - { - if (you.religion == GOD_TROG - && (item.sub_type != BOOK_DESTRUCTION - || !item_ident(item, ISFLAG_KNOW_TYPE))) - { - if (!item_ident(item, ISFLAG_KNOW_TYPE)) - { - ostr << "It's a book, you can <w>%</w>ead it."; - cmd.push_back(CMD_READ); - } - else - { - ostr << "A spellbook! You could <w>%</w>emorise some " - "spells and then cast them with <w>%</w>."; - cmd.push_back(CMD_MEMORISE_SPELL); - cmd.push_back(CMD_CAST_SPELL); - } - 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>%</w>bility. " - "Note that this only works on books that are lying " - "on the floor and not on your current square. "; - cmd.push_back(CMD_USE_ABILITY); - } - else if (!item_ident(item, ISFLAG_KNOW_TYPE)) - { - ostr << "It's a book, you can <w>%</w>ead it" -#ifdef USE_TILE - ", something that can also be achieved by clicking " - "on its tile in your inventory." -#endif - "."; - cmd.push_back(CMD_READ); - } - else if (item.sub_type == BOOK_DESTRUCTION) - { - ostr << "This magical item can cause great destruction " - "- to you, or your surroundings. Use with care!"; - } - else if (!you.skills[SK_SPELLCASTING]) - { - ostr << "A spellbook! You could <w>%</w>emorise some " - "spells and then cast them with <w>%</w>. "; - cmd.push_back(CMD_MEMORISE_SPELL); - cmd.push_back(CMD_CAST_SPELL); - - ostr << "\nFor now, however, that will have to wait until " - "you've learned the basics of Spellcasting by " - "reading lots of scrolls."; - } - else // You actually can cast spells. - { - if (player_can_memorise(item)) - { - ostr << "Such a <lightblue>highlighted " - "spell</lightblue> can be <w>%</w>emorised " - "right away. "; - cmd.push_back(CMD_MEMORISE_SPELL); - } - else - { - ostr << "You cannot memorise 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, "; -#ifdef USE_TILE - ostr << "you can <w>left mouse click</w> on the " - "monster you wish to target (or on your " - "player character to cast a spell on " - "yourself) while pressing the <w>Control " - "key</w>, and then select a spell from the " - "menu. Or you can switch to the spellcasting " - "display by <w>clicking on the</w> " - "corresponding <w>tab</w>." - "\n\nAlternatively, "; -#endif - ostr << "you can type <w>%</w> and choose a " - "spell, e.g. <w>a</w> (check with <w>?</w>). " - "For attack spells you'll "; - cmd.push_back(CMD_CAST_SPELL); - ostr << _tut_target_mode(true); - } - } - } - ostr << "\n"; - Tutorial.tutorial_events[TUT_SEEN_SPBOOK] = false; - break; - - case OBJ_CORPSES: - Tutorial.tutorial_events[TUT_SEEN_CARRION] = false; - - if (item.sub_type == CORPSE_SKELETON) - { - ostr << "Skeletons can be used as components for certain " - "necromantic spells. Apart from that, they are " - "largely useless."; - - if (in_inventory(item)) - { - ostr << " In the drop menu you can select all skeletons, " - "corpses, and rotten chunks in your inventory " - "at once with <w>%&</w>."; - cmd.push_back(CMD_DROP); - } - break; - } - - ostr << "Corpses lying on the floor can be <w>%</w>hopped up with " - "a sharp implement to produce chunks for food (though they " - "may not be healthy)"; - cmd.push_back(CMD_BUTCHER); - - if (!food_is_rotten(item) && god_likes_fresh_corpses(you.religion)) - { - ostr << ", or offered as a sacrifice to " - << god_name(you.religion) - << " <w>%</w>raying over them."; - cmd.push_back(CMD_PRAY); - } - ostr << ". "; - - if (food_is_rotten(item)) - { - ostr << "Rotten corpses won't be of any use to you, though, so " - "you might just as well "; - if (!in_inventory(item)) - ostr << "ignore them. "; - else - { - ostr << "<w>%</w>rop this. Use <w>%&</w> to select all " - "skeletons and rotten chunks or corpses in your " - "inventory. "; - cmd.push_back(CMD_DROP); - cmd.push_back(CMD_DROP); - } - ostr << "No god will accept such rotten sacrifice, either."; - } - else - { -#ifdef USE_TILE - ostr << " For an individual corpse in your inventory, the most " - "practical way to chop it up is to drop it by clicking " - "on it with your <w>left mouse button</w> while " - "<w>Shift</w> is pressed, and then repeat that command " - "for the corpse tile now lying on the floor."; -#endif - } - if (!in_inventory(item)) - break; - - ostr << "\n\nIf there are several items in your inventory you'd " - "like to drop, the most convenient way is to use the " - "<w>%</w>rop menu. On a related note, butchering " - "several corpses on a floor square is facilitated by " - "using the <w>%</w>hop prompt where <w>c</w> is a " - "valid synonym for <w>y</w>es or you can directly chop " - "<w>a</w>ll corpses."; - cmd.push_back(CMD_DROP); - cmd.push_back(CMD_BUTCHER); - break; - - case OBJ_STAVES: - if (item_is_rod( item )) - { - if (!item_ident(item, ISFLAG_KNOW_TYPE)) - { - ostr << "\n\nTo find out what this rod might do, you have " - "to <w>%</w>ield it to see if you can use the " - "spells hidden within, then e<w>%</w>oke it to " - "actually do so" -#ifdef USE_TILE - ", both of which can be done by clicking on it" -#endif - "."; - } - else - { - ostr << "\n\nYou can use this rod's magic by " - "<w>%</w>ielding and e<w>%</w>oking it" -#ifdef USE_TILE - ", both of which can be achieved by clicking on it" -#endif - "."; - } - cmd.push_back(CMD_WIELD_WEAPON); - cmd.push_back(CMD_EVOKE_WIELDED); - } - else - { - ostr << "This staff can enhance your spellcasting of specific " - "spell schools. "; - - bool gives_resist = false; - if (you.spell_no && !item_ident(item, ISFLAG_KNOW_TYPE)) - { - 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."; - } - else if (gives_resistance(item)) - { - ostr << "It also offers its wielder protection from " - "certain sources. For an overview of your " - "resistances (among other things) type <w>%</w>" -#ifdef USE_TILE - " or click on your avatar with the <w>right mouse " - "button</w>" -#endif - "."; - - cmd.push_back(CMD_RESISTS_SCREEN); - gives_resist = true; - } - - if (!gives_resist && you.religion == GOD_TROG) - { - ostr << "\n\nSeeing how " - << god_name(GOD_TROG, false) - << " frowns upon the use of magic, this staff will be " - "of little use to you and you might just as well " - "<w>%</w>rop it now."; - cmd.push_back(CMD_DROP); - } - } - Tutorial.tutorial_events[TUT_SEEN_STAFF] = false; - break; - - case OBJ_MISCELLANY: - if (is_deck(item)) - { - ostr << "Decks of cards are powerful magical items. Try " - "<w>%</w>ielding and e<w>%</w>oking it" -#ifdef USE_TILE - ", either of which can be done by clicking on it" -#endif - ". You can read about the effect of a card by " - "searching the game's database with <w>%/c</w>."; - cmd.push_back(CMD_WIELD_WEAPON); - cmd.push_back(CMD_EVOKE_WIELDED); - cmd.push_back(CMD_DISPLAY_COMMANDS); - } - else - { - ostr << "Miscellaneous items sometimes harbour magical powers " - "that can be harnessed by e<w>%</w>oking the item."; - cmd.push_back(CMD_EVOKE); - } - - Tutorial.tutorial_events[TUT_SEEN_MISC] = false; - break; - - default: - return; - } - - ostr << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - std::string broken = ostr.str(); - if (!cmd.empty()) - insert_commands(broken, cmd); - linebreak_string2(broken, _get_tutorial_cols()); - cgotoxy(1, wherey() + 2); - display_tagged_block(broken); -} // tutorial_describe_item() - -void tutorial_inscription_info(bool autoinscribe, std::string prompt) -{ - // Don't print anything if there's not enough space. - if (wherey() >= get_number_of_lines() - 1) - return; - - std::ostringstream text; - text << "<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - - bool longtext = false; - if (wherey() <= get_number_of_lines() - (autoinscribe ? 10 : 8)) - { - text << "\n" - "Inscriptions are a powerful concept of Dungeon Crawl.\n" - "You can inscribe items to differentiate them, or to comment on them, \n" - "but also to set rules for item interaction. If you are new to Crawl, \n" - "you can safely ignore this feature, though."; - - longtext = true; - } - - if (autoinscribe && wherey() <= get_number_of_lines() - 6) - { - text << "\n" - "Artefacts can be autoinscribed to give a brief overview of their \n" - "known properties."; - - longtext = true; - } - text << "\n" - "(In the main screen, press <w>?6</w> for more information.)\n"; - text << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - - formatted_string::parse_string(text.str()).display(); - - // Ask a second time, if it's been a longish interruption. - if (longtext && !prompt.empty() && wherey() <= get_number_of_lines() - 2) - formatted_string::parse_string(prompt).display(); -} - -// FIXME: With the new targeting system, the hints for interesting monsters -// and features ("right-click/press v for more information") are no -// longer getting displayed. -// Players might still end up e'x'aming and particularly clicking on -// but it's a lot more hit'n'miss now. -bool tutorial_pos_interesting(int x, int y) -{ - return (cloud_type_at(coord_def(x, y)) != CLOUD_NONE - || _water_is_disturbed(x, y) - || _tutorial_feat_interesting(grd[x][y])); -} - -static bool _tutorial_feat_interesting(dungeon_feature_type feat) -{ - // Altars and branch entrances are always interesting. - if (feat >= DNGN_ALTAR_FIRST_GOD && feat <= DNGN_ALTAR_LAST_GOD) - return (true); - if (feat >= DNGN_ENTER_FIRST_BRANCH && feat <= DNGN_ENTER_LAST_BRANCH) - return (true); - - switch (feat) - { - // So are statues, traps, and stairs. - case DNGN_ORCISH_IDOL: - case DNGN_GRANITE_STATUE: - case DNGN_TRAP_MAGICAL: - case DNGN_TRAP_MECHANICAL: - case DNGN_TRAP_NATURAL: - 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_ESCAPE_HATCH_DOWN: - case DNGN_ESCAPE_HATCH_UP: - case DNGN_ENTER_PORTAL_VAULT: - return (true); - default: - return (false); - } -} - -void tutorial_describe_pos(int x, int y) -{ - _tutorial_describe_disturbance(x, y); - _tutorial_describe_cloud(x, y); - _tutorial_describe_feature(x, y); -} - -static void _tutorial_describe_feature(int x, int y) -{ - const dungeon_feature_type feat = grd[x][y]; - const coord_def where(x, y); - - std::ostringstream ostr; - ostr << "\n\n<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - - bool boring = false; - - switch (feat) - { - case DNGN_ORCISH_IDOL: - case DNGN_GRANITE_STATUE: - ostr << "It's just a harmless statue - or is it?\nEven if not " - "a danger by themselves, statues often mark special " - "areas, dangerous ones or ones harbouring treasure."; - 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>Ctrl</w> " - "and the direction of the trap. Note that this usually " - "causes the trap to go off, so it can be quite a " - "dangerous task.\n\n" - - "You can safely pass over a mechanical trap if " - "you're flying or levitating."; - } - else - { - ostr << "Magical traps can't be disarmed, and unlike " - "mechanical traps you can't avoid tripping them " - "by levitating or flying over them."; - } - Tutorial.tutorial_events[TUT_SEEN_TRAP] = false; - break; - - case DNGN_TRAP_NATURAL: // only shafts for now - ostr << "The dungeon contains a number of natural obstacles such " - "as shafts, which lead one to three levels down. They " - "can't be disarmed, but you can safely pass over them " - "if you're levitating or flying.\n" - "If you want to jump down there, use <w>></w> to do so. " - "Be warned that getting back here might be difficult."; - Tutorial.tutorial_events[TUT_SEEN_TRAP] = false; - 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>></w>). To get back to this level again, " - "press <w><<</w> while standing on the upstairs."; -#ifdef USE_TILE - ostr << " In Tiles, you can achieve the same, in either direction, " - "by clicking the <w>left mouse button</w> while pressing " - "<w>Shift</w>. "; -#endif - - if (is_unknown_stair(where)) - { - ostr << "\n\nYou have not yet passed through this particular " - "set of stairs. "; - } - - Tutorial.tutorial_events[TUT_SEEN_STAIRS] = false; - break; - - case DNGN_STONE_STAIRS_UP_I: - case DNGN_STONE_STAIRS_UP_II: - case DNGN_STONE_STAIRS_UP_III: - if (you.absdepth0 < 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 (shallower) level by " - "following these up (<w><<</w>). This is ideal for " - "retreating or finding a safe resting spot, since the " - "previous level will have less monsters and monsters " - "on this level can't follow you up unless they're " - "standing right next to you. To get back to this " - "level again, press <w>></w> while standing on the " - "downstairs."; -#ifdef USE_TILE - ostr << " In Tiles, you can perform either action simply by " - "clicking the <w>left mouse button</w> while pressing " - "<w>Shift</w> instead. "; -#endif - if (is_unknown_stair(where)) - { - ostr << "\n\nYou have not yet passed through this " - "particular set of stairs. "; - } - } - Tutorial.tutorial_events[TUT_SEEN_STAIRS] = false; - break; - - case DNGN_ESCAPE_HATCH_DOWN: - case DNGN_ESCAPE_HATCH_UP: - ostr << "Escape hatches can be used to quickly leave a level with " - "<w><<</w> and <w>></w>, respectively. Note that you will " - "usually be unable to return right away."; - - Tutorial.tutorial_events[TUT_SEEN_ESCAPE_HATCH] = false; - break; - - case DNGN_ENTER_PORTAL_VAULT: - ostr << "This " << _describe_portal(where); - Tutorial.tutorial_events[TUT_SEEN_PORTAL] = false; - break; - - case DNGN_CLOSED_DOOR: - case DNGN_DETECTED_SECRET_DOOR: - if (!Tutorial.tut_explored) - { - ostr << "\nTo avoid accidentally opening a door you'd rather " - "remain closed during travel or autoexplore, you can " - "mark it with an exclusion from the map view " - "(<w>X</w>) with <w>ee</w> while your cursor is on the " - "grid in question. Such an exclusion will prevent " - "autotravel from ever entering that grid until you " - "remove the exclusion with another press of <w>Xe</w>."; - } - break; - - default: - if (feat >= DNGN_ALTAR_FIRST_GOD && feat <= DNGN_ALTAR_LAST_GOD) - { - god_type altar_god = feat_altar_god(feat); - - // I think right now Sif Muna is the only god for whom - // you can find altars early and who may refuse to accept - // worship by one of the tutorial characters. (jpeg) - if (altar_god == GOD_SIF_MUNA - && !player_can_join_god(altar_god)) - { - ostr << "As <w>p</w>raying on the altar will tell you, " - << god_name(altar_god) << " only accepts worship from " - "those who have already dabbled in magic. You can " - "find out more about this god by searching the " - "database with <w>?/g</w>.\n" - "For other gods, you'll be able to join the faith " - "by <w>p</w>raying at their altar."; - } - else 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.\n" - "You can get information about <w>" - << god_name(altar_god) - << "</w> by pressing <w>p</w> while standing on the " - "altar. Before taking up the responding faith " - "you'll be asked for confirmation."; - } - else if (you.religion == altar_god) - { - if (god_likes_items(you.religion)) - { - ostr << "If " - << god_name(you.religion) - << " likes to have certain items or corpses " - "sacrificed on altars, any appropriate item " - "<w>d</w>ropped on an altar during prayer, or " - "already lying on an altar when you start " - "<w>p</w>raying will be automatically " - "sacrificed to " - << god_name(you.religion) - << "."; - } - else // If we don't have anything to say, return early. - return; - } - 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 <w>"; - ostr << god_name(altar_god); - ostr << "</w>, press <w>p</w> 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 <w>^</w>" -#ifdef USE_TILE - ", or click with your <w>right mouse button</w> " - "on your avatar while pressing <w>Shift</w>" -#endif - "."; - } - Tutorial.tutorial_events[TUT_SEEN_ALTAR] = false; - break; - } - else if (feat >= DNGN_ENTER_FIRST_BRANCH - && feat <= DNGN_ENTER_LAST_BRANCH) - { - 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; - } - else - { - // Describe blood-stains even for boring features. - if (!is_bloodcovered(where)) - return; - boring = true; - } - } - - if (is_bloodcovered(where)) - { - if (!boring) - ostr << "\n\n"; - - ostr << "Many forms of combat and some forms of magical attack " - "will splatter the surrounings with blood (if the victim has " - "any blood, that is). Some monsters can smell blood from " - "a distance and will come looking for whatever the blood " - "was spilled from."; - } - - ostr << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - - std::string broken = ostr.str(); - linebreak_string2(broken, _get_tutorial_cols()); - display_tagged_block(broken); -} - -static void _tutorial_describe_cloud(int x, int y) -{ - cloud_type ctype = cloud_type_at(coord_def(x, y)); - if (ctype == CLOUD_NONE) - return; - - std::string cname = cloud_name(env.cgrid(coord_def(x, y))); - - std::ostringstream ostr; - - ostr << "\n\n<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - - ostr << "The " << cname << " "; - - if (ends_with(cname, "s")) - ostr << "are "; - else - ostr << "is "; - - bool need_cloud = false; - switch (ctype) - { - case CLOUD_BLACK_SMOKE: - case CLOUD_GREY_SMOKE: - case CLOUD_BLUE_SMOKE: - case CLOUD_TLOC_ENERGY: - case CLOUD_PURPLE_SMOKE: - case CLOUD_MIST: - case CLOUD_MAGIC_TRAIL: - ostr << "harmless. "; - break; - - default: - if (!is_damaging_cloud(ctype, true)) - { - ostr << "currently harmless, but that could change at some point. " - "Check the overview screen (<w>%</w>) to view your " - "resistances."; - need_cloud = true; - } - else - { - ostr << "probably dangerous, and you should stay out of it if you " - "can. "; - } - } - - if (is_opaque_cloud(env.cgrid[x][y])) - { - ostr << (need_cloud? "\nThis cloud" : "It") - << " is opaque. If two or more opaque clouds are between " - "you and a square you won't be able to see anything in that " - "square."; - } - - ostr << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - - std::string broken = ostr.str(); - linebreak_string2(broken, _get_tutorial_cols()); - display_tagged_block(broken); -} - -static void _tutorial_describe_disturbance(int x, int y) -{ - if (!_water_is_disturbed(x, y)) - return; - - std::ostringstream ostr; - - ostr << "\n\n<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - - ostr << "The strange disturbance means that there's a monster hiding " - "under the surface of the shallow water. Other than non-submerged " - "monsters, a submerged monster will not be autotargeted when doing " - "a ranged attack while there are other, visible targets in sight. " - "Of course you can still target it manually if you wish to."; - - ostr << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - - std::string broken = ostr.str(); - linebreak_string2(broken, _get_tutorial_cols()); - display_tagged_block(broken); -} - -static bool _water_is_disturbed(int x, int y) -{ - const coord_def c(x,y); - const monsters *mon = monster_at(c); - - if (!mon || grd(c) != DNGN_SHALLOW_WATER || !you.see_cell(c)) - return (false); - - return (!mon->visible_to(&you) && !mons_flies(mon)); -} - -bool tutorial_monster_interesting(const monsters *mons) -{ - if (mons_is_unique(mons->type) || mons->type == MONS_PLAYER_GHOST) - return (true); - - // Highlighted in some way. - if (_mons_is_highlighted(mons)) - return (true); - - // The monster is (seriously) out of depth. - if (you.level_type == LEVEL_DUNGEON - && mons_level(mons->type) >= you.absdepth0 + 8) - { - return (true); - } - return (false); -} - -void tutorial_describe_monster(const monsters *mons, bool has_stat_desc) -{ - std::ostringstream ostr; - ostr << "\n\n<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - - bool dangerous = false; - if (mons_is_unique(mons->type)) - { - ostr << "Did you think you were the only adventurer in the dungeon? " - "Well, you thought wrong! These unique adversaries often " - "possess skills that normal monster wouldn't, so be " - "careful.\n\n"; - dangerous = true; - } - else if (mons->type == MONS_PLAYER_GHOST) - { - ostr << "The ghost of a deceased adventurer, it would like nothing " - "better than to send you the same way.\n\n"; - dangerous = true; - } - else - { - const char ch = mons_base_char(mons->type); - if (ch >= '1' && ch <= '5') - { - ostr << "This monster is a demon of the " - << (ch == '1' ? "highest" : - ch == '2' ? "second-highest" : - ch == '3' ? "middle" : - ch == '4' ? "second-lowest" : - ch == '5' ? "lowest" - : "buggy") - << " tier.\n\n"; - } - - // Don't call friendly monsters dangerous. - if (!mons_att_wont_attack(mons->attitude)) - { - // 8 is the default value for the note-taking of OOD monsters. - // Since I'm too lazy to come up with any measurement of my own - // I'll simply reuse that one. - const int level_diff = mons_level(mons->type) - (you.absdepth0 + 8); - - 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"; - dangerous = true; - } - } - } - - if (mons->berserk()) - { - ostr << "A berserking monster is bloodthirsty and fighting madly. " - "Such a blood rage makes it particularly dangerous!\n\n"; - dangerous = true; - } - - // Monster is highlighted. - if (mons->friendly()) - { - ostr << "Friendly monsters will follow you around and attempt to aid " - "you in battle. You can order your allies by <w>t</w>alking " - "to them."; - - if (!mons_att_wont_attack(mons->attitude)) - { - ostr << "\n\nHowever, it is only <w>temporarily</w> friendly, " - "and will become dangerous again when this friendliness " - "wears off."; - } - } - else if (dangerous) - { - if (!Tutorial.tut_explored && mons->foe != MHITYOU) - { - ostr << "You can easily mark its square as dangerous to avoid " - "accidentally entering into its field of view when using " - "auto-explore or auto-travel. To do so, enter targeting " - "mode with <w>x</w> and then press <w>e</w> when your " - "cursor is hovering over the monster's grid. Doing so will " - "mark this grid and all surrounding ones within a radius " - "of 8 as \"excluded\" ones that explore or travel modus " - "won't enter."; - } - else - { - ostr << "This might be a good time to run away"; - - if (you.religion == GOD_TROG && !you.berserk() - && !you.duration[DUR_EXHAUSTED] - && you.hunger_state >= HS_SATIATED) - { - ostr << " or apply your Berserk <w>a</w>bility"; - } - ostr << "."; - } - } - else if (Options.stab_brand != CHATTR_NORMAL - && mons_looks_stabbable(mons)) - { - ostr << "Apparently " - << mons_pronoun((monster_type) mons->type, PRONOUN_NOCAP) - << " has not noticed you - yet. Note that you do not have to " - "engage every monster you meet. Sometimes, discretion is the " - "better part of valour."; - } - else if (Options.may_stab_brand != CHATTR_NORMAL - && mons_looks_distracted(mons)) - { - ostr << "Apparently " - << mons_pronoun((monster_type) mons->type, PRONOUN_NOCAP) - << " has been distracted by something. You could use this " - "opportunity to sneak up on this monster - or to sneak away."; - } - - if (!dangerous && !has_stat_desc) - { - ostr << "\nThis monster doesn't appear to have any resistances or " - "susceptibilities. It cannot fly and is of average speed. " - "Examining other, possibly more high-level monsters can give " - "important clues as to how to deal with them."; - } - - ostr << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; - - std::string broken = ostr.str(); - linebreak_string2(broken, _get_tutorial_cols()); - display_tagged_block(broken); -} - -void tutorial_observe_cell(const coord_def& gc) -{ - if (feat_is_escape_hatch(grd(gc))) - learned_something_new(TUT_SEEN_ESCAPE_HATCH, gc); - else if (feat_is_branch_stairs(grd(gc))) - learned_something_new(TUT_SEEN_BRANCH, gc); - else if (is_feature('>', gc)) - learned_something_new(TUT_SEEN_STAIRS, gc); - else if (is_feature('_', gc)) - learned_something_new(TUT_SEEN_ALTAR, gc); - else if (is_feature('^', gc)) - learned_something_new(TUT_SEEN_TRAP, gc); - else if (feat_is_closed_door(grd(gc))) - learned_something_new(TUT_SEEN_DOOR, gc); - else if (grd(gc) == DNGN_ENTER_SHOP) - learned_something_new(TUT_SEEN_SHOP, gc); - else if (grd(gc) == DNGN_ENTER_PORTAL_VAULT) - learned_something_new(TUT_SEEN_PORTAL, gc); - - const int it = you.visible_igrd(gc); - if (it != NON_ITEM) - { - const item_def& item(mitm[it]); - - if (Options.feature_item_brand != CHATTR_NORMAL - && (is_feature('>', gc) || is_feature('<', gc))) - { - learned_something_new(TUT_STAIR_BRAND, gc); - } - else if (Options.trap_item_brand != CHATTR_NORMAL - && is_feature('^', gc)) - { - learned_something_new(TUT_TRAP_BRAND, gc); - } - else if (Options.heap_brand != CHATTR_NORMAL && item.link != NON_ITEM) - learned_something_new(TUT_HEAP_BRAND, gc); - } -} |