diff options
-rw-r--r-- | crawl-ref/source/delay.cc | 5 | ||||
-rw-r--r-- | crawl-ref/source/describe.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/directn.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/enum.h | 38 | ||||
-rw-r--r-- | crawl-ref/source/externs.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/items.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/misc.cc | 5 | ||||
-rw-r--r-- | crawl-ref/source/mon-util.cc | 3 | ||||
-rw-r--r-- | crawl-ref/source/monstuff.cc | 1 | ||||
-rw-r--r-- | crawl-ref/source/player.cc | 10 | ||||
-rw-r--r-- | crawl-ref/source/religion.cc | 15 | ||||
-rw-r--r-- | crawl-ref/source/spl-cast.cc | 5 | ||||
-rw-r--r-- | crawl-ref/source/state.cc | 5 | ||||
-rw-r--r-- | crawl-ref/source/tutorial.cc | 650 | ||||
-rw-r--r-- | crawl-ref/source/tutorial.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/view.cc | 12 |
16 files changed, 709 insertions, 50 deletions
diff --git a/crawl-ref/source/delay.cc b/crawl-ref/source/delay.cc index 458a2bb6a5..eee93914e1 100644 --- a/crawl-ref/source/delay.cc +++ b/crawl-ref/source/delay.cc @@ -1608,12 +1608,7 @@ inline static void monster_warning(activity_interrupt_type ai, } if (Options.tutorial_left) - { - // enforce that this message comes first tutorial_first_monster(*mon); - if (get_mons_colour(mon) != mon->colour) - learned_something_new(TUT_MONSTER_BRAND); - } } } diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc index 31a44be7ea..b73cad38cc 100644 --- a/crawl-ref/source/describe.cc +++ b/crawl-ref/source/describe.cc @@ -1790,7 +1790,7 @@ void describe_feature_wide(int x, int y) #endif if (Options.tutorial_left) - tutorial_describe_feature(grd[x][y]); + tutorial_describe_pos(x, y); if ( getch() == 0 ) getch(); diff --git a/crawl-ref/source/directn.cc b/crawl-ref/source/directn.cc index e8b7ef1f3a..5b85906d81 100644 --- a/crawl-ref/source/directn.cc +++ b/crawl-ref/source/directn.cc @@ -2382,7 +2382,7 @@ static void describe_cell(int mx, int my) marker.c_str(), traveldest.c_str()); #else - if (Options.tutorial_left && tutorial_feat_interesting(grd[mx][my])) + if (Options.tutorial_left && tutorial_pos_interesting(mx, my)) { feature_desc += " (Press <w>v<lightgray> for more information.)"; diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index 3ded9f9b4a..52760c212f 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -2675,48 +2675,60 @@ enum tutorial_event_type TUT_SEEN_RANDART, TUT_SEEN_FOOD, TUT_SEEN_CARRION, + TUT_SEEN_GOLD, // encountered dungeon features - TUT_SEEN_STAIRS, // 14 + TUT_SEEN_STAIRS, // 15 TUT_SEEN_ESCAPE_HATCH, + TUT_SEEN_BRANCH, TUT_SEEN_TRAP, TUT_SEEN_ALTAR, - TUT_SEEN_SHOP, + TUT_SEEN_SHOP, // 20 TUT_SEEN_DOOR, + TUT_SEEN_SECRET_DOOR, // other 'first events' - TUT_SEEN_MONSTER, // 20 + TUT_SEEN_MONSTER, TUT_MONSTER_BRAND, + TUT_MONSTER_FRIENDLY, // 25 TUT_KILLED_MONSTER, TUT_NEW_LEVEL, TUT_SKILL_RAISE, - TUT_MAKE_CHUNKS, // 25 - TUT_OFFER_CORPSE, + TUT_MAKE_CHUNKS, + TUT_OFFER_CORPSE, // 30 TUT_NEW_ABILITY, TUT_FLEEING_MONSTER, TUT_ROTTEN_FOOD, + TUT_CONVERT, + TUT_EXCOMMUNICATE, // 35 + TUT_SPELL_MISCAST, + TUT_SPELL_HUNGER, + TUT_GLOWING, // status changes - TUT_YOU_ENCHANTED, // 30 - TUT_YOU_SICK, + TUT_YOU_ENCHANTED, + TUT_YOU_SICK, // 40 TUT_YOU_POISON, + TUT_YOU_ROTTING, TUT_YOU_CURSED, TUT_YOU_HUNGRY, - TUT_YOU_STARVING, // 35 + TUT_YOU_STARVING, // 45 TUT_YOU_MUTATED, TUT_POSTBERSERK, // warning - TUT_RUN_AWAY, // 38 + TUT_RUN_AWAY, TUT_RETREAT_CASTER, - TUT_WIELD_WEAPON, // 40 + TUT_WIELD_WEAPON, // 50 TUT_NEED_HEALING, TUT_NEED_POISON_HEALING, TUT_INVISIBLE_DANGER, TUT_NEED_HEALING_INVIS, + TUT_ABYSS, // 55 // interface - TUT_MULTI_PICKUP, // 45 + TUT_MULTI_PICKUP, TUT_HEAVY_LOAD, TUT_SHIFT_RUN, TUT_MAP_VIEW, - TUT_DONE_EXPLORE, - TUT_EVENTS_NUM // 50 + TUT_DONE_EXPLORE, // 60 + TUT_STAIR_BRAND, + TUT_EVENTS_NUM // 62 }; // for numbers higher than 50 change size of tutorial_events in externs.h enum tutorial_types diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index eb1062167b..042b54af42 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -1871,7 +1871,7 @@ public: /////////////////////////////////////////////////////////////////////// // tutorial - FixedVector<bool, 50> tutorial_events; + FixedVector<bool, 70> tutorial_events; bool tut_explored; bool tut_stashes; bool tut_travel; diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc index ab419caef4..70fa4e2fe1 100644 --- a/crawl-ref/source/items.cc +++ b/crawl-ref/source/items.cc @@ -1486,6 +1486,8 @@ int move_item_to_player( int obj, int quant_got, bool quiet ) you.gold, (you.gold > 1) ? "s" : ""); } + learned_something_new(TUT_SEEN_GOLD); + you.turn_is_over = true; return (retval); } diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc index 3cec070520..357b97a80c 100644 --- a/crawl-ref/source/misc.cc +++ b/crawl-ref/source/misc.cc @@ -1181,8 +1181,8 @@ void search_around( bool only_adjacent ) if (grd[srx][sry] == DNGN_SECRET_DOOR && random2(17) <= effective) { - reveal_secret_door(srx, sry); mpr("You found a secret door!"); + reveal_secret_door(srx, sry); exercise(SK_TRAPS_DOORS, (coinflip() ? 2 : 1)); } else if (grd[srx][sry] == DNGN_UNDISCOVERED_TRAP @@ -2135,6 +2135,7 @@ void down_stairs( int old_level, dungeon_feature_type force_stair, if (!force_stair) mpr("You enter the Abyss!"); mpr("To return, you must find a gate leading back."); + learned_something_new(TUT_ABYSS); break; case LEVEL_PANDEMONIUM: @@ -2888,6 +2889,8 @@ void reveal_secret_door(int x, int y) dungeon_feature_type door = grid_secret_door_appearance(x, y); grd[x][y] = grid_is_opaque(door) ? DNGN_CLOSED_DOOR : DNGN_OPEN_DOOR; + viewwindow(true, false); + learned_something_new(TUT_SEEN_SECRET_DOOR, x, y); } // A feeble attempt at Nethack-like completeness for cute messages. diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc index 7a4c952de4..e6b728de53 100644 --- a/crawl-ref/source/mon-util.cc +++ b/crawl-ref/source/mon-util.cc @@ -58,6 +58,7 @@ #include "terrain.h" #include "tiles.h" #include "traps.h" +#include "tutorial.h" #include "view.h" //jmf: moved from inside function @@ -4747,6 +4748,8 @@ void monsters::add_enchantment_effect(const mon_enchant &ench, bool quiet) // they're supposed to follow you now. patrol_point = coord_def(0, 0); } + if (you.can_see(this)) + learned_something_new(TUT_MONSTER_FRIENDLY); break; default: diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc index 0affe99ecf..e06dd5573d 100644 --- a/crawl-ref/source/monstuff.cc +++ b/crawl-ref/source/monstuff.cc @@ -5953,6 +5953,7 @@ static void _mons_open_door(monsters* monster, const coord_def &pos) mprf("%s was actually a secret door!", feature_description(grid, NUM_TRAPS, false, DESC_CAP_THE, false).c_str()); + learned_something_new(TUT_SEEN_SECRET_DOOR, pos.x, pos.y); } if (!you.can_see(monster)) diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index be84bed2a0..5fc8b1b569 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -4867,7 +4867,7 @@ void contaminate_player(int change, bool controlled, bool status_only) // get current contamination level int old_level = _get_contamination_level(); int new_level = 0; - +#define DEBUG_DIAGNOSTICS 1 #if DEBUG_DIAGNOSTICS if (change > 0 || (change < 0 && you.magic_contamination)) { @@ -4875,6 +4875,7 @@ void contaminate_player(int change, bool controlled, bool status_only) change, change + you.magic_contamination ); } #endif +#undef DEBUG_DIAGNOSTICS // make the change if (change + you.magic_contamination < 0) @@ -4890,6 +4891,9 @@ void contaminate_player(int change, bool controlled, bool status_only) // figure out new level new_level = _get_contamination_level(); + if (new_level >= 1) + learned_something_new(TUT_GLOWING); + if (status_only) { if (new_level > 0) @@ -5163,6 +5167,8 @@ bool rot_player( int amount ) (you.rotting) ? "rotting" : "start to rot" ); you.rotting += amount; + + learned_something_new(TUT_YOU_ROTTING); } return true; } @@ -6476,7 +6482,7 @@ bool player::can_see(const actor *target) const bool player::backlit(bool check_haloed) const { - return (magic_contamination >= 5 || duration[DUR_BACKLIGHT] + return (_get_contamination_level() >= 1 || duration[DUR_BACKLIGHT] || ((check_haloed) ? haloed() : false)); } diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc index de6aa4d5b3..3c9f911af9 100644 --- a/crawl-ref/source/religion.cc +++ b/crawl-ref/source/religion.cc @@ -4316,6 +4316,7 @@ void excommunication(god_type new_god) ASSERT(old_god != new_god); const bool was_haloed = you.haloed(); + const int old_piety = you.piety; god_acting gdact(old_god, true); @@ -4421,11 +4422,13 @@ void excommunication(god_type new_god) remove_divine_shield(); if (!is_good_god(new_god)) + { + _inc_penance( old_god, 50 ); _make_god_gifts_hostile(false); + } else _make_god_gifts_good_neutral(false); - _inc_penance( old_god, 50 ); break; case GOD_ZIN: @@ -4444,11 +4447,13 @@ void excommunication(god_type new_god) if (env.sanctuary_time) remove_sanctuary(); - _inc_penance( old_god, 25 ); + if (!is_good_god(new_god)) + _inc_penance( old_god, 25 ); break; case GOD_ELYVILON: - _inc_penance( old_god, 30 ); + if (!is_good_god(new_god)) + _inc_penance( old_god, 30 ); break; default: @@ -4460,6 +4465,8 @@ void excommunication(god_type new_god) // god, you make all non-hostile holy beings hostile. if (!is_good_god(new_god) && _holy_beings_attitude_change()) mpr("The divine host forsakes you.", MSGCH_MONSTER_ENCHANT); + + learned_something_new(TUT_EXCOMMUNICATE, (int) new_god, old_piety); } // end excommunication() static bool _bless_weapon( god_type god, int brand, int colour ) @@ -5057,6 +5064,8 @@ void god_pitch(god_type which_god) gain_piety(20); // allow instant access to first power redraw_skill(you.your_name, player_title()); + + learned_something_new(TUT_CONVERT); } // end god_pitch() bool god_hates_your_god(god_type god, diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc index 68cc286348..f3e68650fb 100644 --- a/crawl-ref/source/spl-cast.cc +++ b/crawl-ref/source/spl-cast.cc @@ -57,6 +57,7 @@ #include "state.h" #include "stuff.h" #include "transfor.h" +#include "tutorial.h" #include "view.h" #include "xom.h" @@ -692,7 +693,10 @@ bool cast_a_spell() { const int spellh = calc_hunger( spell_hunger(spell) ); if (spellh > 0) + { make_hungry(spellh, true); + learned_something_new(TUT_SPELL_HUNGER); + } } you.turn_is_over = true; @@ -1039,6 +1043,7 @@ spret_type your_spells( spell_type spell, int powc, bool allow_fail ) mpr( "You miscast the spell." ); flush_input_buffer( FLUSH_ON_FAILURE ); + learned_something_new( TUT_SPELL_MISCAST ); if (you.religion == GOD_SIF_MUNA && !player_under_penance() diff --git a/crawl-ref/source/state.cc b/crawl-ref/source/state.cc index d3b6756547..7ed5d08de1 100644 --- a/crawl-ref/source/state.cc +++ b/crawl-ref/source/state.cc @@ -190,12 +190,7 @@ bool interrupt_cmd_repeat( activity_interrupt_type ai, } if (Options.tutorial_left) - { - // enforce that this message comes first tutorial_first_monster(*mon); - if (get_mons_colour(mon) != mon->colour) - learned_something_new(TUT_MONSTER_BRAND); - } #else formatted_string fs( channel_to_colour(MSGCH_WARN) ); fs.cprintf("%s (", mon->name(DESC_PLAIN, true).c_str()); diff --git a/crawl-ref/source/tutorial.cc b/crawl-ref/source/tutorial.cc index a451f97260..d0cfaa655e 100644 --- a/crawl-ref/source/tutorial.cc +++ b/crawl-ref/source/tutorial.cc @@ -15,6 +15,7 @@ #include "tutorial.h" #include "cio.h" +#include "cloud.h" #include "command.h" #include "food.h" #include "format.h" @@ -46,10 +47,12 @@ static species_type _get_tutorial_species(unsigned int type); static job_type _get_tutorial_job(unsigned int type); - +static void _tutorial_describe_disturbance(int x, int y); +static void _tutorial_describe_cloud(int x, int y); +static bool _water_is_disturbed(int x, int y); //#define TUTORIAL_DEBUG -#define TUTORIAL_VERSION 111 +#define TUTORIAL_VERSION 112 static int _get_tutorial_cols() { @@ -316,6 +319,8 @@ static std::string _tut_debug_list(int event) return "seen first food"; case TUT_SEEN_CARRION: return "seen first corpse"; + case TUT_SEE_GOLD: + return "seen first pile of gold"; case TUT_SEEN_JEWELLERY: return "seen first jewellery"; case TUT_SEEN_MISC: @@ -326,6 +331,8 @@ static std::string _tut_debug_list(int event) return "seen first stairs"; case TUT_SEEN_ESCAPE_HATCH: return "seen first escape hatch"; + case TUT_SEEN_BRANCH: + return "seen first branch entrance"; case TUT_SEEN_TRAP: return "encountered a trap"; case TUT_SEEN_ALTAR: @@ -334,6 +341,8 @@ static std::string _tut_debug_list(int event) return "seen a shop"; case TUT_SEEN_DOOR: return "seen a closed door"; + case TUT_SEEN_SECRET_DOOR: + return "found a secret door"; case TUT_KILLED_MONSTER: return "killed first monster"; case TUT_NEW_LEVEL: @@ -346,6 +355,8 @@ static std::string _tut_debug_list(int event) return "became sick"; case TUT_YOU_POISON: return "were poisoned"; + case TUT_YOU_ROTTING: + return "were rotting"; case TUT_YOU_CURSED: return "had something cursed"; case TUT_YOU_HUNGRY: @@ -370,6 +381,8 @@ static std::string _tut_debug_list(int event) return "encountered an invisible foe"; case TUT_NEED_HEALING_INVIS: return "had to heal near an unseen monster"; + case TUT_ABYSS: + return "was cast into the Abyss"; case TUT_POSTBERSERK: return "learned about Berserk aftereffects"; case TUT_RUN_AWAY: @@ -392,6 +405,20 @@ static std::string _tut_debug_list(int event) return "made a monster flee"; case TUT_MONSTER_BRAND: return "learned about colour brandings"; + case TUT_MONSTER_FRIENDLY: + return "seen first friendly monster"; + case TUT_CONVERT: + return "converted to a god"; + case TUT_EXCOMMUNICATE: + return "excommunicated by a god"; + case TUT_SPELL_MISCAST: + return "spell miscast"; + case TUT_SPELL_HUNGER: + return "spell casting caused hunger"; + case TUT_GLOWING: + return "player glowing from contamination"; + case TUT_STAIR_BRAND: + return "saw stairs with objects on it"; default: return "faced a bug"; } @@ -950,6 +977,9 @@ void taken_new_item(unsigned char item_type) case OBJ_STAVES: learned_something_new(TUT_SEEN_STAFF); break; + case OBJ_GOLD: + learned_something_new(TUT_SEEN_GOLD); + break; default: /* nothing to be done */ return; } @@ -1034,7 +1064,14 @@ static bool _advise_use_wand() void tutorial_first_monster(const monsters &mon) { if (!Options.tutorial_events[TUT_SEEN_MONSTER]) + { + if (get_mons_colour(&mon) != mon.colour) + learned_something_new(TUT_MONSTER_BRAND); + if (mons_friendly(&mon)) + learned_something_new(TUT_MONSTER_FRIENDLY); + return; + } // crude hack: // if the first monster is sleeping wake it @@ -1122,6 +1159,11 @@ void tutorial_first_monster(const monsters &mon) formatted_message_history(text, MSGCH_TUTORIAL, 0, _get_tutorial_cols()); } + + if (get_mons_colour(&mon) != mon.colour) + learned_something_new(TUT_MONSTER_BRAND, mon.x, mon.y); + if (mons_friendly(&mon)) + learned_something_new(TUT_MONSTER_FRIENDLY, mon.x, mon.y); } void tutorial_first_item(const item_def &item) @@ -1194,6 +1236,245 @@ void tutorial_first_item(const item_def &item) learned_something_new(TUT_SEEN_CARRION, item.x, item.y); } +static void _new_god_conduct() +{ + std::ostringstream text; + + const std::string new_god_name = god_name(you.religion); + + text << "You've just converted to worshiping " << new_god_name + << ". "; + + if (you.religion == GOD_XOM) + { + text << "You can keep Xom happy by keeping him amused; you do " + "<w>not</w> want Xom to grow bored with you. If you've " + "kept him amused he'll treat you like a plaything, " + "randomly helping and harming you for his own amusement."; + return; + } + + text << "Your piety (divine favor) gradually decreases over time, and " + "if it runs out " << new_god_name << " will excommunicate you " + "and punish you. You can prevent this, and even gain " + "enough piety to get powers and divine gifts, by doing things " + "to please " << new_god_name << ". And don't panic: you " + "start out with a decent amount of piety, so any danger of " + "excommunication is far off.\n"; + + formatted_message_history(text.str(), MSGCH_TUTORIAL, 0, + _get_tutorial_cols()); + text.str(""); + + std::vector<std::string> likes; + + // Unique/unusual piety gain methods first + switch(you.religion) + { + case GOD_SIF_MUNA: + likes.push_back("train your various spell casting skills"); + break; + + case GOD_TROG: + likes.push_back("destroy spell books (especially ones you've" + "never touched) via the <w>a</w> key"); + break; + + case GOD_NEMELEX_XOBEH: + likes.push_back("draw unmarked cards and use up decks"); + break; + + case GOD_ELYVILON: + likes.push_back("destroy weapons (especially evil ones) via " + "the <w>a</w> key"); + break; + + default: + break; + } + + switch(you.religion) + { + case GOD_SHINING_ONE: case GOD_KIKUBAAQUDGHA: case GOD_OKAWARU: + case GOD_MAKHLEB: case GOD_SIF_MUNA: case GOD_TROG: + likes.push_back("sacrifice items (by dropping them on an altar " + "and praying)"); + break; + + case GOD_NEMELEX_XOBEH: + likes.push_back("sacrifice items (by standing over them and " + "praying)"); + break; + + case GOD_ZIN: + likes.push_back("sacrifice gold (by praying at an altar)"); + break; + + default: + break; + } + + if (god_likes_butchery(you.religion)) + likes.push_back("butcher corpses while praying"); + + switch(you.religion) + { + case GOD_KIKUBAAQUDGHA: case GOD_YREDELEMNUL: case GOD_OKAWARU: + case GOD_VEHUMET: case GOD_MAKHLEB: case GOD_TROG: + case GOD_BEOGH: case GOD_LUGONU: + likes.push_back("kill living beings"); + break; + + default: + break; + } + + switch(you.religion) + { + case GOD_SHINING_ONE: case GOD_OKAWARU: case GOD_VEHUMET: + case GOD_MAKHLEB: case GOD_LUGONU: + likes.push_back("kill the undead"); + break; + + default: + break; + } + + switch(you.religion) + { + case GOD_SHINING_ONE: case GOD_OKAWARU: case GOD_MAKHLEB: + likes.push_back("kill demons"); + break; + + default: + break; + } + + // Unusual kills + switch(you.religion) + { + case GOD_ZIN: + likes.push_back("kill monsters which cause mutation or rotting"); + break; + + case GOD_SHINING_ONE: + likes.push_back("kill living evil beings"); + break; + + case GOD_BEOGH: + likes.push_back("kill the priests of other religions"); + break; + + case GOD_TROG: + likes.push_back("kill wizards and other users of magic"); + break; + + default: + break; + } + + if (likes.size() == 0) + { + mprf(MSGCH_ERROR, + " %s doesn't like anything? This a bug; please report it.", + new_god_name.c_str()); + } + else + { + text << new_god_name << " likes it when you "; + text << comma_separated_line(likes.begin(), likes.end(), + " and ", ", "); + text << "."; + formatted_message_history(text.str(), MSGCH_TUTORIAL, 0, + _get_tutorial_cols()); + text.str(""); + } + + std::vector<std::string> dislikes; + + if (god_hates_butchery(you.religion)) + dislikes.push_back("butcher corpses while praying"); + + if (is_good_god(you.religion)) + { + dislikes.push_back("drink blood"); + dislikes.push_back("perform cannibalism"); + dislikes.push_back("use necromancy"); + dislikes.push_back("use unholy magic or items"); + dislikes.push_back("attack holy beings"); + dislikes.push_back("attack neutral beings"); + } + + switch(you.religion) + { + case GOD_ZIN: case GOD_SHINING_ONE: case GOD_ELYVILON: + case GOD_OKAWARU: + dislikes.push_back("attack allies"); + break; + + case GOD_BEOGH: + dislikes.push_back("attack allied orcs"); + break; + + default: + break; + } + + switch(you.religion) + { + case GOD_ELYVILON: case GOD_ZIN: case GOD_OKAWARU: + dislikes.push_back("allow an ally to die"); + break; + + default: + break; + } + + switch(you.religion) + { + case GOD_ZIN: + dislikes.push_back("cause yourself to mutate in a way that could " + "have been avoided"); + dislikes.push_back("eat the flesh of sentient beings"); + break; + + case GOD_SHINING_ONE: + dislikes.push_back("poison a monster"); + dislikes.push_back("attack in an unchivalric manner"); + break; + + case GOD_ELYVILON: + dislikes.push_back("kill a living thing while praying"); + break; + + case GOD_TROG: + dislikes.push_back("memorize spells"); + dislikes.push_back("cast spells"); + break; + + default: + break; + } + + if (dislikes.size() > 0) + { + text << "\n"; + text << new_god_name << " dislikes it when you "; + text << comma_separated_line(dislikes.begin(), dislikes.end(), + " or ", ", "); + text << "."; + formatted_message_history(text.str(), MSGCH_TUTORIAL, 0, + _get_tutorial_cols()); + } +} + +#define DELAY_EVENT \ +{ \ + Options.tutorial_events[seen_what] = 1; \ + Options.tutorial_left++; \ + return; \ +} + // Here most of the tutorial messages for various triggers are handled. void learned_something_new(tutorial_event_type seen_what, int x, int y) { @@ -1511,17 +1792,28 @@ void learned_something_new(tutorial_event_type seen_what, int x, int y) "<w>i</w>nventory."; break; + case TUT_SEEN_GOLD: + text << "You have picked up your first pile of gold" +#ifndef USE_TILE + " ('<yellow>$</yellow>')" +#endif + ". Unlike all other objects in Crawl it doesn't show up in " + "your inventory, takes up no space in your inventory, weighs " + "nothing and can't be dropped. Gold can be used to buy " + "items from shops, and can also be sacrificed to some gods."; + break; + case TUT_SEEN_STAIRS: // Don't give this information during the first turn, to give // the player time to have a look around. if (you.num_turns < 1) - return; + DELAY_EVENT; text << "These "; #ifndef USE_TILE // monsters standing on stairs - if (mgrd[ex][ey] != NON_MONSTER) - return; + if (mgrd[x][y] != NON_MONSTER) + DELAY_EVENT; object = env.show[ex][ey]; colour = env.show_col[ex][ey]; @@ -1544,7 +1836,11 @@ void learned_something_new(tutorial_event_type seen_what, int x, int y) case TUT_SEEN_ESCAPE_HATCH: if (you.num_turns < 1) - return; + DELAY_EVENT; + + // monsters standing on stairs + if (mgrd[x][y] != NON_MONSTER) + DELAY_EVENT; text << "These "; #ifndef USE_TILE @@ -1567,6 +1863,53 @@ void learned_something_new(tutorial_event_type seen_what, int x, int y) ", but will usually be unable to return right away."; break; + case TUT_SEEN_BRANCH: + text << "These "; +#ifndef USE_TILE + // monsters standing on stairs + if (mgrd[x][y] != NON_MONSTER) + DELAY_EVENT; + + // XXX: Branch entrace character not being colored yellow. + object = env.show[ex][ey]; + colour = env.show_col[ex][ey]; + { unsigned short dummy; get_item_symbol( object, &ch, &dummy ); } + + text << _colourize_glyph(colour, ch) << " "; +#else + tile_place_cursor(ep.x-1,ep.y-1,true); +#endif + text << "are the entrance to a different branch of the dungeon, " + "which might have different terrain, level layout and " + "monsters than the current main branch you're in. Branches " + "can range from being up to ten levels deep to having only " + "a single level. They can also contain entrances to other " + "branches." + + "\n\nThe first three branches you'll encounter are the " + "Temple, the Orcish Mines and the Lair. While the Mines " + "and the Lair can be dangerous for the new adventurer, " + "the Temple is completely safe and contains a number of " + "altars at which you might convert to a new god."; + break; + + case TUT_STAIR_BRAND: +#ifdef USE_TILE + // XXX: How does stair branding work with tiles? + return; +#else + // monster or player standing on stairs + if (mgrd[x][y] != NON_MONSTER || (you.x_pos == x + && you.y_pos == y)) + DELAY_EVENT; + + text << "If any items are covering stairs or an escape hatch then " + "that will be indicated by highlighting the <w><<</w> or " + "<w>></w> symbol, instead of hiding the stair/hatch symbol " + "with an item sybmol."; +#endif + break; + case TUT_SEEN_TRAP: text << "Oops... you just triggered a trap. An unwary adventurer " "will occasionally stumble into one of these nasty " @@ -1627,7 +1970,7 @@ void learned_something_new(tutorial_event_type seen_what, int x, int y) case TUT_SEEN_DOOR: if (you.num_turns < 1) - return; + DELAY_EVENT; #ifdef USE_TILES tile_place_cursor(ep.x-1,ep.y-1,true); @@ -1646,6 +1989,33 @@ void learned_something_new(tutorial_event_type seen_what, int x, int y) #endif break; + case TUT_SEEN_SECRET_DOOR: +#ifdef USE_TILES + tile_place_cursor(ep.x-1,ep.y-1,true); +#endif + text << "That " +#ifndef USE_TILE + << _colourize_glyph(WHITE, get_screen_glyph(x,y)) << " " +#endif + "was a secret door. You can actively try to find secret " + "doors by searching. To search for one turn, press <w>s</w>, " + "<w>.</w>, <w>delete</w> or <w>keypad-5</w>. Pressing " + "<w>5</w> or <w>shift-and-keypad-5</w> " +#ifdef USE_TILE + ", or clicking into the stat area " +#endif + "will search 100 times, stopping early if you find any " + "secret doors or traps, or when your HP or MP fully " + "recovers.\n\n" + + "If you can't find all three (or any) of the down stairs " + "on a level you should try searching for secret doors, since " + "the missing stairs might be in sections of the level blocked " + "off by them. If you can't find any secret doors then the " + "missing stairs are in sections of the level totally " + "disconnected from the section you're searching."; + break; + case TUT_KILLED_MONSTER: text << "Congratulations, your character just gained some experience " "by killing this monster! Every action will use up some of " @@ -1729,8 +2099,12 @@ void learned_something_new(tutorial_event_type seen_what, int x, int y) break; case TUT_YOU_POISON: + // Hack: reset tut_just_triggered, to force recursive calling of + // learned_something_new(). + Options.tut_just_triggered = false; learned_something_new(TUT_YOU_ENCHANTED); - text << "Poison will slowly reduce your hp. It wears off with time ("; + Options.tut_just_triggered = true; + text << "Poison will slowly reduce your HP. It wears off with time ("; if (!i_feel_safe()) text << "find a quiet corner and "; @@ -1742,6 +2116,25 @@ void learned_something_new(tutorial_event_type seen_what, int x, int y) "), or you could quaff a potion of healing. "; break; + case TUT_YOU_ROTTING: + // Hack: reset tut_just_triggered, to force recursive calling of + // learned_something_new(). + Options.tut_just_triggered = false; + learned_something_new(TUT_YOU_ENCHANTED); + Options.tut_just_triggered = true; + text << "Ugh, your flesh is rotting! Not only does this slowly " + "reduce your HP, it also slowly reduces your <w>maximum</w> " + "HP (your usual maximum HP will be indicated by a number in " + "parentheses at the end of the \"Health\" line in the " + "stat area of the window). While you can wait it out, " + "you'll probably want to stop it as soon as possible by " + "drinking a potion of healing, since the longer you wait " + "the more your maximum HP will be reduced. Once you've " + "stopped rotting you can restore your maximum HP to normal " + "by drinking potions of healing and heal wounds while " + "fully healed."; + 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 " @@ -2005,15 +2398,9 @@ void learned_something_new(tutorial_event_type seen_what, int x, int y) switch(you.religion) { // Gods where first granted ability is active. - case GOD_ZIN: - case GOD_KIKUBAAQUDGHA: - case GOD_YREDELEMNUL: - case GOD_OKAWARU: - case GOD_SIF_MUNA: - case GOD_TROG: - case GOD_NEMELEX_XOBEH: - case GOD_ELYVILON: - case GOD_LUGONU: + case GOD_KIKUBAAQUDGHA: case GOD_YREDELEMNUL: case GOD_NEMELEX_XOBEH: + case GOD_ZIN: case GOD_OKAWARU: case GOD_SIF_MUNA: + case GOD_TROG: case GOD_ELYVILON: case GOD_LUGONU: text << "You just gained a new ability. Press <w>a</w> to " "take a look at your abilities or to use one of them."; break; @@ -2022,9 +2409,71 @@ void learned_something_new(tutorial_event_type seen_what, int x, int y) default: text << "You just gained a new ability. Press <w>^</w> to " "take a look at your ability."; + break; } break; + case TUT_CONVERT: + _new_god_conduct(); + break; + + case TUT_EXCOMMUNICATE: + { + const god_type new_god = (god_type) x; + const int old_piety = y; + + god_type old_god = GOD_NO_GOD; + for (int i = 0; i < MAX_NUM_GODS; i++) + if (you.worshipped[i] > 0) + { + old_god = (god_type) i; + break; + } + + const std::string old_god_name = god_name(old_god); + const std::string new_god_name = god_name(new_god); + + if (new_god == GOD_NO_GOD) + { + if (old_piety < 1) + text << "Uh-oh, " << old_god << " just excommunicated you " + "for running out of piety (your divine favour went " + "to nothing). Either you repeatedly annoyed him, " + "you weren't doing things that pleased him often " + "enough, or some combination of the two. If you " + "can find an altar dedicated to " << old_god + << " you can re-convert and all will be well, otherwise " + "you'll have to weather his displeasure until his " + "wrath is spent."; + else + text << "You just renounced your religion? Are you " + "<w>sure</w> about that? If you can find an " + "altar dedicated to " << old_god << " you can " + "re-convert and all will be well, otherwise you'll " + "have to weather his displeasure until his wrath is " + "spent."; + } + else + { + if (is_good_god(old_god) && is_good_god(new_god)) + text << "Fortunately, it seems that " << old_god << " didn't " + "mind you converting to " << new_god << ". This " + "is only the case when converting from one of the " + "three good gods to a different good god, so don't " + "expect this to be the norm."; + else + text << "Looks like " << old_god << " didn't appreciate you " + "converting to " << new_god << " (it's only safe to " + "convert between gods if both of them is among the " + "three good gods). Unfortunately, converting back to " + << old_god << " will annoy " << new_god << + ", so you're stuck with having to suffer the wrath " + "of one god or another."; + } + + break; + } + case TUT_WIELD_WEAPON: if (Options.tutorial_type == TUT_RANGER_CHAR && you.inv[ you.equip[EQ_WEAPON] ].sub_type == WPN_BOW) @@ -2071,10 +2520,79 @@ void learned_something_new(tutorial_event_type seen_what, int x, int y) #endif break; + case TUT_MONSTER_FRIENDLY: +#ifdef USE_TILE + tile_place_cursor(ep.x-1,ep.y-1,true); +#endif + text << "That monster is friendly to you and will attack your " + "enemies, though you get only half the experience for " + "monsters killed by allies that you'd get for killing them " + "yourself. You can command your allies by pressing <w>t</w> " + "to talk to them, and can tell them which items to pick up " + "(or not pick up) by pressing <w>Ctrl-T</w>."; + break; + case TUT_SEEN_MONSTER: case TUT_SEEN_FIRST_OBJECT: break; + case TUT_ABYSS: + text << "Uh-oh, you've wound up in the Abyss!. The Abyss is filled " + "with nasty monsters, you can't remember or map where you've " + "been, and you're probably going to die. If you want to " + "survive until you can find the exit (a flickering " + "<w>\\</w>), keep moving, don't fight any of the " + "monsters, and don't bother picking up any items on the " + "ground. If you're encumbered or overburdened then " + "lighten up your load, and if the monsters are closing in " + "use potions of speed to get away. And if you can, move " + "in a direction slightly off from a compass direction (for " + "example, north-by-northwest instead of north or northwest), " + "as you'll likely miss the exist if you keep heading solely " + "in a compass direction."; + break; + + case TUT_SPELL_MISCAST: + text << "You just miscast a spell. If the spell casting success " + "chance is high (which can be checked by entering <w>z\?</w>) " + "then a miscast merely means the spell not working, along " + "with a harmless side effect. However, for spells with a " + "low success rate there's a chance of contaminating " + "yourself with magical energy, plus a chance of an " + "additional harmful side effect. Normally this isn't a " + "problem, since magical contamination bleeds off over time, " + "but if you're repeatedly contaminated too often in a short" + "amount of time you'll mutate and suffer from other ill " + "side effects.\n\n" + + "Note that a miscast spell will still consume the full amount " + "of MP and nutrition that a successfully cast spell would."; + break; + + case TUT_SPELL_HUNGER: + text << "The spell you just cast made you hungrier; you can see " + "how hungry spells make you by entering <w>z\?!</w>. The " + "amount of nutrition consumed increases with the level of " + "the spell and decreases according to your intelligence " + "stat multiplied by your Spellcasting skill; if your " + "intelligence and Spellcasting are high enough a spell " + "might not cost you any nutrition at all."; + break; + + case TUT_GLOWING: + text << "Uh-oh, you've accumulated so much magical contamination that " + "you're glowing! You acquire magical contamination from " + "using some powerful magics, like invisibility, haste/speed " + "and potions of resistance. This normally isn't a problem, " + "since contamination slowly bleeds off on its own, but it" + "seems that you've contaminated yourself so many time is " + "such a short amount of time that you're in trouble. " + "Now that you're glowing the contamination is going to " + "mutate you, and possibly even damage you via magical " + "storms. Additionally, glowing is going to make you much " + "less stealthy."; + break; + default: text << "You've found something new (but I don't know what)!"; } @@ -2718,6 +3236,13 @@ void tutorial_inscription_info(bool autoinscribe) formatted_string::parse_string(text.str()).display(); } +bool tutorial_pos_interesting(int x, int y) +{ + return (cloud_type_at(coord_def(x, y)) != CLOUD_NONE + || _water_is_disturbed(x, y) + || tutorial_feat_interesting(grd[x][y])); +} + bool tutorial_feat_interesting(dungeon_feature_type feat) { if (feat >= DNGN_ALTAR_FIRST_GOD && feat <= DNGN_ALTAR_LAST_GOD) @@ -2746,6 +3271,13 @@ bool tutorial_feat_interesting(dungeon_feature_type feat) } } +void tutorial_describe_pos(int x, int y) +{ + _tutorial_describe_disturbance(x, y); + _tutorial_describe_cloud(x, y); + tutorial_describe_feature(grd[x][y]); +} + void tutorial_describe_feature(dungeon_feature_type feat) { std::ostringstream ostr; @@ -2808,7 +3340,7 @@ void tutorial_describe_feature(dungeon_feature_type feat) } else { - ostr << "You can enter the previous (lower) level by following " + ostr << "You can enter the previous (shallower) level by following " "these up (<w><<</w>). To get back to this level " "again, press <w>></w> while standing on the " "downstairs."; @@ -2910,6 +3442,88 @@ void tutorial_describe_feature(dungeon_feature_type feat) formatted_string::parse_block(broken, false).display(); } // tutorial_describe_feature +static void _tutorial_describe_cloud(int x, int y) +{ + cloud_type ctype = cloud_type_at(coord_def(x, y)); + if (ctype == CLOUD_NONE) + return; + + std::string cname = cloud_name(ctype); + + std::ostringstream ostr; + + ostr << "\n\n<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; + + ostr << "The " << cname << " "; + + if (ends_with(cname, "s")) + ostr << "are "; + else + ostr << "is "; + + switch(ctype) + { + case CLOUD_BLACK_SMOKE: + case CLOUD_GREY_SMOKE: + case CLOUD_BLUE_SMOKE: + case CLOUD_PURP_SMOKE: + case CLOUD_MIST: + ostr << "harmless. "; + break; + + default: + ostr << "dangerous, and you should stay out of it if you can. "; + } + + if (is_opaque_cloud(env.cgrid[x][y])) + ostr << "It is opaque. If two or more opaque clouds are between " + "you and a square you won't be able to see anything in that " + "square."; + + ostr << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; + + std::string broken = ostr.str(); + linebreak_string2(broken, _get_tutorial_cols()); + formatted_string::parse_block(broken, false).display(); +} + +static void _tutorial_describe_disturbance(int x, int y) +{ + if (!_water_is_disturbed(x, y)) + return; + + std::ostringstream ostr; + + ostr << "\n\n<" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; + + ostr << "The strange disturbance means that there's a monster hiding " + "under the surface of the shallow water. Distance attacks " + "which only hit a single target may not hit submereged monsters " + "unless you use the <w>!</w> key to fire your ammo/spell/wand " + "after selecting the sumberged target."; + + ostr << "</" << colour_to_str(channel_to_colour(MSGCH_TUTORIAL)) << ">"; + + std::string broken = ostr.str(); + linebreak_string2(broken, _get_tutorial_cols()); + formatted_string::parse_block(broken, false).display(); +} + +static bool _water_is_disturbed(int x, int y) +{ + int mon_num = mgrd[x][y]; + + if (mon_num == NON_MONSTER || grd[x][y] != DNGN_SHALLOW_WATER + || !see_grid(x, y)) + { + return false; + } + + const monsters *mon = &menv[mon_num]; + + return (!player_monster_visible(mon) && !mons_flies(mon)); +} + bool tutorial_monster_interesting(const monsters *mons) { if (mons_is_unique(mons->type) || mons->type == MONS_PLAYER_GHOST) diff --git a/crawl-ref/source/tutorial.h b/crawl-ref/source/tutorial.h index 694831d5f1..0d28e29d31 100644 --- a/crawl-ref/source/tutorial.h +++ b/crawl-ref/source/tutorial.h @@ -48,6 +48,8 @@ void print_tut_skills_info(void); // additional information for tutorial players void tutorial_describe_item(const item_def &item); void tutorial_inscription_info(bool autoinscribe); +bool tutorial_pos_interesting(int x, int y); +void tutorial_describe_pos(int x, int y); bool tutorial_feat_interesting(dungeon_feature_type feat); void tutorial_describe_feature(dungeon_feature_type feat); bool tutorial_monster_interesting(const monsters *mons); diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc index cfb056437c..6afaa25843 100644 --- a/crawl-ref/source/view.cc +++ b/crawl-ref/source/view.cc @@ -4687,14 +4687,26 @@ void viewwindow(bool draw_it, bool do_updates) learned_something_new(TUT_SEEN_ESCAPE_HATCH, gc.x, gc.y); } + else if (grid_is_branch_stairs(grd(gc))) + learned_something_new(TUT_SEEN_BRANCH, gc.x, gc.y); else if (is_feature('>', gc.x, gc.y)) + { learned_something_new(TUT_SEEN_STAIRS, gc.x, gc.y); + } else if (is_feature('_', gc.x, gc.y)) learned_something_new(TUT_SEEN_ALTAR, gc.x, gc.y); else if (grd(gc) == DNGN_CLOSED_DOOR) learned_something_new(TUT_SEEN_DOOR, gc.x, gc.y); else if (grd(gc) == DNGN_ENTER_SHOP) learned_something_new(TUT_SEEN_SHOP, gc.x, gc.y); + + if (igrd[gc.x][gc.y] != NON_ITEM + && Options.feature_item_brand != CHATTR_NORMAL + && (is_feature('>', gc.x, gc.y) + || is_feature('<', gc.x, gc.y))) + { + learned_something_new(TUT_STAIR_BRAND, gc.x, gc.y); + } } } |