From c74a157f8f99fa7fffa2547c92a76d8cb7eecdb5 Mon Sep 17 00:00:00 2001 From: j-p-e-g Date: Sun, 18 Mar 2007 14:57:33 +0000 Subject: Changes to the overview screen (%) - added abilities and made it scrollable. Also, some more clean-up of tutorial.cc. As this involved a lot of spacing changes it was easier to just replace the file as a whole. Sorry about that. There are still some differences between the Linux and Windows versions that I don't really understand but using get_number_of_cols helps a lot. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1055 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/source/abl-show.cc | 21 + crawl-ref/source/abl-show.h | 1 + crawl-ref/source/command.cc | 24 +- crawl-ref/source/items.cc | 2 +- crawl-ref/source/menu.cc | 2 +- crawl-ref/source/output.cc | 1050 ++++++++--------- crawl-ref/source/output.h | 3 - crawl-ref/source/player.cc | 4 +- crawl-ref/source/tutorial.cc | 2566 +++++++++++++++++++++--------------------- crawl-ref/source/tutorial.h | 51 +- crawl-ref/source/view.cc | 15 + crawl-ref/source/view.h | 2 + 12 files changed, 1841 insertions(+), 1900 deletions(-) (limited to 'crawl-ref/source') diff --git a/crawl-ref/source/abl-show.cc b/crawl-ref/source/abl-show.cc index ba15074f49..5f6e065112 100644 --- a/crawl-ref/source/abl-show.cc +++ b/crawl-ref/source/abl-show.cc @@ -294,6 +294,27 @@ const char * get_ability_name_by_index( char index ) return (abil.name); } +std::string print_abilities() +{ + std::string text = "\na: "; + + bool have_any = false; + if (generate_abilities()) + { + for (int i = 0; i < 52; ++i) + if (Curr_abil[i].which != ABIL_NON_ABILITY) + { + if (have_any) + text += ", "; + text += get_ability_name_by_index(i); + have_any = true; + } + } + if (!have_any) + text += "no special abilities"; + + return text; +} const std::string make_cost_description( const struct ability_def &abil ) /***********************************************************************/ diff --git a/crawl-ref/source/abl-show.h b/crawl-ref/source/abl-show.h index e901335fa1..848ac29271 100644 --- a/crawl-ref/source/abl-show.h +++ b/crawl-ref/source/abl-show.h @@ -42,6 +42,7 @@ const std::string make_cost_description( const struct ability_def &abil ); bool activate_ability( void ); // handles all special abilities now char show_abilities( void ); bool generate_abilities( void ); +std::string print_abilities( void ); std::vector get_ability_names( void ); diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc index 3431591f1d..ddfe34fa6f 100644 --- a/crawl-ref/source/command.cc +++ b/crawl-ref/source/command.cc @@ -1055,8 +1055,8 @@ void list_tutorial_help() // Page size is number of lines - one line for --more-- prompt. cols.set_pagesize(get_number_of_lines()); - cols.add_formatted( - 0, + unsigned short ch, colour; + std::string text = "Item types (and common commands)\n" ") : hand weapons (wield)\n" "( : missiles (throw or fire)\n" @@ -1066,10 +1066,17 @@ void list_tutorial_help() "! : potions (quaff)\n" "= : rings (Put on and Remove)\n" "\" : amulets (Put on and Remove)\n" - "/ : wands (zap)\n" - // is it possible to replace that with e.g. Options.char_table[DNGN_ITEM_BOOK] - "+, : : books (read, Memorise and Zap)\n" - "\\, | : staves, rods (wield and Evoke)\n" + "/ : wands (zap)\n" + ""; + get_item_symbol(DNGN_ITEM_BOOK, &ch, &colour); + snprintf(info, INFO_SIZE, "%c", ch); + text += info; + text += " : books (read, Memorise and Zap)\n" + ""; + get_item_symbol(DNGN_ITEM_STAVE, &ch, &colour); + snprintf(info, INFO_SIZE, "%c", ch); + text += info; + text += " : staves, rods (wield and Evoke)\n" "\n" "Movement and attacking\n" "Use the numpad for movement (try both\n" @@ -1078,10 +1085,13 @@ void list_tutorial_help() " yubn : diagonal movement.\n" "Walking into a monster will attack it\n" "with the wielded weapon or barehanded.\n" - "For ranged attacks use either\n" + "For ranged attacks use either\n" "f to launch missiles (like arrows)\n" "t to throw items by hand (like darts)\n" "Z to cast spells (Z? lists spells).\n", + + cols.add_formatted( + 0, text.c_str(), true, true, cmdhelp_textfilter); cols.add_formatted( diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc index eb62d1fc99..946ae0b55c 100644 --- a/crawl-ref/source/items.cc +++ b/crawl-ref/source/items.cc @@ -1568,7 +1568,7 @@ int move_item_to_player( int obj, int quant_got, bool quiet ) if (Options.tutorial_left) { taken_new_item(item.base_type); - if (is_random_artefact(you.inv[freeslot])) + if (is_artefact(item) || get_equip_desc( item ) != ISFLAG_NO_DESC) learned_something_new(TUT_SEEN_RANDART); } diff --git a/crawl-ref/source/menu.cc b/crawl-ref/source/menu.cc index 163f50cb4d..bc7ee61821 100644 --- a/crawl-ref/source/menu.cc +++ b/crawl-ref/source/menu.cc @@ -1178,7 +1178,7 @@ int linebreak_string2( std::string& s, int maxcol ) if ( s[loc] == '\n' ) xpos = 0; // hard linebreak - else if ( xpos > maxcol ) + else if ( xpos >= maxcol ) { if (spacepos >= xpos-maxcol) { diff --git a/crawl-ref/source/output.cc b/crawl-ref/source/output.cc index 967b13bb9b..2d952b5919 100644 --- a/crawl-ref/source/output.cc +++ b/crawl-ref/source/output.cc @@ -22,10 +22,12 @@ #include "externs.h" +#include "abl-show.h" #include "format.h" #include "fight.h" #include "initfile.h" #include "itemname.h" +#include "menu.h" #include "ouch.h" #include "player.h" #include "religion.h" @@ -962,25 +964,20 @@ std::vector get_full_detail(bool calc_unid) return cols.formatted_lines(); } -// new status overview screen, including stats, mutations etc. -// TODO: add abilities +static std::string status_mut_abilities(void); + +// new scrollable status overview screen, +// including stats, mutations etc. void print_overview_screen() { - clrscr(); - - formatted_string fs = get_full_detail2(false); - gotoxy(1, 1); - fs.display(); - - getch(); - redraw_screen(); -} + bool calc_unid = false; + formatted_scroller cmd_help; + // Set flags, and don't use easy exit. + cmd_help.set_flags(MF_NOSELECT | MF_NOWRAP, false); + cmd_help.set_more( formatted_string::parse_string( + "[ + : Page down. - : Page up. Esc exits.]")); -formatted_string get_full_detail2(bool calc_unid) -{ std::string text; - formatted_string output; - char title[50]; snprintf(title, sizeof title, " the %s ", player_title()); @@ -1004,52 +1001,305 @@ formatted_string get_full_detail2(bool calc_unid) int linelength = strlen(you.your_name) + strlen(title) + strlen(race_class) + strlen(time_turns); - for (int count = 0; linelength >= 80 && count < 2; count++ ) + for (int count = 0; linelength >= get_number_of_cols() && count < 2; count++ ) { switch (count) { - case 0: - snprintf(race_class, sizeof race_class, - "(%s%s)", - get_species_abbrev(you.species), - get_class_abbrev(you.char_class) ); - break; - case 1: - strcpy(title, ""); - break; - default: - break; + case 0: + snprintf(race_class, sizeof race_class, + "(%s%s)", + get_species_abbrev(you.species), + get_class_abbrev(you.char_class) ); + break; + case 1: + strcpy(title, ""); + break; + default: + break; } linelength = strlen(you.your_name) + strlen(title) + strlen(race_class) + strlen(time_turns); } - output.textcolor(YELLOW); - output.cprintf("%s%s%s", you.your_name, title, race_class); - output.gotoxy(get_number_of_cols() - strlen(time_turns), -1); - output.cprintf("%s", time_turns); - output.textcolor(LIGHTGREY); - output.cprintf(EOL); + text = ""; + snprintf(info, INFO_SIZE, "%s%s%s", you.your_name, title, race_class); + text += info; + int k = get_number_of_cols() - linelength -1; + text += std::string(k, ' '); + snprintf(info, INFO_SIZE, "%s", time_turns); + text += info; + text += "\n\n"; + + cmd_help.add_text(text); + + char buf[1000]; + // 3 columns, splits at columns 32, 52 + column_composer cols1(4, 16, 27, 38); + + if (!player_rotted()) + snprintf(buf, sizeof buf, "HP %3d/%d",you.hp,you.hp_max); + else + snprintf(buf, sizeof buf, "HP %3d/%d (%d)", + you.hp, you.hp_max, you.hp_max + player_rotted() ); + + cols1.add_formatted(0, buf, false); + + snprintf(buf, sizeof buf, "MP %3d/%d", + you.magic_points, you.max_magic_points); + + cols1.add_formatted(0, buf, false); + + if (you.strength == you.max_strength) + snprintf(buf, sizeof buf, "Str %3d", you.strength); + else + snprintf(buf, sizeof buf, "Str %3d (%d)", + you.strength, you.max_strength); + cols1.add_formatted(1, buf, false); + + if (you.intel == you.max_intel) + snprintf(buf, sizeof buf, "Int %3d", you.intel); + else + snprintf(buf, sizeof buf, "Int %3d (%d)", + you.intel, you.max_intel); + cols1.add_formatted(1, buf, false); + + if (you.dex == you.max_dex) + snprintf(buf, sizeof buf, "Dex %3d", you.dex); + else + snprintf(buf, sizeof buf, "Dex %3d (%d)", + you.dex, you.max_dex); + cols1.add_formatted(1, buf, false); + + snprintf(buf, sizeof buf, + "AC %3d\n" + "EV %3d\n" + "Sh %3d\n", + player_AC(), + player_evasion(), + player_shield_class()); + cols1.add_formatted(2, buf, false); + + char god_colour_tag[20]; + god_colour_tag[0] = 0; + std::string godpowers(god_name(you.religion)); + if ( you.religion != GOD_NO_GOD ) + { + if ( player_under_penance() ) + strcpy(god_colour_tag, "*"); + else + { + snprintf(god_colour_tag, sizeof god_colour_tag, "<%s>", + colour_to_str(god_colour(you.religion))); + // piety rankings + int prank = piety_rank() - 1; + if ( prank < 0 ) + prank = 0; + // Careful about overflow. We erase some of the god's name + // if necessary. + godpowers = godpowers.substr(0, 17 - prank) + + std::string(prank, '*'); + } + } + + snprintf(buf, sizeof buf, + "Experience: %d/%lu (%d)\n" + "Spells: %2d memorised, %2d level%s left\n" + "God: %s%s Gold: %d\n", + you.experience_level, you.experience, you.exp_available, + you.spell_no, player_spell_levels(), (player_spell_levels() == 1) ? "" : "s", + god_colour_tag, godpowers.c_str(), you.gold); + cols1.add_formatted(3, buf, false); + + std::vector blines = cols1.formatted_lines(); + unsigned i; + for (i = 0; i < blines.size(); ++i ) + cmd_help.add_item_formatted_string(blines[i]); + cmd_help.add_text("\n"); + + // 3 columns, splits at columns 21, 38 + column_composer cols(3, 21, 38); + + const int rfire = player_res_fire(calc_unid); + const int rcold = player_res_cold(calc_unid); + const int rlife = player_prot_life(calc_unid); + const int rpois = player_res_poison(calc_unid); + const int relec = player_res_electricity(calc_unid); + const int rsust = player_sust_abil(calc_unid); + const int rmuta = wearing_amulet(AMU_RESIST_MUTATION, calc_unid); + const int rslow = wearing_amulet(AMU_RESIST_SLOW, calc_unid); + + snprintf(buf, sizeof buf, + "%sRes.Fire : %s\n" + "%sRes.Cold : %s\n" + "%sLife Prot.: %s\n" + "%sRes.Poison: %s\n" + "%sRes.Elec. : %s\n" + "\n" + "%sSust.Abil.: %s\n" + "%sRes.Mut. : %s\n" + "%sRes.Slow : %s\n", + determine_color_string(rfire), itosym3(rfire), + determine_color_string(rcold), itosym3(rcold), + determine_color_string(rlife), itosym3(rlife), + determine_color_string(rpois), itosym1(rpois), + determine_color_string(relec), itosym1(relec), + determine_color_string(rsust), itosym1(rsust), + determine_color_string(rmuta), itosym1(rmuta), + determine_color_string(rslow), itosym1(rslow)); + cols.add_formatted(0, buf, false); + + int saplevel = 0; + switch (you.species) + { + case SP_GHOUL: + saplevel = 3; + snprintf(buf, sizeof buf, "%sSaprovore : %s", + determine_color_string(3), itosym3(3) ); + break; + + case SP_KOBOLD: + case SP_TROLL: + saplevel = 2; + snprintf(buf, sizeof buf, "%sSaprovore : %s", + determine_color_string(2), itosym3(2) ); + break; + + case SP_HILL_ORC: + case SP_OGRE: + saplevel = 1; + break; + default: + saplevel = 0; + break; + } + const char* pregourmand; + const char* postgourmand; + if ( wearing_amulet(AMU_THE_GOURMAND, calc_unid) ) + { + pregourmand = "Gourmand : "; + postgourmand = itosym1(1); + saplevel = 1; + } + else + { + pregourmand = "Saprovore : "; + postgourmand = itosym3(saplevel); + } + snprintf(buf, sizeof buf, "%s%s%s", + determine_color_string(saplevel), pregourmand, postgourmand); + cols.add_formatted(0, buf, false); + + + const int rinvi = player_see_invis(calc_unid); + const int rward = wearing_amulet(AMU_WARDING, calc_unid) || + (you.religion == GOD_VEHUMET && you.duration[DUR_PRAYER] && + !player_under_penance() && you.piety >= piety_breakpoint(2)); + const int rcons = wearing_amulet(AMU_CONSERVATION, calc_unid); + const int rcorr = wearing_amulet(AMU_RESIST_CORROSION, calc_unid); + const int rclar = wearing_amulet(AMU_CLARITY, calc_unid); + snprintf(buf, sizeof buf, + "%sSee Invis. : %s\n" + "%sWarding : %s\n" + "%sConserve : %s\n" + "%sRes.Corr. : %s\n" + "%sClarity : %s\n" + "\n", + determine_color_string(rinvi), itosym1(rinvi), + determine_color_string(rward), itosym1(rward), + determine_color_string(rcons), itosym1(rcons), + determine_color_string(rcorr), itosym1(rcorr), + determine_color_string(rclar), itosym1(rclar)); + cols.add_formatted(1, buf, false); - std::vector vfs = get_stat_info(); - for (unsigned int i = 0; i < vfs.size(); i++) + if ( scan_randarts(RAP_PREVENT_TELEPORTATION, calc_unid) ) + snprintf(buf, sizeof buf, "\n%sPrev.Telep.: %s", + determine_color_string(-1), itosym1(1)); + else { - output += vfs[i]; - output += formatted_string::parse_string("\n"); + const int rrtel = player_teleport(calc_unid); + snprintf(buf, sizeof buf, "\n%sRnd.Telep. : %s", + determine_color_string(rrtel), itosym1(rrtel)); } - output += formatted_string::parse_string("\n"); + cols.add_formatted(1, buf, false); + + const int rctel = player_control_teleport(calc_unid); + const int rlevi = player_is_levitating(); + const int rcfli = wearing_amulet(AMU_CONTROLLED_FLIGHT, calc_unid); + snprintf(buf, sizeof buf, + "%sCtrl.Telep.: %s\n" + "%sLevitation : %s\n" + "%sCtrl.Flight: %s\n", + determine_color_string(rctel), itosym1(rctel), + determine_color_string(rlevi), itosym1(rlevi), + determine_color_string(rcfli), itosym1(rcfli)); + cols.add_formatted(1, buf, false); - vfs = get_res_info(calc_unid); - for (unsigned int i = 0; i < vfs.size(); i++) { - output += vfs[i]; - output += formatted_string::parse_string("\n"); + char str_pass[ITEMNAME_SIZE]; + const int e_order[] = + { + EQ_WEAPON, EQ_BODY_ARMOUR, EQ_SHIELD, EQ_HELMET, EQ_CLOAK, + EQ_GLOVES, EQ_BOOTS, EQ_AMULET, EQ_RIGHT_RING, EQ_LEFT_RING + }; + + for(i = 0; i < NUM_EQUIP; i++) + { + int eqslot = e_order[i]; + const char *slot = equip_slot_to_name( eqslot ); + if (eqslot == EQ_LEFT_RING || eqslot == EQ_RIGHT_RING) + slot = "Ring"; + else if (eqslot == EQ_BOOTS && + (you.species == SP_CENTAUR || you.species == SP_NAGA)) + slot = "Barding"; + + if ( you.equip[ e_order[i] ] != -1) + { + const int inum = you.equip[e_order[i]]; + in_name( inum, DESC_PLAIN, str_pass, true ); + str_pass[38] = 0; // truncate + const char* colname = colour_to_str(you.inv[inum].colour); + snprintf(buf, sizeof buf, "%-7s: <%s>%s", + slot, colname, str_pass, colname); + } + else + { + if (e_order[i] == EQ_WEAPON) + { + if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS) + snprintf(buf, sizeof buf, "%-7s: Blade Hands", slot); + else if (you.skills[SK_UNARMED_COMBAT]) + snprintf(buf, sizeof buf, "%-7s: Unarmed", slot); + else + snprintf(buf, sizeof buf, "%-7s:", slot); + } + else + { + snprintf(buf, sizeof buf, "%-7s:", slot); + } + } + cols.add_formatted(2, buf, false); + } } + blines = cols.formatted_lines(); + + for (i = 0; i < blines.size(); ++i ) + cmd_help.add_item_formatted_string(blines[i]); + cmd_help.add_text("\n"); + + cmd_help.add_text(status_mut_abilities()); + cmd_help.show(); + redraw_screen(); +} + +// creates rows of short descriptions for current +// status, mutations and abilities +std::string status_mut_abilities() +{ //---------------------------- // print status information //---------------------------- - text = "\n@: "; + std::string text = "@: "; if (you.burden_state == BS_ENCUMBERED) text += "burdened, "; @@ -1079,6 +1329,7 @@ formatted_string get_full_detail2(bool calc_unid) if (you.duration[DUR_REGENERATION]) text += "regenerating, "; +// not used as resistance part already says so // if (you.duration[DUR_INSULATION]) // text += "insulated, "; @@ -1088,24 +1339,27 @@ formatted_string get_full_detail2(bool calc_unid) if (you.duration[DUR_STONESKIN]) text += "stone skin, "; +// resistance part already says so // if (you.duration[DUR_CONTROLLED_FLIGHT]) // text += "control flight, "; if (you.duration[DUR_TELEPORT]) text += "about to teleport, "; +// resistance part already says so // if (you.duration[DUR_CONTROL_TELEPORT]) // text += "control teleport, "; if (you.duration[DUR_DEATH_CHANNEL]) text += "death channel, "; - if (you.duration[DUR_FORESCRY]) //jmf: added 19mar2000 + if (you.duration[DUR_FORESCRY]) text += "forewarned, "; - if (you.duration[DUR_SILENCE]) //jmf: added 27mar2000 + if (you.duration[DUR_SILENCE]) text += "radiating silence, "; +// resistance part already says so // if (you.duration[DUR_SEE_INVISIBLE]) // text += "see invisible, "; @@ -1135,7 +1389,7 @@ formatted_string get_full_detail2(bool calc_unid) text += (you.poisoning > 10) ? "extremely" : (you.poisoning > 5) ? "very" : (you.poisoning > 3) ? "quite" - : "mildly"; + : "mildly"; text += " poisoned, "; } @@ -1149,7 +1403,6 @@ formatted_string get_full_detail2(bool calc_unid) if (you.rotting || you.species == SP_GHOUL) text += "rotting, "; -// contaminate_player( 0, true ); if (you.confusing_touch) text += "confusing touch, "; @@ -1200,56 +1453,38 @@ formatted_string get_full_detail2(bool calc_unid) text += info; - size_t start = 0, pos = 0, oldpos = 0; - for (; pos < strlen(text.c_str()); oldpos++) { - - // get next "word" - pos = text.find(' ', oldpos); - if (pos - start >= 80 && pos < strlen(text.c_str())) { - output += formatted_string::parse_string( - text.substr(start, oldpos-start)); - output += formatted_string::parse_string(EOL); - start = oldpos; - } - oldpos = pos; - } - - output += formatted_string::parse_string(text.substr(start, pos-start)); - text = ""; - switch (you.attribute[ATTR_TRANSFORMATION]) { - case TRAN_SPIDER: - text += "\nYou are in spider-form."; - break; - case TRAN_BLADE_HANDS: - text += "\nYou have blades for hands."; - break; - case TRAN_STATUE: - text += "\nYou are a statue."; - break; - case TRAN_ICE_BEAST: - text += "\nYou are an ice creature."; - break; - case TRAN_DRAGON: - text += "\nYou are in dragon-form."; - break; - case TRAN_LICH: - text += "\nYou are in lich-form."; - break; - case TRAN_SERPENT_OF_HELL: - text += "\nYou are a huge demonic serpent."; - break; - case TRAN_AIR: - text += "\nYou are a cloud of diffuse gas."; - break; + case TRAN_SPIDER: + text += "\nYou are in spider-form."; + break; + case TRAN_BLADE_HANDS: + text += "\nYou have blades for hands."; + break; + case TRAN_STATUE: + text += "\nYou are a statue."; + break; + case TRAN_ICE_BEAST: + text += "\nYou are an ice creature."; + break; + case TRAN_DRAGON: + text += "\nYou are in dragon-form."; + break; + case TRAN_LICH: + text += "\nYou are in lich-form."; + break; + case TRAN_SERPENT_OF_HELL: + text += "\nYou are a huge demonic serpent."; + break; + case TRAN_AIR: + text += "\nYou are a cloud of diffuse gas."; + break; } - text += EOL; const int to_hit = calc_your_to_hit( false ) * 2; snprintf( info, INFO_SIZE, - "%s in your current equipment.", + "\n%s in your current equipment.", (to_hit < 1) ? "You are completely incapable of fighting" : (to_hit < 5) ? "Hitting even clumsy monsters is extremely awkward" : (to_hit < 10) ? "Hitting average monsters is awkward" : @@ -1259,18 +1494,16 @@ formatted_string get_full_detail2(bool calc_unid) (to_hit < 45) ? "Very agile monsters are a bit difficult to hit" : (to_hit < 60) ? "Very agile monsters are a bit hard to hit" : (to_hit < 100) ? "You feel comfortable with your ability to fight" - : "You feel confident with your ability to fight" ); + : "You feel confident with your ability to fight" ); text += info; if (you.deaths_door) text += "\nYou are standing in death's doorway."; - output += formatted_string::parse_string(text); - //---------------------------- // print mutation information //---------------------------- - text = "\nA: "; + text += "\nA: "; bool have_any = false; int AC_change = 0; @@ -1281,171 +1514,170 @@ formatted_string get_full_detail2(bool calc_unid) switch (you.species) //mv: following code shows innate abilities - if any { - case SP_MERFOLK: - text += "change form in water"; - have_any = true; - break; - - case SP_NAGA: - // breathe poison replaces spit poison: - if (!you.mutation[MUT_BREATHE_POISON]) - text += "spit poison" EOL; - else - text += "breathe poison"; + case SP_MERFOLK: + text += "change form in water"; + have_any = true; + break; + + case SP_NAGA: + // breathe poison replaces spit poison: + if (!you.mutation[MUT_BREATHE_POISON]) + text += "spit poison"; + else + text += "breathe poison"; + + have_any = true; + break; + + case SP_TROLL: + text += "saprovore 2"; + have_any = true; + break; + + case SP_GHOUL: + text += "saprovore 3"; + text += ", deterioriation"; + have_any = true; + break; + + case SP_GREY_ELF: + if (you.experience_level > 4) + { + text += "charming"; + have_any = true; + } + break; + + case SP_HIGH_ELF: + if (you.experience_level > 14) + { + text += "charming"; + have_any = true; + } + break; + + case SP_KENKU: + text += "cannot wear helmets"; + if (you.experience_level > 4) + { + text += ", able to fly"; + if (you.experience_level > 14) + text += " continuously"; + have_any = true; + } + break; + + case SP_MUMMY: + text += "in touch with death"; + + have_any = true; + break; + + case SP_GREY_DRACONIAN: + if (you.experience_level > 6) + { + text += "spiky tail"; + have_any = true; + } + break; + + case SP_GREEN_DRACONIAN: + if (you.experience_level > 6) + { + text += "breathe poison"; + have_any = true; + } + break; + + case SP_RED_DRACONIAN: + if (you.experience_level > 6) + { + text += "breathe fire"; + have_any = true; + } + break; + + case SP_WHITE_DRACONIAN: + if (you.experience_level > 6) + { + text += "breathe frost"; + have_any = true; + } + break; + + case SP_BLACK_DRACONIAN: + if (you.experience_level > 6) + { + text += "breathe lightning"; + have_any = true; + } + break; + + case SP_GOLDEN_DRACONIAN: + if (you.experience_level > 6) + { + text += "spit acid"; + text += ", acid resistance"; + have_any = true; + } + break; + + case SP_PURPLE_DRACONIAN: + if (you.experience_level > 6) + { + text += "breathe power"; + have_any = true; + } + break; + + case SP_MOTTLED_DRACONIAN: + if (you.experience_level > 6) + { + text += "breathe sticky flames"; + have_any = true; + } + break; + + case SP_PALE_DRACONIAN: + if (you.experience_level > 6) + { + text += "breathe steam"; + have_any = true; + } + break; + + case SP_KOBOLD: + text += "saprovore 2"; + have_any = true; + break; + + case SP_HILL_ORC: + case SP_OGRE: + text += "saprovore 1"; + have_any = true; + break; + } //end switch - innate abilities + // a bit more stuff + if ( (you.species >= SP_OGRE && you.species <= SP_OGRE_MAGE) || + player_genus(GENPC_DRACONIAN) || + you.species == SP_SPRIGGAN ) + { + if (have_any) + text += ", "; + text += "unfitting armour"; have_any = true; - break; + } - case SP_TROLL: - text += "saprovore 2"; - have_any = true; - break; - - case SP_GHOUL: - text += "saprovore 3"; - text += ", deterioriation"; - have_any = true; - break; - - case SP_GREY_ELF: - if (you.experience_level > 4) - { - text += "charming"; - have_any = true; - } - break; - - case SP_HIGH_ELF: - if (you.experience_level > 14) - { - text += "charming"; - have_any = true; - } - break; - - case SP_KENKU: - text += "cannot wear helmets"; - if (you.experience_level > 4) - { - text += ", able to fly"; - if (you.experience_level > 14) - text += " continuously"; - have_any = true; - } - break; - - case SP_MUMMY: - text += "in touch with death"; - -// if (you.experience_level > 12) -// text += "You can restore your body by infusing magical energy." EOL; - - have_any = true; - break; - - case SP_GREY_DRACONIAN: - if (you.experience_level > 6) - { - text += "spiky tail"; - have_any = true; - } - break; - - case SP_GREEN_DRACONIAN: - if (you.experience_level > 6) - { - text += "breathe poison"; - have_any = true; - } - break; - - case SP_RED_DRACONIAN: - if (you.experience_level > 6) - { - text += "breathe fire"; - have_any = true; - } - break; - - case SP_WHITE_DRACONIAN: - if (you.experience_level > 6) - { - text += "breathe frost"; - have_any = true; - } - break; - - case SP_BLACK_DRACONIAN: - if (you.experience_level > 6) - { - text += "breathe lightning"; - have_any = true; - } - break; - - case SP_GOLDEN_DRACONIAN: - if (you.experience_level > 6) - { - text += "spit acid"; - text += ", acid resistance"; - have_any = true; - } - break; - - case SP_PURPLE_DRACONIAN: - if (you.experience_level > 6) - { - text += "breathe power"; - have_any = true; - } - break; - - case SP_MOTTLED_DRACONIAN: - if (you.experience_level > 6) - { - text += "breathe sticky flames"; - have_any = true; - } - break; - - case SP_PALE_DRACONIAN: - if (you.experience_level > 6) - { - text += "breathe steam"; - have_any = true; - } - break; - - case SP_KOBOLD: - text += "saprovore 2" EOL; - have_any = true; - break; - - case SP_HILL_ORC: - case SP_OGRE: - text += "saprovore 1" EOL; - have_any = true; - break; - } //end switch - innate abilities - - // a bit more stuff - if ( (you.species >= SP_OGRE && you.species <= SP_OGRE_MAGE) || - player_genus(GENPC_DRACONIAN) || - you.species == SP_SPRIGGAN ) + for (unsigned i = 0; i < 100; i++) { - if (have_any) - text += ", "; - text += "unfitting armour"; - have_any = true; - } - - for (unsigned i = 0; i < 100; i++) { if (!you.mutation[i]) continue; int level = you.mutation[ i ]; - switch(i) { + switch(i) + { case MUT_TOUGH_SKIN: AC_change += level; break; @@ -1474,7 +1706,8 @@ formatted_string get_full_detail2(bool calc_unid) break; case MUT_REPULSION_FIELD: EV_change += 2*level-1; - if (level == 3) { + if (level == 3) + { if (have_any) text += ", "; text += "repel missiles"; @@ -1946,320 +2179,13 @@ formatted_string get_full_detail2(bool calc_unid) if (!have_any) text += "no striking features"; - start = 0; - oldpos = 0; - for (pos = 0; pos < strlen(text.c_str()); oldpos++) - { - // get next "word" - pos = text.find(' ', oldpos); - if (pos - start >= 80 && pos < strlen(text.c_str())) - { - output += formatted_string::parse_string(text.substr(start, oldpos-start)); - output += formatted_string::parse_string(EOL); - start = oldpos; - } - oldpos = pos; - } - - output += formatted_string::parse_string(text.substr(start, pos-start)); - //---------------------------- // print ability information //---------------------------- -/* - text = "\na: "; - - have_any = false; - for (unsigned int loopy = 0; loopy < 52; loopy++) - { - if (Curr_abil[loopy].which != ABIL_NON_ABILITY) - { - have_any = true; - - const struct ability_def abil = get_ability_def( Curr_abil[loopy].which ); - - if (have_any) - text += ", "; - snprintf(info, INFO_SIZE, "%s", abil.name ); - text += info; - } - - } - if (!have_any) - text += "no special abilities"; - - start = 0; - oldpos = 0; - for (pos = 0; pos < strlen(text.c_str()); oldpos++) - { - // get next "word" - pos = text.find(' ', oldpos); - if (pos - start >= 80 && pos < strlen(text.c_str())) - { - output += formatted_string::parse_string(text.substr(start, oldpos-start)); - output += formatted_string::parse_string(EOL); - start = oldpos; - } - oldpos = pos; - } - - output += formatted_string::parse_string(text.substr(start, pos-start)); -*/ - - return output; -} - -std::vector get_stat_info() -{ - char buf[1000]; - // 3 columns, splits at columns 32, 52 - column_composer cols(4, 16, 27, 38); - - if (!player_rotted()) - snprintf(buf, sizeof buf, "HP %3d/%d",you.hp,you.hp_max); - else - snprintf(buf, sizeof buf, "HP %3d/%d (%d)", - you.hp, you.hp_max, you.hp_max + player_rotted() ); - - cols.add_formatted(0, buf, false); - - snprintf(buf, sizeof buf, "MP %3d/%d", - you.magic_points, you.max_magic_points); - - cols.add_formatted(0, buf, false); - - if (you.strength == you.max_strength) - snprintf(buf, sizeof buf, "Str %3d", you.strength); - else - snprintf(buf, sizeof buf, "Str %3d (%d)", - you.strength, you.max_strength); - cols.add_formatted(1, buf, false); - - if (you.intel == you.max_intel) - snprintf(buf, sizeof buf, "Int %3d", you.intel); - else - snprintf(buf, sizeof buf, "Int %3d (%d)", - you.intel, you.max_intel); - cols.add_formatted(1, buf, false); - - if (you.dex == you.max_dex) - snprintf(buf, sizeof buf, "Dex %3d", you.dex); - else - snprintf(buf, sizeof buf, "Dex %3d (%d)", - you.dex, you.max_dex); - cols.add_formatted(1, buf, false); - - snprintf(buf, sizeof buf, - "AC %3d\n" - "EV %3d\n" - "Sh %3d\n", - player_AC(), - player_evasion(), - player_shield_class()); - cols.add_formatted(2, buf, false); - - char god_colour_tag[20]; - god_colour_tag[0] = 0; - std::string godpowers(god_name(you.religion)); - if ( you.religion != GOD_NO_GOD ) - { - if ( player_under_penance() ) - strcpy(god_colour_tag, "*"); - else - { - snprintf(god_colour_tag, sizeof god_colour_tag, "<%s>", - colour_to_str(god_colour(you.religion))); - // piety rankings - int prank = piety_rank() - 1; - if ( prank < 0 ) - prank = 0; - // Careful about overflow. We erase some of the god's name - // if necessary. - godpowers = godpowers.substr(0, 17 - prank) + - std::string(prank, '*'); - } - } - - snprintf(buf, sizeof buf, - "Experience: %d/%lu (%d)\n" - "Spells: %2d memorised, %2d level%s left\n" - "God: %s%s Gold: %d\n", - you.experience_level, you.experience, you.exp_available, - you.spell_no, player_spell_levels(), (player_spell_levels() == 1) ? "" : "s", - god_colour_tag, godpowers.c_str(), you.gold); - cols.add_formatted(3, buf, false); + + text += print_abilities(); + linebreak_string2(text, get_number_of_cols()); - return cols.formatted_lines(); + return text; } -std::vector get_res_info(bool calc_unid) -{ - char buf[1000]; - // 3 columns, splits at columns 21, 38 - column_composer cols(3, 21, 38); - - const int rfire = player_res_fire(calc_unid); - const int rcold = player_res_cold(calc_unid); - const int rlife = player_prot_life(calc_unid); - const int rpois = player_res_poison(calc_unid); - const int relec = player_res_electricity(calc_unid); - const int rsust = player_sust_abil(calc_unid); - const int rmuta = wearing_amulet(AMU_RESIST_MUTATION, calc_unid); - const int rslow = wearing_amulet(AMU_RESIST_SLOW, calc_unid); - - snprintf(buf, sizeof buf, - "%sRes.Fire : %s\n" - "%sRes.Cold : %s\n" - "%sLife Prot.: %s\n" - "%sRes.Poison: %s\n" - "%sRes.Elec. : %s\n" - "\n" - "%sSust.Abil.: %s\n" - "%sRes.Mut. : %s\n" - "%sRes.Slow : %s\n", - determine_color_string(rfire), itosym3(rfire), - determine_color_string(rcold), itosym3(rcold), - determine_color_string(rlife), itosym3(rlife), - determine_color_string(rpois), itosym1(rpois), - determine_color_string(relec), itosym1(relec), - determine_color_string(rsust), itosym1(rsust), - determine_color_string(rmuta), itosym1(rmuta), - determine_color_string(rslow), itosym1(rslow)); - cols.add_formatted(0, buf, false); - - int saplevel = 0; - switch (you.species) - { - case SP_GHOUL: - saplevel = 3; - snprintf(buf, sizeof buf, "%sSaprovore : %s", - determine_color_string(3), itosym3(3) ); - break; - - case SP_KOBOLD: - case SP_TROLL: - saplevel = 2; - snprintf(buf, sizeof buf, "%sSaprovore : %s", - determine_color_string(2), itosym3(2) ); - break; - - case SP_HILL_ORC: - case SP_OGRE: - saplevel = 1; - break; - default: - saplevel = 0; - break; - } - const char* pregourmand; - const char* postgourmand; - if ( wearing_amulet(AMU_THE_GOURMAND, calc_unid) ) - { - pregourmand = "Gourmand : "; - postgourmand = itosym1(1); - saplevel = 1; - } - else - { - pregourmand = "Saprovore : "; - postgourmand = itosym3(saplevel); - } - snprintf(buf, sizeof buf, "%s%s%s", - determine_color_string(saplevel), pregourmand, postgourmand); - cols.add_formatted(0, buf, false); - - - const int rinvi = player_see_invis(calc_unid); - const int rward = wearing_amulet(AMU_WARDING, calc_unid) || - (you.religion == GOD_VEHUMET && you.duration[DUR_PRAYER] && - !player_under_penance() && you.piety >= piety_breakpoint(2)); - const int rcons = wearing_amulet(AMU_CONSERVATION, calc_unid); - const int rcorr = wearing_amulet(AMU_RESIST_CORROSION, calc_unid); - const int rclar = wearing_amulet(AMU_CLARITY, calc_unid); - snprintf(buf, sizeof buf, - "%sSee Invis. : %s\n" - "%sWarding : %s\n" - "%sConserve : %s\n" - "%sRes.Corr. : %s\n" - "%sClarity : %s\n" - "\n", - determine_color_string(rinvi), itosym1(rinvi), - determine_color_string(rward), itosym1(rward), - determine_color_string(rcons), itosym1(rcons), - determine_color_string(rcorr), itosym1(rcorr), - determine_color_string(rclar), itosym1(rclar)); - cols.add_formatted(1, buf, false); - - if ( scan_randarts(RAP_PREVENT_TELEPORTATION, calc_unid) ) - snprintf(buf, sizeof buf, "\n%sPrev.Telep.: %s", - determine_color_string(-1), itosym1(1)); - else - { - const int rrtel = player_teleport(calc_unid); - snprintf(buf, sizeof buf, "\n%sRnd.Telep. : %s", - determine_color_string(rrtel), itosym1(rrtel)); - } - cols.add_formatted(1, buf, false); - - const int rctel = player_control_teleport(calc_unid); - const int rlevi = player_is_levitating(); - const int rcfli = wearing_amulet(AMU_CONTROLLED_FLIGHT, calc_unid); - snprintf(buf, sizeof buf, - "%sCtrl.Telep.: %s\n" - "%sLevitation : %s\n" - "%sCtrl.Flight: %s\n", - determine_color_string(rctel), itosym1(rctel), - determine_color_string(rlevi), itosym1(rlevi), - determine_color_string(rcfli), itosym1(rcfli)); - cols.add_formatted(1, buf, false); - - - { - char str_pass[ITEMNAME_SIZE]; - const int e_order[] = - { - EQ_WEAPON, EQ_BODY_ARMOUR, EQ_SHIELD, EQ_HELMET, EQ_CLOAK, - EQ_GLOVES, EQ_BOOTS, EQ_AMULET, EQ_RIGHT_RING, EQ_LEFT_RING - }; - - for(int i = 0; i < NUM_EQUIP; i++) - { - int eqslot = e_order[i]; - const char *slot = equip_slot_to_name( eqslot ); - if (eqslot == EQ_LEFT_RING || eqslot == EQ_RIGHT_RING) - slot = "Ring"; - else if (eqslot == EQ_BOOTS && - (you.species == SP_CENTAUR || you.species == SP_NAGA)) - slot = "Barding"; - - if ( you.equip[ e_order[i] ] != -1) - { - const int inum = you.equip[e_order[i]]; - in_name( inum, DESC_PLAIN, str_pass, true ); - str_pass[38] = 0; // truncate - const char* colname = colour_to_str(you.inv[inum].colour); - snprintf(buf, sizeof buf, "%-7s: <%s>%s", - slot, colname, str_pass, colname); - } - else - { - if (e_order[i] == EQ_WEAPON) - { - if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS) - snprintf(buf, sizeof buf, "%-7s: Blade Hands", slot); - else if (you.skills[SK_UNARMED_COMBAT]) - snprintf(buf, sizeof buf, "%-7s: Unarmed", slot); - else - snprintf(buf, sizeof buf, "%-7s:", slot); - } - else - { - snprintf(buf, sizeof buf, "%-7s:", slot); - } - } - cols.add_formatted(2, buf, false); - } - } - - return cols.formatted_lines(); -} diff --git a/crawl-ref/source/output.h b/crawl-ref/source/output.h index 6d4f13fb13..71a8955d0d 100644 --- a/crawl-ref/source/output.h +++ b/crawl-ref/source/output.h @@ -29,8 +29,5 @@ int equip_name_to_slot(const char *s); const char *equip_slot_to_name(int equip); void print_overview_screen(void); -formatted_string get_full_detail2(bool calc_unid); -std::vector get_stat_info(void); -std::vector get_res_info(bool calc_unid); #endif diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index 500dec8e7b..2ac89e2b44 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -2213,8 +2213,8 @@ void gain_exp( unsigned int exp_gained ) you.exp_available += exp_gained; level_change(); - if (Options.tutorial_left && you.experience_level == 5) - tutorial_finished(); + if (Options.tutorial_left && you.experience_level == 5) + tutorial_finished(); } // end gain_exp() void level_change(void) diff --git a/crawl-ref/source/tutorial.cc b/crawl-ref/source/tutorial.cc index 8e7edddbcf..6cbe6f8a47 100644 --- a/crawl-ref/source/tutorial.cc +++ b/crawl-ref/source/tutorial.cc @@ -1,1300 +1,1266 @@ -/* - * Created for Crawl Reference by JPEG on $Date: 2007-01-11$ - */ - -#include "AppHdr.h" -#include "tutorial.h" -#include - -#include "command.h" -#include "files.h" -#include "itemprop.h" -#include "menu.h" -#include "message.h" -#include "misc.h" -#include "newgame.h" -#include "output.h" -#include "player.h" -#include "religion.h" -#include "spl-util.h" -#include "stuff.h" -#include "view.h" - -//#define TUTORIAL_DEBUG -#define TUTORIAL_VERSION 101 -int INFO2_SIZE = 500; -char info2[500]; - -static std::string tut_debug_list(int event); - -void save_tutorial( FILE* fp ) -{ - writeLong( fp, TUTORIAL_VERSION); - writeShort( fp, Options.tutorial_type); - for ( unsigned i = 0; i < TUT_EVENTS_NUM; ++i ) - writeShort( fp, Options.tutorial_events[i] ); -} - -void load_tutorial( FILE* fp ) -{ - Options.tutorial_left = 0; - - int version = readLong(fp); - Options.tutorial_type = readShort(fp); - if (version != TUTORIAL_VERSION) - return; - for ( long i = 0; i < TUT_EVENTS_NUM; ++i ) - { - Options.tutorial_events[i] = readShort(fp); - Options.tutorial_left += Options.tutorial_events[i]; - } -} - -// override init file definition for some options -void init_tutorial_options() -{ - if (!Options.tutorial_left) { - return; - } - - Options.delay_message_clear = false; - Options.auto_list = true; -} - -// tutorial selection screen and choice -bool pick_tutorial() -{ - char keyn; - bool printed = false; - -tut_query: - if (!printed) - { - clrscr(); - - textcolor( WHITE ); - cprintf("You must be new here indeed!"); - - cprintf(EOL EOL); - textcolor( CYAN ); - cprintf("You can be:"); - cprintf(EOL EOL); - - textcolor( LIGHTGREY ); - - for (int i = 0; i < TUT_TYPES_NUM; i++) - print_tutorial_menu(i); - - textcolor( BROWN ); - cprintf(EOL "SPACE - Back to class selection; Bksp - Back to race selection; X - Quit" - EOL "* - Random tutorial" - EOL); - printed = true; - } - keyn = c_getch(); - - if (keyn == '*') - keyn = 'a' + random2(TUT_TYPES_NUM); - - // choose character for tutorial game - if (keyn >= 'a' && keyn <= 'a' + TUT_TYPES_NUM - 1) - { - Options.tutorial_type = keyn - 'a'; - for (int i = 0; i < TUT_EVENTS_NUM; i++) - Options.tutorial_events[i] = 1; - - Options.tut_explored = 1; - Options.tut_stashes = 1; - Options.tut_travel = 1; - // possibly combine to specialty_counter or something - Options.tut_spell_counter = 0; - Options.tut_throw_counter = 0; - Options.tut_berserk_counter = 0; - Options.tut_melee_counter = 0; - Options.tut_last_healed = 0; - - Options.tutorial_left = TUT_EVENTS_NUM; - you.species = get_tutorial_species(Options.tutorial_type); - you.char_class = get_tutorial_job(Options.tutorial_type); - - Options.random_pick = true; // random choice of starting spellbook - Options.weapon = WPN_HAND_AXE; // easiest choice for dwarves - return true; - } - - if (keyn == CK_BKSP || keyn == ' ') - { - // in this case, undo previous choices -// set_startup_options(); - you.species = 0; you.char_class = JOB_UNKNOWN; - Options.race = 0; Options.cls = 0; - } - - switch (keyn) - { - case CK_BKSP: - choose_race(); - break; - case ' ': - choose_class(); - break; - case 'X': - cprintf(EOL "Goodbye!"); - end(0); - break; - default: - printed = false; - goto tut_query; - } - - return false; -} - -void print_tutorial_menu(unsigned int type) -{ - char letter = 'a' + type; - char desc[100]; - - switch(type) - { -/* - case TUT_MELEE_CHAR: - strcpy(desc, "(Standard melee oriented character)"); - break; -*/ - 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 - snprintf(info, INFO_SIZE, "Error in print_tutorial_menu: type = %d", type); - mpr( info ); - break; - } - - cprintf("%c - %s %s %s" EOL, letter, species_name(get_tutorial_species(type), 1), - get_class_name(get_tutorial_job(type)), desc); -} - -unsigned int get_tutorial_species(unsigned int type) -{ - switch(type) - { -/* - case TUT_MELEE_CHAR: - return SP_MOUNTAIN_DWARF; -*/ - 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; - } -} - -// TO DO: check whether job and species are compatible... -unsigned int get_tutorial_job(unsigned int type) -{ - switch(type) - { -/* - case TUT_MELEE_CHAR: - return JOB_FIGHTER; -*/ - 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; - } -} - -static formatted_string formatted_paragraph(std::string text, unsigned int width) -{ - std::string result; - - size_t start = 0, pos = 0, end = 0; - - for (; end < strlen(text.c_str()); pos = end+1) - { - // get next "word" - end = text.find(' ', pos); - if ( end >= strlen(text.c_str()) ) - pos = end; - - if (end - start >= width || end >= strlen(text.c_str()) ) - { - end = pos; - result += text.substr(start, end-start-1); - result += EOL; - start = pos; - } - } - result += EOL; - - return formatted_string::parse_string(result); -} - -static formatted_string tut_starting_info(unsigned int width) -{ - std::string result; // the entire page - std::string text; // one paragraph - - result += "Welcome to Dungeon Crawl!" EOL EOL; - - text += "Your object is to lead a "; - snprintf(info, INFO_SIZE, "%s %s ", species_name(get_tutorial_species(Options.tutorial_type), 1), - get_class_name(get_tutorial_job(Options.tutorial_type))); - text += info2; - text += "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 deep as possible but beware, as death lurks around " - "every corner here."; - result += formatted_paragraph(text, width); - - result += "For the moment, just remember the following keys and their functions:" EOL; - result += " ? - shows the items and the commands" EOL; - result += " S - saves the game, to be resumed later (but note that death is permanent)" EOL; - result += " x - examine something in your vicinity" EOL EOL; - - text = "This tutorial will help you playing Crawl without reading any documentation. " - "If you feel intrigued, there is more information available in these files " - "(all of which can also be read in-game):"; - result += formatted_paragraph(text,width); - - result += " readme.txt - A very short guide to Crawl." EOL; - result += " manual.txt - This contains all details on races, magic, skills, etc." EOL; - result += " crawl_options.txt - Crawl's interface is highly configurable. This document " EOL; - result += " explains all the options (which themselves are set in " EOL; - result += " the init.txt or .crawlrc files)." EOL; - result += EOL; - result += "Press Space to proceed to the basics (the screen division and movement)." EOL; - result += "Press Esc to fast forward to the game start." EOL EOL; - - - return formatted_string::parse_string(result); -} - -formatted_string tutorial_debug() -{ - std::string result; - bool lbreak = false; - snprintf(info, INFO2_SIZE, "Tutorial Debug Screen"); - - int i = 39 - strlen(info) / 2; - result += std::string(i, ' '); - result += ""; - result += info; - result += "" EOL EOL; - - result += ""; - for (i=0; i < TUT_EVENTS_NUM; i++) - { - snprintf(info, INFO_SIZE, "%d: %d (%s)", - i, Options.tutorial_events[i], tut_debug_list(i).c_str()); - result += info; - - // break text into 2 columns where possible - if (strlen(info) > 39 || lbreak) - { - result += EOL; - lbreak = false; - } - else - { - result += std::string(39 - strlen(info), ' '); - lbreak = true; - } - } - result += "" EOL EOL; - - snprintf(info, INFO_SIZE, "tutorial_left: %d\n", Options.tutorial_left); - result += info; - result += EOL; - - snprintf(info, INFO_SIZE, "You are a %s %s, and the tutorial will reflect that.", - species_name(get_tutorial_species(Options.tutorial_type), 1), - get_class_name(get_tutorial_job(Options.tutorial_type))); - - result += info; - - return formatted_string::parse_string(result); -} - -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: // doesn't work for now - return "seen first random artefact"; - case TUT_SEEN_FOOD: - return "seen first food"; - case TUT_SEEN_CARRION: - return "seen first corpse"; - 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_STAIRS: - return "seen first stairs"; - case TUT_SEEN_TRAPS: - 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_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_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_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_POSTBERSERK: - return "learned about Berserk aftereffects"; - case TUT_RUN_AWAY: - return "were told to run away"; - case TUT_SHIFT_RUN: - return "learned about shift-run"; - case TUT_MAP_VIEW: - return "learned about the level map"; -/* - case TUT_AUTOEXPLORE: - return "learned about autoexplore"; -*/ - case TUT_DONE_EXPLORE: - return "explored a level"; - case TUT_YOU_MUTATED: - return "caught a mutation"; - case TUT_NEW_ABILITY: - return "gained a divine ability"; - case TUT_WIELD_WEAPON: - return "wielded an unsuitable weapon"; - default: - return "faced a bug"; - } - -} - -static formatted_string tutorial_map_intro() -{ - std::string result; - - result = "" - "What you see here is the typical Crawl screen. The upper left map " - "shows your hero as the @ in the center. The parts " - "of the map you remember but cannot currently see, will be greyed " - "out." - "" EOL; - result += "[--more-- Press Escape to skip the basics]"; - - linebreak_string2(result,80); - return formatted_string::parse_string(result); -} - -static formatted_string tutorial_stats_intro() -{ - std::string result; - result += ""; - - result += "To the right, important properties of " EOL; - result += "the character are displayed. The most " EOL; - result += "basic one is your health, measured as " EOL; - snprintf(info, INFO_SIZE, "HP: %d/%d. ", you.hp, you.hp_max); - result += info; - if (Options.tutorial_type==TUT_MAGIC_CHAR) result += " "; - result += "These are your current out " EOL; - result += "of maximum health points. When health " EOL; - result += "drops to zero, you die. " EOL; - snprintf(info, INFO_SIZE, "Magic: %d/%d", you.magic_points, you.max_magic_points); - result += info; - result += " is your energy for casting " EOL; - result += "spells, although more mundane actions " EOL; - result += "often draw from Magic, too. " EOL; - result += "Further down, Strength, Dexterity and " EOL; - result += "Intelligence are shown and provide an " EOL; - result += "all-around account of the character's " EOL; - result += "attributes. " EOL; - - result += ""; - result += " " EOL; - result += "[--more-- Press Escape to skip the basics]" EOL; - result += " " EOL; - result += " " EOL; - - return formatted_string::parse_string(result); -} - -static formatted_string tutorial_message_intro() -{ - std::string result; - - result = "" - "This lower part of the screen is reserved for messages. Everything " - "related to the tutorial is shown in this colour. If you missed " - "something, previous messages can be read again with " - "Ctrl-P." - "" EOL; - result += "[--more-- Press Escape to skip the basics]"; - - linebreak_string2(result,80); - return formatted_string::parse_string(result); -} - -static void tutorial_movement_info() -{ - std::string text; - 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 hjklyubn. A basic " - "command list can be found under ?, and the most important " - "commands will be explained to you as it becomes necessary. "; - print_formatted_paragraph(text, 80, MSGCH_TUTORIAL); -} - -// copied from display_mutations and adapted -void tut_starting_screen() -{ - - int x1, x2, y1, y2; - int MAX_INFO = 4; -#ifdef TUTORIAL_DEBUG - MAX_INFO++; -#endif - char ch; - - for (int i=0; i<=MAX_INFO; i++) - { - x1 = 1; y1 = 1; - x2 = 80; y2 = 25; - - if (i==1 || i==3) - { - y1 = 18; // message window - } - else if (i==2) - { - x2 = 40; // map window - y2 = 18; - } - - if (i==0) - clrscr(); - - gotoxy(x1,y1); - - if (i==0) - tut_starting_info(x2).display(); - else if (i==1) - tutorial_map_intro().display(); - else if (i==2) - tutorial_stats_intro().display(); - else if (i==3) - tutorial_message_intro().display(); - else if (i==4) - tutorial_movement_info(); - else - { -#ifdef TUTORIAL_DEBUG - clrscr(); - gotoxy(x1,y1); - tutorial_debug().display(); -#else - continue; -#endif - } - - ch = c_getch(); - redraw_screen(); - if (ch == ESCAPE) - break; - - } - -} - -void tutorial_death_screen() -{ - Options.tutorial_left = 0; - std::string text; - - mpr( "Condolences! Your character's premature perishing is a sad, but " - "common occurence in Crawl. Rest assured that with diligence and " - "playing experience your characters will last longer.\n", MSGCH_TUTORIAL, 0); - mpr( "Perhaps the following advice can improve your playing style:", MSGCH_TUTORIAL); - more(); - - if (Options.tutorial_type == TUT_MAGIC_CHAR - && Options.tut_spell_counter < Options.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 (Options.tutorial_type == TUT_BERSERK_CHAR - && Options.tut_berserk_counter < 1 ) - { - text = "Don't forget to go berserk when fighting particularly " - "difficult foes. It is risky, but makes you faster " - "and beefier. Also try to pray prior to battles so that "; - text += god_name(you.religion); - text += " will soon provide more abilities."; - } - else if (Options.tutorial_type == TUT_RANGER_CHAR - && 2*Options.tut_throw_counter < Options.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); - // If a character has been unusually busy with projectiles and spells - // give some other hint rather than the first one. - if (Options.tut_throw_counter + Options.tut_spell_counter >= Options.tut_melee_counter - && hint == 0) - hint = random2(5)+1; - - 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. In Crawl, searching and resting are " - "one and the same. To search for one turn, press s, " - "., delete or keypad-5. " - "Pressing 5 or shift-and-keypad-5 will " - "let you rest for a longer time (you will stop resting 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."; - } - } - print_formatted_paragraph(text, 80, MSGCH_TUTORIAL); - more(); - - mpr( "See you next game!", MSGCH_TUTORIAL); - - for ( long i = 0; i < TUT_EVENTS_NUM; ++i ) - Options.tutorial_events[i] = 0; -} - -void tutorial_finished() -{ - std::string text; - - Options.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 help screen (?) " - "will look different from now on. Here's a last playing hint:"; - print_formatted_paragraph(text, 80, MSGCH_TUTORIAL); - more(); - - if (Options.tut_explored) - { - text = "Walking around and exploring levels gets easier by using " - "auto-explore (Ctrl-O). You can even make Crawl " - "automatically pick up interesting items by setting the " - "option explore_greedy=true in the init file."; - } - else if (Options.tut_travel) - { - text = "There is a convenient way for travelling between far away " - "dungeon levels: press Ctrl-G and enter the desired " - "destination. If your travel gets interrupted, issueing " - "Ctrl-G Enter will continue it."; - } - else if (Options.tut_stashes) - { - text = "You can search among all items existing in the dungeon with " - "the Ctrl-F command. For example, " - "Ctrl-F \"knife\" will list all knives. You can then " - "travel to one of the spots. It is even possible to enter words " - "like \"shop\" or \"altar\"."; - } - else - { - int hint = random2(3); - switch (hint) - { - case 0: - text = "The game keeps an automated logbook for your characters. Use " - "?: to read it. You can enter notes manually with " - "the : command. Once your character perishes, two " - "morgue files are left in the /morgue directory. " - "The one ending in .txt contains a copy of your logbook. " - "During play, you can create a dump file with #."; - break; - case 1: - text = "Crawl has a macro function built in: press ~m " - "to define a macro by first specifying a trigger key (say, " - "F1) and a command sequence, for example " - "Za+.. The latter will make the F1 " - "key always zap the spell in slot a at the nearest monster. " - "For more information on macros, type ?~."; - break; - case 2: - text = "The interface can be greatly customised. All options are " - "explained in the file crawl_options.txt which " - "can be found in the /docs directory. The " - "options themselves are set in init.txt or " - ".crawlrc. Crawl will complain when it can't " - "find either file.\n"; - break; - default: - text = "Oops... No hint for now. Better luck next time!"; - } - } - print_formatted_paragraph(text, 80, MSGCH_TUTORIAL); - more(); - - for ( long i = 0; i < TUT_EVENTS_NUM; ++i ) - Options.tutorial_events[i] = 0; -} - -void tutorial_prayer_reminder() -{ - if (Options.tut_just_triggered) - return; - - if (coinflip()) // always would be too annoying - { - std::string text; - text = "Remember to pray before battle, so as to dedicate " - "your kills to "; - text += god_name(you.religion); - text += ". Should the monster leave a corpse, consider " - "Dissecting it as a sacrifice to "; - text += god_name(you.religion); - text += ", as well."; - print_formatted_paragraph(text, 80, MSGCH_TUTORIAL); - } -} - -void tutorial_healing_reminder() -{ - if (you.poisoning) - { - if (Options.tutorial_events[TUT_NEED_POISON_HEALING]) - learned_something_new(TUT_NEED_POISON_HEALING); - } - else - { - if (Options.tutorial_events[TUT_NEED_HEALING]) - learned_something_new(TUT_NEED_HEALING); - else if (you.num_turns - Options.tut_last_healed >= 50 && !you.poisoning) - { - if (Options.tut_just_triggered) - return; - std::string text; - text = "Remember to rest between fights and to enter unexplored " - "terrain with full hitpoints and/or magic. For resting, " - "press 5 or Shift-numpad 5."; - print_formatted_paragraph(text, 80, MSGCH_TUTORIAL); - } - Options.tut_last_healed = you.num_turns; - } -} - -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_STAVES: - case OBJ_MISCELLANY: - learned_something_new(TUT_SEEN_MISC); - break; - default: /* nothing to be done */ - return; - } -} - -void tutorial_first_monster(monsters mon) -{ - if (!Options.tutorial_events[TUT_SEEN_MONSTER]) - return; - - std::string text = "That "; - formatted_string st = formatted_string::parse_string(text); - st.formatted_string::textcolor(channel_to_colour(MSGCH_TUTORIAL)); - st.formatted_string::add_glyph(&mon); - text = " is a monster, usually depicted by a letter. Some typical early monsters "; - st += formatted_string::parse_string(text); - formatted_mpr(st, MSGCH_TUTORIAL); - - text = "look like r, g, b or " - "K. You can gain information about it by pressing " - "x, moving the cursor on the monster and then pressing " - "v. To attack it with your wielded weapon, just move into it."; - print_formatted_paragraph(text, 80, MSGCH_TUTORIAL); - - more(); - - if (Options.tutorial_type == TUT_RANGER_CHAR) - { - text = "However, as a hunter you will want to deal with it using your " - "bow. Do this as follows: wbf+. where wb " - "wields the bow, f fires appropriate ammunition " - "(your arrows) and enters targeting mode. + and " - "- allow you to select the proper monster. Finally, " - "Enter or . fire. If you miss, " - "ff fires at the previous target again."; - print_formatted_paragraph(text, 80, MSGCH_TUTORIAL); - } - else if (Options.tutorial_type == TUT_MAGIC_CHAR) - { - text = "However, as a conjurer you will want to deal with it using magic." - "Do this as follows: Za+. where Za zaps " - "the first spell you know, "; - text += spell_title(get_spell_by_letter('a')); - text += ", and enters targeting mode. + and - " - "allow you to select the proper target. Finally, Enter " - "or . fire. If you miss, Zap will " - "fire at the previous target again."; - print_formatted_paragraph(text, 80, MSGCH_TUTORIAL); - } - - Options.tutorial_events[TUT_SEEN_MONSTER] = 0; - Options.tutorial_left--; - Options.tut_just_triggered = 1; -} - -void tutorial_first_item(item_def item) -{ - if (!Options.tutorial_events[TUT_SEEN_FIRST_OBJECT] || Options.tut_just_triggered) - return; - - std::string text = "That "; - formatted_string st = formatted_string::parse_string(text); - st.formatted_string::textcolor(channel_to_colour(MSGCH_TUTORIAL)); - st.formatted_string::add_glyph(&item); - text = " is an item. If you move there and press g or " - ", you will pick it up."; - st += formatted_string::parse_string(text); - formatted_mpr(st, MSGCH_TUTORIAL); - - text = "Generally, items are shown by non-letter symbols like " - "%%?!\"=()[. Once it is in your inventory, you can drop " - "it again with d. Several types of objects will usually " - "be picked up automatically."; - print_formatted_paragraph(text, 80, MSGCH_TUTORIAL); - - Options.tutorial_events[TUT_SEEN_FIRST_OBJECT] = 0; - Options.tutorial_left--; - Options.tut_just_triggered = 1; -} - -#if 0 -static void tutorial_first_stairs(int x, int y) { - std::string text = "The "; - - formatted_string st = formatted_string::parse_string(text); - st.formatted_string::textcolor(env.show_col[x-you.x_pos + 9][y-you.y_pos + 9]); - text = get_screen_glyph(x,y); - text += " are some downstairs. You can enter the next (deeper) " - "level by following "; - st += formatted_string::parse_string(text); - formatted_mpr(st); - text = "them down (>). To get back to " - "this level again, press << while standing on the " - "upstairs."; - print_formatted_paragraph(text, 80, MSGCH_TUTORIAL); -} -#endif - -void learned_something_new(unsigned int seen_what, int x, int y) -{ - // already learned about that - if (!Options.tutorial_events[seen_what]) - return; - - // don't trigger twice in the same turn - if (Options.tut_just_triggered) - return; - - std::string text; - - switch(seen_what) - { - case TUT_SEEN_POTION: - text = "You have picked up your first potion ('!'). Use " - "q to drink (quaff) it."; - break; - case TUT_SEEN_SCROLL: - text = "You have picked up your first scroll ('?'). Type " - "r to read it."; - break; - case TUT_SEEN_WAND: - text = "You have picked up your first wand ('/'). Type " - "z to zap it."; - break; - case TUT_SEEN_SPBOOK: - text = "You have picked up a spellbook ('+' or " - "':'). You can read it by typing r, " - "memorise spells via M and cast a memorised spell " - "with Z."; - - if (!you.skills[SK_SPELLCASTING]) - { - text += "\nHowever, first you will have to get accustomed to " - "spellcasting by reading lots of scrolls."; - } - break; - case TUT_SEEN_WEAPON: - text = "This is the first weapon ('(') you've picked up. " - "Use w 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 with v."; - if (Options.tutorial_type == TUT_BERSERK_CHAR) - { - text += "\nAs you're already trained in Axes you should stick with " - "these. Checking other axes can be worthwhile."; - } - break; - case TUT_SEEN_MISSILES: - text = "This is the first stack of missiles (')') you've " - "picked up. Darts can be thrown by hand, but other missile types " - "like arrows and needles require a launcher and training in " - "using it to be really effective. v gives more " - "information about both missiles and launcher."; - if (Options.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 (Options.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 ('[') you've " - "picked up. Use W to wear it and T to " - "take it off again. You can view its properties with " - "v."; - - if (you.species == SP_CENTAUR || you.species == SP_MINOTAUR) - { - snprintf( info, INFO_SIZE, "\nNote that as a %s, you will be unable to wear %s.", - species_name(you.species, 1), you.species == SP_CENTAUR ? "boots" : "helmets"); - text += info; - } - break; - case TUT_SEEN_RANDART: - text = "Weapons and armour that have descriptions like this are more " - "likely to be of higher enchantment or have special properties, " - "good or bad."; - break; - case TUT_SEEN_FOOD: - text = "You have picked up some food ('%%'). You can eat it " - "by typing e."; - break; - case TUT_SEEN_CARRION: - text = "You have picked up a corpse ('%%'). When a corpse " - "is lying on the ground, you can Dissect with a " - "sharp implement. Once hungry you can eat the " - "resulting chunks (though they may not be healthy)."; - if (Options.tutorial_type == TUT_BERSERK_CHAR) - { - text += " During prayer you can offer corpses to "; - text += god_name(you.religion); - text += "by dissecting them, as well."; - } - break; - case TUT_SEEN_JEWELLERY: - text = "You have picked up a a piece of jewellery, either a ring " - "('=') or an amulet ('\"'). Type " - "P to put it on and R to remove it. You " - "can view it with v although often magic is " - "necessary to reveal its true nature."; - 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 wielding and " - "Evoking it."; - break; - case TUT_SEEN_STAIRS: - if (you.num_turns < 1) - return; - -// tutorial_first_stairs(x,y); - - - text = "The "; - text += get_screen_glyph(x,y); - text += " are some downstairs. You can enter the next (deeper) " - "level by following them down (>). To get back to " - "this level again, press << while standing on the " - "upstairs."; - - break; - case TUT_SEEN_TRAPS: - text = "Oops... you just entered a trap. An unwary adventurer will " - "occasionally stumble into one of these nasty constructions " - "depicted by ^. They can do physical damage (with " - "darts or needles, for example) or have other, more magical " - "effects, like teleportation."; - break; - case TUT_SEEN_ALTAR: - text = "The "; - text += get_screen_glyph(x,y); - text += " is an altar. You can get information about it by pressing " - "p while standing on the square. Before taking up " - "the responding faith you'll be asked for confirmation."; - break; - case TUT_SEEN_SHOP: - text = "The "; - text += get_screen_glyph(x,y); - text += " is a shop. You can enter it by typing <<."; - break; - case TUT_SEEN_DOOR: - if (you.num_turns < 1) - return; - - text = "The "; - text += get_screen_glyph(x,y); - text += " is a closed door. You can open it by walking into it. " - "Sometimes it is useful to close a door. Do so by pressing " - "c, followed by the direction."; - 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 (Options.tutorial_type == TUT_BERSERK_CHAR) - text += "in melee battle will raise your Axes and Fighting skills."; - else if (Options.tutorial_type == TUT_RANGER_CHAR) - text += "using bow and arrows will raise your Bows and Ranged Combat " - "skills."; - else // if (Options.tutorial_type == TUT_MAGIC_CHAR) - text += "with offensive magic will raise your Conjurations and " - "Spellcasting skills."; - - if (you.religion != GOD_NO_GOD) - { - text += "\nTo dedicate your kills to "; - text += god_name(you.religion); - text += " pray before battle. Not all gods will be " - "pleased by doing this."; - } - 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 (Options.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 Mcb, which can then be " - "zapped with Zb."; - } - break; - case TUT_SKILL_RAISE: - text = "One of your skills just got raised. Type m to take " - "a look at your skills screen."; - break; - case TUT_YOU_ENCHANTED: - text = "Enchantments of all types can befall you temporarily. " - "Abbreviated signalisation appears at the lower end of the stats " - "area. Press @ for more details. A list of all " - "possible enchantments is given in the manual."; - break; - case TUT_YOU_SICK: - learned_something_new(TUT_YOU_ENCHANTED); - text = "Corpses can be spoiled or inedible, resulting in sickness. " - "Also, some monsters' flesh is less palatable than others'. " - "During sickness your hitpoints won't regenerate and sometimes " - "an attribute may decrease. It wears off with time (wait with " - "5) or you could quaff a potion of healing."; - break; - case TUT_YOU_POISON: - learned_something_new(TUT_YOU_ENCHANTED); - text = "Poison will slowly reduce your hp. It wears off with time " - "(wait with 5) or you could quaff a potion of " - "healing."; - 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. Weapons can also be uncursed using enchanting " - "scrolls."; - break; - case TUT_YOU_HUNGRY: - text = "There are two ways to overcome hunger: food you started " - "with or found, and selfmade chunks from corpses. To get the " - "latter, all you need to do is D a corpse with a " - "sharp implement. Your starting weapon will do nicely." - "Try to dine on chunks in order to save permanent food."; - break; - case TUT_YOU_STARVING: - text = "You are now suffering from terrible hunger. You'll need to " - "eat 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."; - break; - case TUT_MULTI_PICKUP: - text = "There's a more comfortable way to pick up several items at the " - "same time. If you press , or g twice " - "you can choose items from a menu. This takes less keystrokes " - "but has no influence on the number of turns needed. Multi-pickup " - "will be interrupted by monsters or other dangerous events."; - 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 drop " - "some of the stuff you don't need or that's too heavy to lug " - "around permanently."; - break; - case TUT_ROTTEN_FOOD: - text = "One or more of the chunks or corpses you carry has started to " - "rot. Few races can digest these safely, so you might just as " - "well drop them now."; - if (you.religion == GOD_TROG || you.religion == GOD_MAKHLEB || - you.religion == GOD_OKAWARU) - { - text += "\nIf it is a rotting corpse you carry now might be a good " - "time to drop and Dissect it during " - "prayer (p) as an offer to "; - text += god_name(you.religion); - text += "."; - } - break; - case TUT_MAKE_CHUNKS: - text = "How lucky! That monster left a corpse which you can now " - "Dissect. One or more chunks will appear, to be " - "eaten later on. Beware that some chunks may be, " - "sometimes or always, hazardous. Only experience can help " - "you here."; - - if (you.duration[DUR_PRAYER] && - (you.religion == GOD_OKAWARU || you.religion == GOD_MAKHLEB || - you.religion == GOD_TROG || you.religion == GOD_ELYVILON)) - { - text += "\nNote that dissection under prayer offers the corpse to "; - text += god_name(you.religion); - text += " - check your god's attitude about this with ^."; - } - break; - case TUT_SHIFT_RUN: - text = "Walking around takes less keystrokes if you press " - "Shift-direction or / direction. " - "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 X to bring up the level map. Typing " - "? shows the list of level map commands. " - "Most importantly, moving the cursor to a spot and pressing " - ". or Enter lets your character move " - "there on its own."; - break; - case TUT_DONE_EXPLORE: - text = "You have explored the dungeon on this level. The downstairs look " - "like '>'. Proceed there and press > to " - "go down."; - - if (Options.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 s, . or 5 to do so."; - } - else - { - text += "Each level of Crawl has three white up and three white 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 s " - "or ., or for 100 turns with 5 or " - "Shift-numpad 5."; - } - 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. Press 5 or " - "shift-numpad-5 to do so."; - break; - case TUT_NEED_POISON_HEALING: - text = "Your poisoning could easily kill you, so now would be a good " - "time to quaff 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!"; - 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."; - 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! "; - if (Options.tutorial_type == TUT_BERSERK_CHAR && !you.berserker) - { - text += "\nAlso, with "; - text += god_name(you.religion); - text += "'s support you can use your Berserk ability (a) " - "to temporarily gain more hitpoints and greater strength. "; - } - 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 A."; - break; - case TUT_NEW_ABILITY: - text = "You just gained a new ability. Press a to take a " - "look at your abilities or to use one of them."; - break; - case TUT_WIELD_WEAPON: - text = "You might want to wield a more suitable implement " - "when attacking monsters."; - if (Options.tutorial_type == TUT_RANGER_CHAR - && you.inv[ you.equip[EQ_WEAPON] ].sub_type == WPN_BOW) - { - text += "You can easily switch between weapons in slots a and " - "b by pressing '."; - } - break; - case TUT_SEEN_MONSTER: - case TUT_SEEN_FIRST_OBJECT: - break; - default: - text += "You've found something new (but I don't know what)!"; - } - if (seen_what != TUT_SEEN_MONSTER && seen_what != TUT_SEEN_FIRST_OBJECT) - print_formatted_paragraph(text, 80, MSGCH_TUTORIAL); - - more(); - - Options.tut_just_triggered = true; - Options.tutorial_events[seen_what] = 0; - Options.tutorial_left--; -} +/* + * Created for Crawl Reference by JPEG on $Date: 2007-01-11$ + */ + +#include "tutorial.h" +#include + +#include "command.h" +#include "files.h" +#include "itemprop.h" +#include "menu.h" +#include "message.h" +#include "misc.h" +#include "newgame.h" +#include "output.h" +#include "player.h" +#include "religion.h" +#include "spl-util.h" +#include "stuff.h" +#include "view.h" + +//#define TUTORIAL_DEBUG +#define TUTORIAL_VERSION 110 + +int COLS = get_number_of_cols(); + +void save_tutorial( FILE* fp ) +{ + writeLong( fp, TUTORIAL_VERSION); + writeShort( fp, Options.tutorial_type); + for ( unsigned i = 0; i < TUT_EVENTS_NUM; ++i ) + writeShort( fp, Options.tutorial_events[i] ); +} + +void load_tutorial( FILE* fp ) +{ + Options.tutorial_left = 0; + + int version = readLong(fp); + Options.tutorial_type = readShort(fp); + if (version != TUTORIAL_VERSION) + return; + for ( long i = 0; i < TUT_EVENTS_NUM; ++i ) + { + Options.tutorial_events[i] = readShort(fp); + Options.tutorial_left += Options.tutorial_events[i]; + } +} + +// override init file definition for some options +void init_tutorial_options() +{ + if (!Options.tutorial_left) + return; + + Options.delay_message_clear = false; + Options.auto_list = true; +} + +// tutorial selection screen and choice +bool pick_tutorial() +{ + char keyn; + bool printed = false; + +tut_query: + if (!printed) + { + clrscr(); + + textcolor( WHITE ); + cprintf("You must be new here indeed!"); + + cprintf(EOL EOL); + textcolor( CYAN ); + cprintf("You can be:"); + cprintf(EOL EOL); + + textcolor( LIGHTGREY ); + + for (int i = 0; i < TUT_TYPES_NUM; i++) + print_tutorial_menu(i); + + textcolor( BROWN ); + cprintf(EOL "SPACE - Back to class selection; Bksp - Back to race selection; X - Quit" + EOL "* - Random tutorial" + EOL); + printed = true; + } + keyn = c_getch(); + + if (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) + { + Options.tutorial_type = keyn - 'a'; + you.species = get_tutorial_species(Options.tutorial_type); + you.char_class = get_tutorial_job(Options.tutorial_type); + + // activate all triggers + for (int i = 0; i < TUT_EVENTS_NUM; i++) + Options.tutorial_events[i] = 1; + Options.tutorial_left = TUT_EVENTS_NUM; + + // store whether explore, stash search or travelling was used + Options.tut_explored = 1; + Options.tut_stashes = 1; + Options.tut_travel = 1; + + // used to compare which fighting means was used most often + Options.tut_spell_counter = 0; + Options.tut_throw_counter = 0; + Options.tut_berserk_counter = 0; + Options.tut_melee_counter = 0; + + // for occasional healing reminders + Options.tut_last_healed = 0; + + Options.random_pick = true; // random choice of starting spellbook + Options.weapon = WPN_HAND_AXE; // easiest choice for fighters + return true; + } + + if (keyn == CK_BKSP || keyn == ' ') + { + // in this case, undo previous choices +// set_startup_options(); + you.species = 0; you.char_class = JOB_UNKNOWN; + Options.race = 0; Options.cls = 0; + } + + switch (keyn) + { + case CK_BKSP: + choose_race(); + break; + case ' ': + choose_class(); + break; + case 'X': + cprintf(EOL "Goodbye!"); + end(0); + break; + default: + printed = false; + goto tut_query; + } + + return false; +} + +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" EOL, letter, species_name(get_tutorial_species(type), 1), + get_class_name(get_tutorial_job(type)), desc); +} + +unsigned int 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; + } +} + +// TO DO: check whether job and species are compatible... +unsigned int 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; + } +} + +// the tutorial welcome screen +static formatted_string tut_starting_info(unsigned int width) +{ + std::string result; // the entire page + std::string text; // one paragraph + + result += "Welcome to Dungeon Crawl!" EOL EOL; + + text += "Your object is to lead a "; + snprintf(info, INFO_SIZE, "%s %s ", species_name(get_tutorial_species(Options.tutorial_type), 1), + get_class_name(get_tutorial_job(Options.tutorial_type))); + text += info; + text += "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 as death lurks around " + "every corner here." EOL EOL; + linebreak_string2(text, width); + result += formatted_string::parse_string(text); + + result += "For the moment, just remember the following two keys and their functions:" EOL; + result += " ? - shows the items and the commands" EOL; + result += " S - saves the game, to be resumed later (but note that death is permanent)" EOL; + result += " x - examine something in your vicinity" EOL EOL; + + text = "This tutorial will help you playing Crawl without reading any documentation. " + "If you feel intrigued, there is more information available in these files " + "(all of which can also be read in-game):" EOL; + linebreak_string2(text, width); + result += formatted_string::parse_string(text); + + result += " readme.txt - A very short guide to Crawl." EOL; + result += " manual.txt - This contains all details on races, magic, skills, etc." EOL; + result += " crawl_options.txt - Crawl's interface is highly configurable. This document " EOL; + result += " explains all the options." EOL; + result += EOL; + result += "Press Space to proceed to the basics (the screen division and movement)." EOL; + result += "Press Esc to fast forward to the game start."; + + + return formatted_string::parse_string(result); +} + +#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_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_STAIRS: + return "seen first stairs"; + case TUT_SEEN_TRAPS: + 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_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_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_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_POSTBERSERK: + return "learned about Berserk aftereffects"; + case TUT_RUN_AWAY: + return "were told to run away"; + case TUT_SHIFT_RUN: + return "learned about shift-run"; + case TUT_MAP_VIEW: + return "learned about the level map"; + case TUT_DONE_EXPLORE: + return "explored a level"; + case TUT_YOU_MUTATED: + return "caught a mutation"; + case TUT_NEW_ABILITY: + return "gained a divine ability"; + case TUT_WIELD_WEAPON: + return "wielded an unsuitable weapon"; + default: + return "faced a bug"; + } +} + +static formatted_string tutorial_debug() +{ + std::string result; + bool lbreak = false; + snprintf(info, INFO_SIZE, "Tutorial Debug Screen"); + + int i = COLS/2-1 - strlen(info) / 2; + result += std::string(i, ' '); + result += ""; + result += info; + result += "" EOL EOL; + + result += ""; + for (i=0; i < TUT_EVENTS_NUM; i++) + { + snprintf(info, INFO_SIZE, "%d: %d (%s)", + i, Options.tutorial_events[i], tut_debug_list(i).c_str()); + result += info; + + // break text into 2 columns where possible + if (strlen(info) >= COLS/2 || lbreak) + { + result += EOL; + lbreak = false; + } + else + { + result += std::string(COLS/2-1 - strlen(info), ' '); + lbreak = true; + } + } + result += "" EOL EOL; + + snprintf(info, INFO_SIZE, "tutorial_left: %d\n", Options.tutorial_left); + result += info; + result += EOL; + + snprintf(info, INFO_SIZE, "You are a %s %s, and the tutorial will reflect that.", + species_name(get_tutorial_species(Options.tutorial_type), 1), + get_class_name(get_tutorial_job(Options.tutorial_type))); + + result += info; + + return formatted_string::parse_string(result); +} +#endif // debug + +static formatted_string tutorial_map_intro() +{ + std::string result; + + result = "" + "What you see here is the typical Crawl screen. The upper left map " + "shows your hero as the @ in the center. The parts " + "of the map you remember but cannot currently see, will be greyed " + "out." + "" EOL; + result += " --more-- Press Escape to skip the basics"; + + linebreak_string2(result,COLS); + return formatted_string::parse_string(result); +} + +static formatted_string tutorial_stats_intro() +{ + std::string result; + result += ""; + + result += "To the right, important properties of " EOL; + result += "the character are displayed. The most " EOL; + result += "basic one is your health, measured as " EOL; + snprintf(info, INFO_SIZE, "HP: %d/%d. ", you.hp, you.hp_max); + result += info; + if (Options.tutorial_type==TUT_MAGIC_CHAR) + result += " "; + result += "These are your current out " EOL; + result += "of maximum health points. When health " EOL; + result += "drops to zero, you die. " EOL; + snprintf(info, INFO_SIZE, "Magic: %d/%d", you.magic_points, you.max_magic_points); + result += info; + result += " is your energy for casting " EOL; + result += "spells, although more mundane actions " EOL; + result += "often draw from Magic, too. " EOL; + result += "Further down, Strength, Dexterity and " EOL; + result += "Intelligence are shown and provide an " EOL; + result += "all-around account of the character's " EOL; + result += "attributes. " EOL; + + result += ""; + result += " " EOL; + result += " --more-- Press Escape to skip the basics" EOL; + result += " " EOL; + result += " " EOL; + + return formatted_string::parse_string(result); +} + +static formatted_string tutorial_message_intro() +{ + std::string result; + + result = "" + "This lower part of the screen is reserved for messages. Everything " + "related to the tutorial is shown in this colour. If you missed " + "something, previous messages can be read again with " + "Ctrl-P." + "" EOL; + result += " --more-- Press Escape to skip the basics"; + + linebreak_string2(result,COLS); + return formatted_string::parse_string(result); +} + +static void tutorial_movement_info() +{ + std::string text; + 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 hjklyubn. A basic " + "command list can be found under ?, and the most important " + "commands will be explained to you as it becomes necessary. "; + print_formatted_paragraph(text, COLS, MSGCH_TUTORIAL); +} + +// copied from display_mutations and adapted +void tut_starting_screen() +{ + + int x1, x2, y1, y2; + int MAX_INFO = 4; +#ifdef TUTORIAL_DEBUG + MAX_INFO++; +#endif + char ch; + + for (int i=0; i<=MAX_INFO; i++) + { + x1 = 1; y1 = 1; + x2 = COLS; y2 = get_number_of_lines(); + + if (i==1 || i==3) + { + y1 = 18; // message window + } + else if (i==2) + { + x2 = 40; // map window + y2 = 18; + } + + if (i==0) + clrscr(); + + gotoxy(x1,y1); + + if (i==0) + tut_starting_info(x2).display(); + else if (i==1) + tutorial_map_intro().display(); + else if (i==2) + tutorial_stats_intro().display(); + else if (i==3) + tutorial_message_intro().display(); + else if (i==4) + tutorial_movement_info(); + else + { +#ifdef TUTORIAL_DEBUG + clrscr(); + gotoxy(x1,y1); + tutorial_debug().display(); +#else + continue; +#endif + } + + ch = c_getch(); + redraw_screen(); + if (ch == ESCAPE) + break; + } +} + +// once a tutorial character dies, offer some playing hints +void tutorial_death_screen() +{ + Options.tutorial_left = 0; + std::string text; + + mpr( "Condolences! Your character's premature perishing is a sad, but " + "common occurence in Crawl. Rest assured that with diligence and " + "playing experience your characters will last longer.\n", MSGCH_TUTORIAL); + mpr( "Perhaps the following advice can improve your playing style:", MSGCH_TUTORIAL); + more(); + + if (Options.tutorial_type == TUT_MAGIC_CHAR + && Options.tut_spell_counter < Options.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 (Options.tutorial_type == TUT_BERSERK_CHAR + && Options.tut_berserk_counter < 1 ) + { + text = "Don't forget to go berserk when fighting particularly " + "difficult foes. It is risky, but makes you faster " + "and beefier. Also try to pray prior to battles so that "; + text += god_name(you.religion); + text += " will soon feel like providing more abilities."; + } + else if (Options.tutorial_type == TUT_RANGER_CHAR + && 2*Options.tut_throw_counter < Options.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); + // If a character has been unusually busy with projectiles and spells + // give some other hint rather than the first one. + if (Options.tut_throw_counter + Options.tut_spell_counter >= Options.tut_melee_counter + && hint == 0) + hint = random2(5)+1; + + 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. In Crawl, searching and resting are " + "one and the same. To search for one turn, press s, " + "., delete or keypad-5. " + "Pressing 5 or shift-and-keypad-5 will " + "let you rest for a longer time (you will stop resting 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."; + } + } + print_formatted_paragraph(text, COLS, MSGCH_TUTORIAL); + more(); + + mpr( "See you next game!", MSGCH_TUTORIAL); + + for ( long i = 0; i < TUT_EVENTS_NUM; ++i ) + Options.tutorial_events[i] = 0; +} + +// if a character survives until Xp 5, 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; + + Options.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 help screen (?) " + "will look different from now on. Here's a last playing hint:"; + print_formatted_paragraph(text, COLS, MSGCH_TUTORIAL); + more(); + + if (Options.tut_explored) + { + text = "Walking around and exploring levels gets easier by using " + "auto-explore (Ctrl-O). You can even make Crawl " + "automatically pick up interesting items by setting the " + "option explore_greedy=true in the init file."; + } + else if (Options.tut_travel) + { + text = "There is a convenient way for travelling between far away " + "dungeon levels: press Ctrl-G and enter the desired " + "destination. If your travel gets interrupted, issueing " + "Ctrl-G Enter will continue it."; + } + else if (Options.tut_stashes) + { + text = "You can search among all items existing in the dungeon with " + "the Ctrl-F command. For example, " + "Ctrl-F \"knife\" will list all knives. You can then " + "travel to one of the spots. It is even possible to enter words " + "like \"shop\" or \"altar\"."; + } + else + { + int hint = random2(3); + switch (hint) + { + case 0: + text = "The game keeps an automated logbook for your characters. Use " + "?: to read it. You can enter notes manually with " + "the : command. Once your character perishes, two " + "morgue files are left in the /morgue directory. " + "The one ending in .txt contains a copy of your logbook. " + "During play, you can create a dump file with #."; + break; + case 1: + text = "Crawl has a macro function built in: press ~m " + "to define a macro by first specifying a trigger key (say, " + "F1) and a command sequence, for example " + "Za+.. The latter will make the F1 " + "key always zap the spell in slot a at the nearest monster. " + "For more information on macros, type ?~."; + break; + case 2: + text = "The interface can be greatly customised. All options are " + "explained in the file crawl_options.txt which " + "can be found in the /docs directory. The " + "options themselves are set in init.txt or " + ".crawlrc. Crawl will complain when it can't " + "find either file.\n"; + break; + default: + text = "Oops... No hint for now. Better luck next time!"; + } + } + print_formatted_paragraph(text, COLS, MSGCH_TUTORIAL); + more(); + + for ( long i = 0; i < TUT_EVENTS_NUM; ++i ) + Options.tutorial_events[i] = 0; +} + +// occasionally remind religious characters of praying +void tutorial_prayer_reminder() +{ + if (Options.tut_just_triggered) + return; + + if (coinflip()) // always would be too annoying + { + std::string text; + text = "Remember to pray before battle, so as to dedicate " + "your kills to "; + text += god_name(you.religion); + text += ". Should the monster leave a corpse, consider " + "Dissecting it as a sacrifice to "; + text += god_name(you.religion); + text += ", as well."; + print_formatted_paragraph(text, COLS, MSGCH_TUTORIAL); + } +} + +// occasionally remind injured characters of resting +void tutorial_healing_reminder() +{ + if (you.poisoning && 2*you.hp < you.hp_max) + { + if (Options.tutorial_events[TUT_NEED_POISON_HEALING]) + learned_something_new(TUT_NEED_POISON_HEALING); + } + else + { + if (Options.tutorial_events[TUT_NEED_HEALING]) + learned_something_new(TUT_NEED_HEALING); + else if (you.num_turns - Options.tut_last_healed >= 50 && !you.poisoning) + { + if (Options.tut_just_triggered) + return; + + std::string text; + text = "Remember to rest between fights and to enter unexplored " + "terrain with full hitpoints and/or magic. For resting, " + "press 5 or Shift-numpad 5."; + print_formatted_paragraph(text, COLS, MSGCH_TUTORIAL); + } + Options.tut_last_healed = you.num_turns; + } +} + +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; + default: /* nothing to be done */ + return; + } +} + +void tutorial_first_monster(monsters mon) +{ + if (!Options.tutorial_events[TUT_SEEN_MONSTER]) + return; + + std::string text = "That "; + formatted_string st = formatted_string::parse_string(text); + st.formatted_string::textcolor(channel_to_colour(MSGCH_TUTORIAL)); + st.formatted_string::add_glyph(&mon); + text = " is a monster, usually depicted by a letter. Some typical early monsters "; + st += formatted_string::parse_string(text); + formatted_mpr(st, MSGCH_TUTORIAL); + + text = "look like r, g, b or " + "K. You can gain information about it by pressing " + "x, moving the cursor on the monster and then pressing " + "v. To attack it with your wielded weapon, just move into it."; + print_formatted_paragraph(text, COLS, MSGCH_TUTORIAL); + + more(); + + if (Options.tutorial_type == TUT_RANGER_CHAR) + { + text = "However, as a hunter you will want to deal with it using your " + "bow. Do this as follows: wbf+. where wb " + "wields the bow, f fires appropriate ammunition " + "(your arrows) and enters targeting mode. + and " + "- allow you to select the proper monster. Finally, " + "Enter or . fire. If you miss, " + "ff fires at the previous target again."; + print_formatted_paragraph(text, COLS, MSGCH_TUTORIAL); + } + else if (Options.tutorial_type == TUT_MAGIC_CHAR) + { + text = "However, as a conjurer you will want to deal with it using magic." + "Do this as follows: Za+. where Za zaps " + "the first spell you know, "; + text += spell_title(get_spell_by_letter('a')); + text += ", and enters targeting mode. + and - " + "allow you to select the proper target. Finally, Enter " + "or . fire. If you miss, Zap will " + "fire at the previous target again."; + print_formatted_paragraph(text, COLS, MSGCH_TUTORIAL); + } + + Options.tutorial_events[TUT_SEEN_MONSTER] = 0; + Options.tutorial_left--; + Options.tut_just_triggered = 1; +} + +void tutorial_first_item(item_def item) +{ + if (!Options.tutorial_events[TUT_SEEN_FIRST_OBJECT] || Options.tut_just_triggered) + return; + + std::string text = "That "; + formatted_string st = formatted_string::parse_string(text); + st.formatted_string::textcolor(channel_to_colour(MSGCH_TUTORIAL)); + st.formatted_string::add_glyph(&item); + text = " is an item. If you move there and press g or " + ", you will pick it up."; + st += formatted_string::parse_string(text); + formatted_mpr(st, MSGCH_TUTORIAL); + + text = "Generally, items are shown by non-letter symbols like " + "%%?!\"=()[. Once it is in your inventory, you can drop " + "it again with d. Several types of objects will usually " + "be picked up automatically."; + print_formatted_paragraph(text, COLS, MSGCH_TUTORIAL); + + Options.tutorial_events[TUT_SEEN_FIRST_OBJECT] = 0; + Options.tutorial_left--; + Options.tut_just_triggered = 1; +} + +// Here most of the tutorial messages for various triggers are handled. +void learned_something_new(unsigned int seen_what, int x, int y) +{ + // already learned about that + if (!Options.tutorial_events[seen_what]) + return; + + // don't trigger twice in the same turn + if (Options.tut_just_triggered) + return; + + std::string text; + unsigned short ch, colour; + + switch(seen_what) + { + case TUT_SEEN_POTION: + text = "You have picked up your first potion ('!'). Use " + "q to drink (quaff) it."; + break; + case TUT_SEEN_SCROLL: + text = "You have picked up your first scroll ('?'). Type " + "r to read it."; + break; + case TUT_SEEN_WAND: + text = "You have picked up your first wand ('/'). Type " + "z to zap it."; + break; + case TUT_SEEN_SPBOOK: + get_item_symbol(DNGN_ITEM_BOOK, &ch, &colour); + snprintf(info, INFO_SIZE, "%c", ch); + text = "You have picked up a spellbook ('"; + text += info; + text += "'). You can read it by typing r, " + "memorise spells via M and cast a memorised spell " + "with Z."; + + if (!you.skills[SK_SPELLCASTING]) + { + text += "\nHowever, first you will have to get accustomed to " + "spellcasting by reading lots of scrolls."; + } + break; + case TUT_SEEN_WEAPON: + text = "This is the first weapon ('(') you've picked up. " + "Use w 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 with v."; + if (Options.tutorial_type == TUT_BERSERK_CHAR) + { + text += "\nAs you're already trained in Axes you should stick with " + "these. Checking other axes can be worthwhile."; + } + break; + case TUT_SEEN_MISSILES: + text = "This is the first stack of missiles (')') you've " + "picked up. Darts can be thrown by hand, but other missile types " + "like arrows and needles require a launcher and training in " + "using it to be really effective. v gives more " + "information about both missiles and launcher."; + if (Options.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 (Options.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 ('[') you've " + "picked up. Use W to wear it and T to " + "take it off again. You can view its properties with " + "v."; + + if (you.species == SP_CENTAUR || you.species == SP_MINOTAUR) + { + snprintf( info, INFO_SIZE, "\nNote that as a %s, you will be unable to wear %s.", + species_name(you.species, 1), you.species == SP_CENTAUR ? "boots" : "helmets"); + text += info; + } + 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 ('%%'). You can eat it " + "by typing e."; + break; + case TUT_SEEN_CARRION: + text = "You have picked up a corpse ('%%'). When a corpse " + "is lying on the ground, you can Dissect with a " + "sharp implement. Once hungry you can eat the " + "resulting chunks (though they may not be healthy)."; + if (Options.tutorial_type == TUT_BERSERK_CHAR) + { + text += " During prayer you can offer corpses to "; + text += god_name(you.religion); + text += " by dissecting them, as well."; + } + break; + case TUT_SEEN_JEWELLERY: + text = "You have picked up a a piece of jewellery, either a ring " + "('=') or an amulet ('\"'). Type " + "P to put it on and R to remove it. You " + "can view it with v although often magic is " + "necessary to reveal its true nature."; + 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 wielding and " + "Evoking it."; + break; + case TUT_SEEN_STAFF: + get_item_symbol(DNGN_ITEM_STAVE, &ch, &colour); + snprintf(info, INFO_SIZE, "%c", ch); + text = "You have picked up a magic staff or a rod, both of which are " + "represented by '"; + text += info; + text += "'. Both must be 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 " + "Evoking it. For the latter the power depends on " + "your Evocations skill."; + break; + case TUT_SEEN_STAIRS: + if (you.num_turns < 1) + return; + + text = "The "; + text += get_screen_glyph(x,y); + text += " are some downstairs. You can enter the next (deeper) " + "level by following them down (>). To get back to " + "this level again, press << while standing on the " + "upstairs."; + + break; + case TUT_SEEN_TRAPS: + text = "Oops... you just entered a trap. An unwary adventurer will " + "occasionally stumble into one of these nasty constructions " + "depicted by ^. They can do physical damage (with " + "darts or needles, for example) or have other, more magical " + "effects, like teleportation."; + break; + case TUT_SEEN_ALTAR: + text = "The "; + text += get_screen_glyph(x,y); + text += " is an altar. You can get information about it by pressing " + "p while standing on the square. Before taking up " + "the responding faith you'll be asked for confirmation."; + break; + case TUT_SEEN_SHOP: + text = "The "; + text += get_screen_glyph(x,y); + text += " is a shop. You can enter it by typing <<."; + break; + case TUT_SEEN_DOOR: + if (you.num_turns < 1) + return; + + text = "The "; + text += get_screen_glyph(x,y); + text += " is a closed door. You can open it by walking into it. " + "Sometimes it is useful to close a door. Do so by pressing " + "c, followed by the direction."; + 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 (Options.tutorial_type == TUT_BERSERK_CHAR) + text += "in melee battle will raise your Axes and Fighting skills."; + else if (Options.tutorial_type == TUT_RANGER_CHAR) + text += "using bow and arrows will raise your Bows and Ranged Combat " + "skills."; + else // if (Options.tutorial_type == TUT_MAGIC_CHAR) + text += "with offensive magic will raise your Conjurations and " + "Spellcasting skills."; + + if (you.religion != GOD_NO_GOD) + { + text += "\nTo dedicate your kills to "; + text += god_name(you.religion); + text += " pray before battle. Not all gods will be " + "pleased by doing this."; + } + 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 (Options.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 Mcb, which can then be " + "zapped with Zb."; + } + break; + case TUT_SKILL_RAISE: + text = "One of your skills just got raised. Type m to take " + "a look at your skills screen."; + break; + case TUT_YOU_ENCHANTED: + text = "Enchantments of all types can befall you temporarily. " + "Abbreviated signalisation appears at the lower end of the stats " + "area. Press @ for more details. A list of all " + "possible enchantments is given in the manual."; + break; + case TUT_YOU_SICK: + learned_something_new(TUT_YOU_ENCHANTED); + text = "Corpses can be spoiled or inedible, resulting in sickness. " + "Also, some monsters' flesh is less palatable than others'. " + "During sickness your hitpoints won't regenerate and sometimes " + "an attribute may decrease. It wears off with time (wait with " + "5) or you could quaff a potion of healing."; + break; + case TUT_YOU_POISON: + learned_something_new(TUT_YOU_ENCHANTED); + text = "Poison will slowly reduce your hp. It wears off with time " + "(wait with 5) or you could quaff a potion of " + "healing."; + 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. Weapons can also be uncursed using enchanting " + "scrolls."; + break; + case TUT_YOU_HUNGRY: + text = "There are two ways to overcome hunger: food you started " + "with or found, and selfmade chunks from corpses. To get the " + "latter, all you need to do is D a corpse with a " + "sharp implement. Your starting weapon will do nicely." + "Try to dine on chunks in order to save permanent food."; + break; + case TUT_YOU_STARVING: + text = "You are now suffering from terrible hunger. You'll need to " + "eat 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."; + break; + case TUT_MULTI_PICKUP: + text = "There's a more comfortable way to pick up several items at the " + "same time. If you press , or g twice " + "you can choose items from a menu. This takes less keystrokes " + "but has no influence on the number of turns needed. Multi-pickup " + "will be interrupted by monsters or other dangerous events."; + 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 drop " + "some of the stuff you don't need or that's too heavy to lug " + "around permanently."; + break; + case TUT_ROTTEN_FOOD: + text = "One or more of the chunks or corpses you carry has started to " + "rot. Few races can digest these safely, so you might just as " + "well drop them now."; + if (you.religion == GOD_TROG || you.religion == GOD_MAKHLEB || + you.religion == GOD_OKAWARU) + { + text += "\nIf it is a rotting corpse you carry now might be a good " + "time to drop and Dissect it during " + "prayer (p) as an offer to "; + text += god_name(you.religion); + text += "."; + } + break; + case TUT_MAKE_CHUNKS: + text = "How lucky! That monster left a corpse which you can now " + "Dissect. One or more chunks will appear that you can " + "then eat. Beware that some chunks may be, " + "sometimes or always, hazardous. Only experience can help " + "you here."; + + if (you.duration[DUR_PRAYER] && + (you.religion == GOD_OKAWARU || you.religion == GOD_MAKHLEB || + you.religion == GOD_TROG || you.religion == GOD_ELYVILON)) + { + text += "\nNote that dissection under prayer offers the corpse to "; + text += god_name(you.religion); + text += " - check your god's attitude about this with ^."; + } + break; + case TUT_SHIFT_RUN: + text = "Walking around takes less keystrokes if you press " + "Shift-direction or / direction. " + "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 X to bring up the level map. Typing " + "? shows the list of level map commands. " + "Most importantly, moving the cursor to a spot and pressing " + ". or Enter lets your character move " + "there on its own."; + break; + case TUT_DONE_EXPLORE: + text = "You have explored the dungeon on this level. The downstairs look " + "like '>'. Proceed there and press > to " + "go down. "; + + if (Options.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 s, . or 5 to do so."; + } + else + { + text += "Each level of Crawl has three white up and three white 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 s " + "or ., or for 100 turns with 5 or " + "Shift-numpad 5."; + } + 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. Press 5 or " + "shift-numpad-5 to do so."; + break; + case TUT_NEED_POISON_HEALING: + text = "Your poisoning could easily kill you, so now would be a good " + "time to quaff 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!"; + 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."; + 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! "; + if (Options.tutorial_type == TUT_BERSERK_CHAR && !you.berserker) + { + text += "\nAlso, with "; + text += god_name(you.religion); + text += "'s support you can use your Berserk ability (a) " + "to temporarily gain more hitpoints and greater strength. "; + } + 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 A."; + break; + case TUT_NEW_ABILITY: + text = "You just gained a new ability. Press a to take a " + "look at your abilities or to use one of them."; + break; + case TUT_WIELD_WEAPON: + text = "You might want to wield a more suitable implement " + "when attacking monsters."; + if (Options.tutorial_type == TUT_RANGER_CHAR + && you.inv[ you.equip[EQ_WEAPON] ].sub_type == WPN_BOW) + { + text += "You can easily switch between weapons in slots a and " + "b by pressing '."; + } + break; + case TUT_SEEN_MONSTER: + case TUT_SEEN_FIRST_OBJECT: + break; + default: + text += "You've found something new (but I don't know what)!"; + } + if (seen_what != TUT_SEEN_MONSTER && seen_what != TUT_SEEN_FIRST_OBJECT) + print_formatted_paragraph(text, COLS, MSGCH_TUTORIAL); + +// more(); + + Options.tut_just_triggered = true; + Options.tutorial_events[seen_what] = 0; + Options.tutorial_left--; +} diff --git a/crawl-ref/source/tutorial.h b/crawl-ref/source/tutorial.h index 86a1cedae3..a204c829ed 100644 --- a/crawl-ref/source/tutorial.h +++ b/crawl-ref/source/tutorial.h @@ -20,81 +20,84 @@ void save_tutorial( FILE* fp ); void load_tutorial( FILE* fp ); void init_tutorial_options(void); + bool pick_tutorial(void); void print_tutorial_menu(unsigned int type); unsigned int get_tutorial_species(unsigned int type); unsigned int get_tutorial_job(unsigned int type); -//formatted_string tut_starting_info(unsigned int width); + formatted_string tut_starting_info2(); void tut_starting_screen(); void tutorial_death_screen(void); void tutorial_finished(void); + void tutorial_prayer_reminder(void); void tutorial_healing_reminder(void); + void taken_new_item(unsigned char item_type); void tutorial_first_monster(const monsters mon); void tutorial_first_item(const item_def item); void learned_something_new(unsigned int seen_what, int x=0, int y=0); -//void learned_something_new(unsigned int seen_what/*, formatted_string st = formatted_string::parse_string("") */); -//void tutorial_output_commands(const std::string &str, unsigned int colour); +// any change in this list warrants an increase in the +// version number in tutorial.cc enum tutorial_event { TUT_SEEN_FIRST_OBJECT, // 0 - /* seen certain items */ + // seen certain items TUT_SEEN_POTION, TUT_SEEN_SCROLL, TUT_SEEN_WAND, TUT_SEEN_SPBOOK, TUT_SEEN_JEWELLERY, // 5 TUT_SEEN_MISC, + TUT_SEEN_STAFF, TUT_SEEN_WEAPON, TUT_SEEN_MISSILES, - TUT_SEEN_ARMOUR, - TUT_SEEN_RANDART, // 10 + TUT_SEEN_ARMOUR, // 10 + TUT_SEEN_RANDART, TUT_SEEN_FOOD, TUT_SEEN_CARRION, + // encountered dungeon features TUT_SEEN_STAIRS, - TUT_SEEN_TRAPS, - TUT_SEEN_ALTAR, // 15 - TUT_SEEN_SHOP, // not tested so far + TUT_SEEN_TRAPS, // 15 + TUT_SEEN_ALTAR, + TUT_SEEN_SHOP, TUT_SEEN_DOOR, - /* other 'first events */ + // other 'first events' TUT_SEEN_MONSTER, - TUT_KILLED_MONSTER, - TUT_NEW_LEVEL, // 20 + TUT_KILLED_MONSTER, // 20 + TUT_NEW_LEVEL, TUT_SKILL_RAISE, TUT_YOU_ENCHANTED, TUT_YOU_SICK, - TUT_YOU_POISON, - TUT_YOU_CURSED, // 25 + TUT_YOU_POISON, // 25 + TUT_YOU_CURSED, TUT_YOU_HUNGRY, TUT_YOU_STARVING, TUT_MULTI_PICKUP, - TUT_HEAVY_LOAD, - TUT_ROTTEN_FOOD, // 30 + TUT_HEAVY_LOAD, // 30 + TUT_ROTTEN_FOOD, TUT_NEED_HEALING, TUT_NEED_POISON_HEALING, TUT_RUN_AWAY, - TUT_MAKE_CHUNKS, - TUT_POSTBERSERK, // 35 + TUT_MAKE_CHUNKS, // 35 + TUT_POSTBERSERK, TUT_SHIFT_RUN, TUT_MAP_VIEW, -// TUT_AUTOEXPLORE, TUT_DONE_EXPLORE, - TUT_YOU_MUTATED, // 39 - TUT_NEW_ABILITY, + TUT_YOU_MUTATED, // 40 + TUT_NEW_ABILITY, TUT_WIELD_WEAPON, - TUT_EVENTS_NUM // 42 + TUT_EVENTS_NUM // 43 }; // for numbers higher than 45 change size of tutorial_events in externs.h enum tutorial_types { -// TUT_MELEE_CHAR, // 0 TUT_BERSERK_CHAR, TUT_MAGIC_CHAR, TUT_RANGER_CHAR, - TUT_TYPES_NUM // 4 + TUT_TYPES_NUM // 3 }; #endif diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc index 03b1669855..e06ced44d1 100644 --- a/crawl-ref/source/view.cc +++ b/crawl-ref/source/view.cc @@ -324,6 +324,21 @@ static void get_symbol( int x, int y, *colour = fix_colour(*colour); } +void get_item_symbol(unsigned int object, unsigned short *ch, + unsigned short *colour) +{ + if (object < NUM_FEATURES) + { + *ch = Feature[object].symbol; + + // Don't clobber with BLACK, because the colour should be already set. + if (Feature[object].colour != BLACK) + *colour = Feature[object].colour; + } + *colour = fix_colour(*colour); + +} + unsigned char get_sightmap_char( int feature ) { if (feature < NUM_FEATURES) diff --git a/crawl-ref/source/view.h b/crawl-ref/source/view.h index 276c6d1f39..52bc2dd1f1 100644 --- a/crawl-ref/source/view.h +++ b/crawl-ref/source/view.h @@ -117,6 +117,8 @@ void get_item_glyph(const item_def *item, unsigned short *glych, void get_mons_glyph(const monsters *mons, unsigned short *glych, unsigned short *glycol); int get_screen_glyph( int x, int y ); +void get_item_symbol(unsigned int object, unsigned short *ch, + unsigned short *colour); void set_envmap_char( int x, int y, unsigned char chr ); unsigned get_envmap_char(int x, int y); -- cgit v1.2.3-54-g00ecf