summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/source/delay.cc5
-rw-r--r--crawl-ref/source/describe.cc2
-rw-r--r--crawl-ref/source/directn.cc2
-rw-r--r--crawl-ref/source/enum.h38
-rw-r--r--crawl-ref/source/externs.h2
-rw-r--r--crawl-ref/source/items.cc2
-rw-r--r--crawl-ref/source/misc.cc5
-rw-r--r--crawl-ref/source/mon-util.cc3
-rw-r--r--crawl-ref/source/monstuff.cc1
-rw-r--r--crawl-ref/source/player.cc10
-rw-r--r--crawl-ref/source/religion.cc15
-rw-r--r--crawl-ref/source/spl-cast.cc5
-rw-r--r--crawl-ref/source/state.cc5
-rw-r--r--crawl-ref/source/tutorial.cc650
-rw-r--r--crawl-ref/source/tutorial.h2
-rw-r--r--crawl-ref/source/view.cc12
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);
+ }
}
}