summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
authordshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2007-02-06 16:03:52 +0000
committerdshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2007-02-06 16:03:52 +0000
commit24be3228d463d6b2501cbca9368e14e91ab42182 (patch)
tree5fd30f60a43e1e69188bce4e30646c3e89e288b2 /crawl-ref
parentf878a03c781fd57fea8447caaecee847ab576d8b (diff)
downloadcrawl-ref-24be3228d463d6b2501cbca9368e14e91ab42182.tar.gz
crawl-ref-24be3228d463d6b2501cbca9368e14e91ab42182.zip
Tutorial (JPEG) and some formatting cleanup.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@924 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref')
-rw-r--r--crawl-ref/source/FixVec.h14
-rw-r--r--crawl-ref/source/Kills.cc9
-rw-r--r--crawl-ref/source/acr.cc131
-rw-r--r--crawl-ref/source/clua.cc27
-rw-r--r--crawl-ref/source/command.cc65
-rw-r--r--crawl-ref/source/command.h1
-rw-r--r--crawl-ref/source/delay.cc5
-rw-r--r--crawl-ref/source/dungeon.cc12
-rw-r--r--crawl-ref/source/enum.h1
-rw-r--r--crawl-ref/source/externs.h16
-rw-r--r--crawl-ref/source/fight.cc70
-rw-r--r--crawl-ref/source/files.cc20
-rw-r--r--crawl-ref/source/food.cc3
-rw-r--r--crawl-ref/source/initfile.h21
-rw-r--r--crawl-ref/source/invent.h3
-rw-r--r--crawl-ref/source/item_use.cc67
-rw-r--r--crawl-ref/source/items.cc32
-rw-r--r--crawl-ref/source/libutil.cc12
-rw-r--r--crawl-ref/source/libw32c.cc30
-rw-r--r--crawl-ref/source/makefile.obj1
-rw-r--r--crawl-ref/source/message.cc40
-rw-r--r--crawl-ref/source/misc.cc8
-rw-r--r--crawl-ref/source/monstuff.cc10
-rw-r--r--crawl-ref/source/newgame.cc151
-rw-r--r--crawl-ref/source/newgame.h2
-rw-r--r--crawl-ref/source/notes.cc70
-rw-r--r--crawl-ref/source/notes.h6
-rw-r--r--crawl-ref/source/ouch.cc6
-rw-r--r--crawl-ref/source/player.cc15
-rw-r--r--crawl-ref/source/religion.cc2
-rw-r--r--crawl-ref/source/skills.cc2
-rw-r--r--crawl-ref/source/skills2.cc9
-rw-r--r--crawl-ref/source/stash.cc7
-rw-r--r--crawl-ref/source/stuff.cc23
-rw-r--r--crawl-ref/source/travel.cc9
-rw-r--r--crawl-ref/source/tutorial.cc1351
-rw-r--r--crawl-ref/source/tutorial.h100
-rw-r--r--crawl-ref/source/view.cc160
-rw-r--r--crawl-ref/source/view.h1
39 files changed, 2210 insertions, 302 deletions
diff --git a/crawl-ref/source/FixVec.h b/crawl-ref/source/FixVec.h
index 176ced5cf4..0086750ae3 100644
--- a/crawl-ref/source/FixVec.h
+++ b/crawl-ref/source/FixVec.h
@@ -22,7 +22,8 @@
// class FixedVector
// ==========================================================================
-template <class TYPE, int SIZE> class FixedVector {
+template <class TYPE, int SIZE> class FixedVector
+{
//-----------------------------------
// Types
@@ -48,7 +49,8 @@ public:
FixedVector() {}
- FixedVector(TYPE def) : mData() {
+ FixedVector(TYPE def) : mData()
+ {
init(def);
}
@@ -112,10 +114,10 @@ FixedVector<TYPE, SIZE>::FixedVector(TYPE value0, TYPE value1, ...)
va_list ap;
va_start(ap, value1); // second argument is last fixed parameter
- for (int index = 2; index < SIZE; index++) {
- TYPE value = va_arg(ap, TYPE);
-
- mData[index] = value;
+ for (int index = 2; index < SIZE; index++)
+ {
+ TYPE value = va_arg(ap, TYPE);
+ mData[index] = value;
}
va_end(ap);
diff --git a/crawl-ref/source/Kills.cc b/crawl-ref/source/Kills.cc
index bd1936db50..06b8dc8cd1 100644
--- a/crawl-ref/source/Kills.cc
+++ b/crawl-ref/source/Kills.cc
@@ -694,14 +694,17 @@ void kill_monster_desc::load(FILE *file)
//
#define KILLEXP_ACCESS(name, type, field) \
- static int kill_lualc_##name(lua_State *ls) { \
- if (!lua_islightuserdata(ls, 1)) { \
+ static int kill_lualc_##name(lua_State *ls) \
+ { \
+ if (!lua_islightuserdata(ls, 1)) \
+ { \
luaL_argerror(ls, 1, "Unexpected argument type"); \
return 0; \
} \
\
kill_exp *ke = static_cast<kill_exp*>( lua_touserdata(ls, 1) ); \
- if (ke) { \
+ if (ke) \
+ { \
lua_push##type(ls, ke->field); \
return 1; \
} \
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index 80019c23b0..ea6c8daeb1 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -122,6 +122,7 @@
#include "tags.h"
#include "transfor.h"
#include "travel.h"
+#include "tutorial.h"
#include "view.h"
#include "stash.h"
@@ -223,12 +224,15 @@ int main( int argc, char *argv[] )
bool game_start = initialise();
+ // override some options for tutorial
+ init_tutorial_options();
+
if (game_start || Options.always_greet)
{
- snprintf( info, INFO_SIZE, "Welcome, %s the %s %s.",
- you.your_name, species_name( you.species,you.experience_level ), you.class_name );
-
- mpr( info );
+ mprf( "Welcome, %s the %s %s.",
+ you.your_name,
+ species_name( you.species,you.experience_level ),
+ you.class_name );
// Starting messages can go here as this should only happen
// at the start of a new game -- bwr
@@ -295,6 +299,19 @@ int main( int argc, char *argv[] )
wield_warning(false);
}
+ if (Options.tutorial_left)
+ {
+ // print stats and everything
+ prep_input();
+ char ch = 'x';
+ mpr("Press any key to start the tutorial intro, or Escape to skip it.",
+ MSGCH_TUTORIAL);
+ ch = c_getch();
+
+ if (ch != ESCAPE)
+ tut_starting_screen();
+ }
+
if ( game_start )
{
snprintf(info, INFO_SIZE,
@@ -311,7 +328,6 @@ int main( int argc, char *argv[] )
while (true)
{
input();
- // cprintf("x");
}
// Should never reach this stage, right?
@@ -738,6 +754,8 @@ static void handle_wizard_command( void )
// Set up the running variables for the current run.
static void start_running( int dir, int mode )
{
+ if (Options.tutorial_events[TUT_SHIFT_RUN] && mode == RMODE_START)
+ Options.tutorial_events[TUT_SHIFT_RUN] = 0;
if (i_feel_safe(true))
you.running.initialise(dir, mode);
}
@@ -816,6 +834,30 @@ static void input()
fire_monster_alerts();
+
+ if (Options.tut_just_triggered)
+ Options.tut_just_triggered = false;
+
+ bool help;
+ if (Options.tutorial_events[TUT_SEEN_MONSTER])
+ help = i_feel_safe();
+
+ if (Options.tutorial_events[TUT_RUN_AWAY]
+ && 2*you.hp < you.hp_max && !i_feel_safe())
+ {
+ learned_something_new(TUT_RUN_AWAY);
+ }
+
+ if (Options.tutorial_left && i_feel_safe())
+ {
+ if ( 2*you.hp < you.hp_max || 2*you.magic_points < you.max_magic_points )
+ tutorial_healing_reminder();
+ else if (Options.tutorial_events[TUT_SHIFT_RUN] && you.num_turns >= 200)
+ learned_something_new(TUT_SHIFT_RUN);
+ else if (Options.tutorial_events[TUT_MAP_VIEW] && you.num_turns >= 500)
+ learned_something_new(TUT_MAP_VIEW);
+ }
+
if ( you.paralysis )
{
world_reacts();
@@ -868,7 +910,8 @@ static void input()
viewwindow(true, false);
}
-static int toggle_flag( bool* flag, const char* flagname ) {
+static int toggle_flag( bool* flag, const char* flagname )
+{
char buf[INFO_SIZE];
*flag = !(*flag);
sprintf( buf, "%s is now %s.", flagname,
@@ -877,7 +920,8 @@ static int toggle_flag( bool* flag, const char* flagname ) {
return *flag;
}
-static void go_upstairs() {
+static void go_upstairs()
+{
if (grd[you.x_pos][you.y_pos] == DNGN_ENTER_SHOP)
{
if ( you.berserker )
@@ -889,7 +933,8 @@ static void go_upstairs() {
else if ((grd[you.x_pos][you.y_pos] < DNGN_STONE_STAIRS_UP_I
|| grd[you.x_pos][you.y_pos] > DNGN_ROCK_STAIRS_UP)
&& (grd[you.x_pos][you.y_pos] < DNGN_RETURN_FROM_ORCISH_MINES
- || grd[you.x_pos][you.y_pos] >= 150)) {
+ || grd[you.x_pos][you.y_pos] >= 150))
+ {
mpr( "You can't go up here!" );
return;
}
@@ -899,8 +944,8 @@ static void go_upstairs() {
1 + (you.burden_state > BS_UNENCUMBERED) );
}
-static void go_downstairs() {
-
+static void go_downstairs()
+{
if ((grd[you.x_pos][you.y_pos] < DNGN_ENTER_LABYRINTH
|| grd[you.x_pos][you.y_pos] > DNGN_ROCK_STAIRS_DOWN)
&& grd[you.x_pos][you.y_pos] != DNGN_ENTER_HELL
@@ -908,7 +953,8 @@ static void go_downstairs() {
|| grd[you.x_pos][you.y_pos] > DNGN_TRANSIT_PANDEMONIUM)
&& grd[you.x_pos][you.y_pos] != DNGN_STONE_ARCH)
&& !(grd[you.x_pos][you.y_pos] >= DNGN_ENTER_ORCISH_MINES
- && grd[you.x_pos][you.y_pos] < DNGN_RETURN_FROM_ORCISH_MINES)) {
+ && grd[you.x_pos][you.y_pos] < DNGN_RETURN_FROM_ORCISH_MINES))
+ {
mpr( "You can't go down here!" );
return;
}
@@ -919,14 +965,16 @@ static void go_downstairs() {
you.your_level );
}
-static void experience_check() {
+static void experience_check()
+{
snprintf( info, INFO_SIZE, "You are a level %d %s %s.",
you.experience_level,
species_name(you.species,you.experience_level),
you.class_name);
mpr(info);
- if (you.experience_level < 27) {
+ if (you.experience_level < 27)
+ {
int xp_needed = (exp_needed(you.experience_level+2)-you.experience)+1;
snprintf( info, INFO_SIZE,
"Level %d requires %ld experience (%d point%s to go!)",
@@ -936,12 +984,14 @@ static void experience_check() {
(xp_needed > 1) ? "s" : "");
mpr(info);
}
- else {
+ else
+ {
mpr( "I'm sorry, level 27 is as high as you can go." );
mpr( "With the way you've been playing, I'm surprised you got this far." );
}
- if (you.real_time != -1) {
+ if (you.real_time != -1)
+ {
const time_t curr = you.real_time + (time(NULL) - you.start_time);
char buff[200];
@@ -962,13 +1012,14 @@ static void experience_check() {
/* note that in some actions, you don't want to clear afterwards.
e.g. list_jewellery, etc. */
-void process_command( command_type cmd ) {
+void process_command( command_type cmd )
+{
FixedVector < int, 2 > plox;
apply_berserk_penalty = true;
- switch ( cmd ) {
-
+ switch (cmd)
+ {
case CMD_OPEN_DOOR_UP_RIGHT: open_door(-1, -1); break;
case CMD_OPEN_DOOR_UP: open_door( 0, -1); break;
case CMD_OPEN_DOOR_UP_LEFT: open_door( 1, -1); break;
@@ -1033,12 +1084,14 @@ void process_command( command_type cmd ) {
break;
case CMD_MAKE_NOTE:
+// Options.tut_made_note = 0;
make_user_note();
break;
case CMD_CLEAR_MAP:
if (you.level_type != LEVEL_LABYRINTH &&
- you.level_type != LEVEL_ABYSS) {
+ you.level_type != LEVEL_ABYSS)
+ {
mpr("Clearing level map.");
clear_map();
}
@@ -1057,6 +1110,8 @@ void process_command( command_type cmd ) {
break;
case CMD_SEARCH_STASHES:
+ if (Options.tut_stashes)
+ Options.tut_stashes = 0;
stashes.search_stashes();
break;
@@ -1096,10 +1151,14 @@ void process_command( command_type cmd ) {
break;
case CMD_THROW:
+ if (Options.tutorial_left)
+ Options.tut_throw_counter++;
throw_anything();
break;
case CMD_FIRE:
+ if (Options.tutorial_left)
+ Options.tut_throw_counter++;
shoot_thing();
break;
@@ -1198,6 +1257,8 @@ void process_command( command_type cmd ) {
break;
}
+ if (Options.tutorial_left)
+ Options.tut_spell_counter++;
if (!cast_a_spell())
flush_input_buffer( FLUSH_ON_FAILURE );
break;
@@ -1216,6 +1277,8 @@ void process_command( command_type cmd ) {
break;
case CMD_INTERLEVEL_TRAVEL:
+ if (Options.tut_travel)
+ Options.tut_travel = 0;
if (!can_travel_interlevel())
{
mpr("Sorry, you can't auto-travel out of here.");
@@ -1227,6 +1290,8 @@ void process_command( command_type cmd ) {
break;
case CMD_EXPLORE:
+ if (Options.tut_explored)
+ Options.tut_explored = 0;
if (you.level_type == LEVEL_LABYRINTH || you.level_type == LEVEL_ABYSS)
{
mpr("It would help if you knew where you were, first.");
@@ -1237,6 +1302,8 @@ void process_command( command_type cmd ) {
break;
case CMD_DISPLAY_MAP:
+ if (Options.tutorial_events[TUT_MAP_VIEW])
+ Options.tutorial_events[TUT_MAP_VIEW] = 0;
#if (!DEBUG_DIAGNOSTICS)
if (you.level_type == LEVEL_LABYRINTH || you.level_type == LEVEL_ABYSS)
{
@@ -1289,7 +1356,10 @@ void process_command( command_type cmd ) {
#endif
case CMD_DISPLAY_COMMANDS:
- list_commands(false);
+ if (Options.tutorial_left)
+ list_tutorial_help();
+ else
+ list_commands(false);
redraw_screen();
break;
@@ -1372,7 +1442,8 @@ void process_command( command_type cmd ) {
}
}
-static void prep_input() {
+static void prep_input()
+{
you.time_taken = player_speed();
you.shield_blocks = 0; // no blocks this round
#ifdef UNIX
@@ -1847,7 +1918,8 @@ static void decrement_durations()
chances[1] = you.mutation[MUT_BERSERK] * 25;
chances[2] = (wearing_amulet( AMU_RAGE ) ? 10 : 0);
chances[3] = (player_has_spell( SPELL_BERSERKER_RAGE ) ? 5 : 0);
- const char* reasons[4] = {
+ const char* reasons[4] =
+ {
"You struggle, and manage to stay standing.",
"Your mutated body refuses to collapse.",
"You feel your neck pulse as blood rushes through your body.",
@@ -1897,6 +1969,9 @@ static void decrement_durations()
int dur = 12 + roll_dice( 2, 12 );
you.exhausted += dur;
+ // for tutorial
+ unsigned tut_slow = Options.tutorial_events[TUT_YOU_ENCHANTED];
+ Options.tutorial_events[TUT_YOU_ENCHANTED] = 0;
slow_player( dur );
make_hungry(700, true);
@@ -1905,6 +1980,9 @@ static void decrement_durations()
you.hunger = 50;
calc_hp();
+
+ learned_something_new(TUT_POSTBERSERK);
+ Options.tutorial_events[TUT_YOU_ENCHANTED] = tut_slow;
}
if (you.confusing_touch > 1)
@@ -2297,8 +2375,10 @@ static command_type get_next_cmd()
}
/* for now, this is an extremely yucky hack */
-command_type keycode_to_command( keycode_type key ) {
- switch ( key ) {
+command_type keycode_to_command( keycode_type key )
+{
+ switch ( key )
+ {
case 'b': return CMD_MOVE_DOWN_LEFT;
case 'h': return CMD_MOVE_LEFT;
case 'j': return CMD_MOVE_DOWN;
@@ -2423,7 +2503,8 @@ keycode_type get_next_keycode()
return (keyin);
}
-static void middle_input() {
+static void middle_input()
+{
if (Options.stash_tracking)
stashes.update_visible_stashes(
Options.stash_tracking == STM_ALL?
diff --git a/crawl-ref/source/clua.cc b/crawl-ref/source/clua.cc
index 0a4d1f3d17..ce8dcd358a 100644
--- a/crawl-ref/source/clua.cc
+++ b/crawl-ref/source/clua.cc
@@ -43,8 +43,10 @@
#include <cctype>
#define CL_RESETSTACK_RETURN(ls, oldtop, retval) \
- if (true) {\
- if (oldtop != lua_gettop(ls)) { \
+ if (true) \
+ {\
+ if (oldtop != lua_gettop(ls)) \
+ { \
lua_settop(ls, oldtop); \
} \
return (retval); \
@@ -318,13 +320,15 @@ int CLua::push_args(lua_State *ls, const char *format, va_list args,
format = cs + 1;
int argc = 0;
- for (const char *run = format; *run; run++) {
+ for (const char *run = format; *run; run++)
+ {
if (*run == '>')
break;
char argtype = *run;
++argc;
- switch (argtype) {
+ switch (argtype)
+ {
case 'u': // Light userdata
lua_pushlightuserdata(ls, va_arg(args, void*));
break;
@@ -1065,7 +1069,8 @@ static int l_item_class(lua_State *ls)
}
// FIXME: Fold this back into itemname.cc.
-static const char *ring_types[] = {
+static const char *ring_types[] =
+{
"regeneration",
"protection",
"protection from fire",
@@ -1092,7 +1097,8 @@ static const char *ring_types[] = {
"teleport control",
};
-static const char *amulet_types[] = {
+static const char *amulet_types[] =
+{
"rage", "resist slowing", "clarity", "warding", "resist corrosion",
"gourmand", "conservation", "controlled flight", "inaccuracy",
"resist mutation"
@@ -2013,7 +2019,8 @@ static int l_mons_y(lua_State *ls, monsters *mons, const char *attr)
return (1);
}
-struct MonsAccessor {
+struct MonsAccessor
+{
const char *attribute;
int (*accessor)(lua_State *ls, monsters *mons, const char *attr);
};
@@ -2159,7 +2166,8 @@ void lua_open_globals(lua_State *ls)
// We could simplify this a great deal by just using lex and yacc, but I
// don't know if we want to introduce them.
-struct lua_pat_op {
+struct lua_pat_op
+{
const char *token;
const char *luatok;
@@ -2167,7 +2175,8 @@ struct lua_pat_op {
bool posttext; // Is this followed by a pattern?
};
-static lua_pat_op pat_ops[] = {
+static lua_pat_op pat_ops[] =
+{
{ "<<", " ( ", false, true },
{ ">>", " ) ", true, false },
{ "!!", " not ", false, true },
diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc
index 814c49e68e..f9eafe4a7f 100644
--- a/crawl-ref/source/command.cc
+++ b/crawl-ref/source/command.cc
@@ -993,6 +993,71 @@ void list_commands(bool wizard)
show_keyhelp_menu(cols.formatted_lines(), true);
}
+void list_tutorial_help()
+{
+ // 2 columns, split at column 40.
+ column_composer cols(2, 41);
+ // Page size is number of lines - one line for --more-- prompt.
+ cols.set_pagesize(get_number_of_lines());
+
+ cols.add_formatted(
+ 0,
+ "<h>Item types (and common commands)\n"
+ "<cyan>)</cyan> : hand weapons (<w>w</w>ield)\n"
+ "<brown>(</brown> : missiles (<w>t</w>hrow or <w>f</w>ire)\n"
+ "<cyan>[</cyan> : armour (<w>W</w>ear and <w>T</w>ake off)\n"
+ "<brown>%</brown> : food and corpses (<w>e</w>at and <w>D</w>issect)\n"
+ "<w>?</w> : scrolls (<w>r</w>ead)\n"
+ "<magenta>!</magenta> : potions (<w>q</w>uaff)\n"
+ "<blue>=</blue> : rings (<w>P</w>ut on and <w>R</w>emove)\n"
+ "<red>\"</red> : amulets (<w>P</w>ut on and <w>R</w>emove)\n"
+ "<darkgrey>/</darkgrey> : wands (<w>z</w>ap)\n"
+ // is it possible to replace that with e.g. Options.char_table[DNGN_ITEM_BOOK]
+ "<lightcyan>+</lightcyan>, <lightcyan>:</lightcyan> : books (<w>r</w>ead, <w>M</w>emorise and <w>Z</w>ap)\n"
+ "<brown>\\</brown>, <brown>|</brown> : staves, rods (<w>w</w>ield and <w>E</w>voke)\n"
+ "\n"
+ "<h>Movement and attacking\n"
+ "Use the <w>numpad</w> for movement (try both\n"
+ "Numlock on and off). You can also use\n"
+ " <w>hjkl</w> : left, down, up, right and\n"
+ " <w>yubn</w> : diagonal movement.\n"
+ "Walking into a monster will attack it\n"
+ "with the wielded weapon or barehanded.\n"
+ "For ranged attacks use either\n"
+ "<w>f</w> to launch missiles (like arrows)\n"
+ "<w>t</w> to throw items by hand (like darts)\n"
+ "<w>Z</w> to cast spells (<w>Z?</w> lists spells).\n",
+ true, true, cmdhelp_textfilter);
+
+ cols.add_formatted(
+ 1,
+ "<h>Additional important commands\n"
+ "<w>S</w> : Save the game and exit\n"
+ "<w>s</w> : search for one turn (also <w>.</w> and <w>Del</w>)\n"
+ "<w>5</w> : rest full/search longer (<w>Shift-Num 5</w>)\n"
+ "<w>x</w> : examine surroundings\n"
+ "<w>v</w> : examine object in inventory\n"
+ "<w>i</w> : list inventory\n"
+ "<w>g</w> : pick up item from ground (also <w>,</w>)\n"
+ "<w>d</w> : drop item\n"
+ "<w>X</w> : show map of the whole level\n"
+ "<w><<</w> or <w>></w> : ascend/descend the stairs\n"
+ "<w>Ctrl-P</w> : show previous messages\n"
+ "\n"
+ "<h>Targeting (for spells and missiles)\n"
+ "Use <w>+</w> (or <w>=</w>) and <w>-</w> to cycle between\n"
+ "hostile monsters. <w>Enter</w> or <w>.</w> or <w>Del</w>\n"
+ "all fire at the selected target.\n"
+ "If the previous target is still alive\n"
+ "and in sight, one of <w>f</w>, <w>p</w>, <w>t</w> fires\n"
+ "at it again (without selecting anything).\n"
+ "Any movement key fires straight away in\n"
+ "the chosen direction.\n",
+ true, true, cmdhelp_textfilter,40);
+
+ show_keyhelp_menu(cols.formatted_lines(), false);
+}
+
static void list_wizard_commands()
{
const char *line;
diff --git a/crawl-ref/source/command.h b/crawl-ref/source/command.h
index f33e79954a..37ed7353c3 100644
--- a/crawl-ref/source/command.h
+++ b/crawl-ref/source/command.h
@@ -62,6 +62,7 @@ void swap_inv_slots(int slot1, int slot2, bool verbose);
void show_levelmap_help();
void show_targeting_help();
void list_commands(bool wizard);
+void list_tutorial_help(void);
// Actually defined in acr.cc; we may want to move this to command.cc
void process_command(command_type cmd);
diff --git a/crawl-ref/source/delay.cc b/crawl-ref/source/delay.cc
index 2279e4fb68..fdc04f3360 100644
--- a/crawl-ref/source/delay.cc
+++ b/crawl-ref/source/delay.cc
@@ -38,6 +38,7 @@
#include "spl-util.h"
#include "stuff.h"
#include "travel.h"
+#include "tutorial.h"
extern std::vector<SelItem> items_for_multidrop;
@@ -751,8 +752,10 @@ static void armour_wear_effects(const int item_slot)
if (is_random_artefact( arm ))
use_randart( item_slot );
- if (item_cursed( arm ))
+ if (item_cursed( arm )) {
mpr( "Oops, that feels deathly cold." );
+ learned_something_new(TUT_YOU_CURSED);
+ }
if (eq_slot == EQ_SHIELD)
warn_shield_penalties();
diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index 7aac2b04b5..ba8baac296 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -56,12 +56,14 @@
#define MAX_PIT_MONSTERS 10
-struct pit_mons_def {
+struct pit_mons_def
+{
int type;
int rare;
};
-struct spec_t {
+struct spec_t
+{
bool created;
bool hooked_up;
int x1;
@@ -3838,7 +3840,8 @@ static int builder_by_type(int level_number, char level_type)
if (which_demon >= 0)
{
- const char *pandemon_level_names[] = {
+ const char *pandemon_level_names[] =
+ {
"mnoleg", "lom_lobon", "cerebov", "gloorx_vloq"
};
you.unique_creatures[40 + which_demon] = 1;
@@ -3896,6 +3899,9 @@ static int random_map_for_dlevel(int level_number, bool wantmini = false)
vault = random_map_for_tag("entry", wantmini);
}
+ if (Options.tutorial_left)
+ vault = -1;
+
return (vault);
}
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 15fb845e5c..4819753756 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -1739,6 +1739,7 @@ enum msg_channel_type
MSGCH_FLOOR_ITEMS, // like equipment, but lists of floor items
MSGCH_MULTITURN_ACTION, // delayed action messages
MSGCH_DIAGNOSTICS, // various diagnostic messages
+ MSGCH_TUTORIAL, // messages for tutorial
NUM_MESSAGE_CHANNELS // always last
};
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index 3d90aeb275..8db5af8ee5 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -996,6 +996,22 @@ public:
int prev_book;
bool prev_randpick;
+ ///////////////////////////////////////////////////////////////////////
+ // tutorial
+ FixedVector<bool, 45> tutorial_events;
+// bool tut_made_note;
+ bool tut_explored;
+ bool tut_stashes;
+ bool tut_travel;
+ unsigned int tut_spell_counter;
+ unsigned int tut_throw_counter;
+ unsigned int tut_berserk_counter;
+ unsigned tut_last_healed;
+
+ bool tut_just_triggered;
+ unsigned int tutorial_type;
+ unsigned int tutorial_left;
+
public:
// Convenience accessors for the second-class options in named_options.
int o_int(const char *name, int def = 0) const;
diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc
index 6f2f3ad4e7..0c41990f30 100644
--- a/crawl-ref/source/fight.cc
+++ b/crawl-ref/source/fight.cc
@@ -69,6 +69,7 @@
#include "spells4.h"
#include "spl-cast.h"
#include "stuff.h"
+#include "tutorial.h"
#include "view.h"
#define HIT_WEAK 7
@@ -130,7 +131,8 @@ int effective_stat_bonus( int wepType )
// returns random2(x) is random_factor is true, otherwise
// the mean.
-static int maybe_random2( int x, bool random_factor ) {
+static int maybe_random2( int x, bool random_factor )
+{
if ( random_factor )
return random2(x);
else
@@ -139,8 +141,8 @@ static int maybe_random2( int x, bool random_factor ) {
// Returns the to-hit for your extra unarmed.attacks.
// DOES NOT do the final roll (i.e., random2(your_to_hit)).
-static int calc_your_to_hit_unarmed() {
-
+static int calc_your_to_hit_unarmed()
+{
int your_to_hit;
your_to_hit = 13 + you.dex / 2 + you.skills[SK_UNARMED_COMBAT] / 2
@@ -162,14 +164,16 @@ static int calc_your_to_hit_unarmed() {
int calc_your_to_hit( int heavy_armour,
bool hand_and_a_half_bonus,
bool water_attack,
- bool random_factor ) {
+ bool random_factor )
+{
const int weapon = you.equip[EQ_WEAPON];
const bool ur_armed = (weapon != -1); // compacts code a bit {dlb}
int wpn_skill = SK_UNARMED_COMBAT;
- if (weapon != -1) {
+ if (weapon != -1)
+ {
wpn_skill = weapon_skill( you.inv[weapon].base_type,
you.inv[weapon].sub_type );
}
@@ -193,13 +197,16 @@ int calc_your_to_hit( int heavy_armour,
your_to_hit += maybe_random2(1 + you.skills[SK_FIGHTING], random_factor);
// weapon skill contribution
- if (ur_armed) {
- if (wpn_skill != SK_FIGHTING) {
+ if (ur_armed)
+ {
+ if (wpn_skill != SK_FIGHTING)
+ {
your_to_hit += maybe_random2(you.skills[wpn_skill] + 1,
random_factor);
}
}
- else { // ...you must be unarmed
+ else
+ { // ...you must be unarmed
your_to_hit +=
(you.species == SP_TROLL || you.species == SP_GHOUL) ? 4 : 2;
@@ -261,12 +268,14 @@ int calc_your_to_hit( int heavy_armour,
you.sure_blade / 2);
// other stuff
- if (!ur_armed) {
+ if (!ur_armed)
+ {
if ( you.confusing_touch )
// just trying to touch is easier that trying to damage
your_to_hit += maybe_random2(you.dex, random_factor);
- switch ( you.attribute[ATTR_TRANSFORMATION] ) {
+ switch ( you.attribute[ATTR_TRANSFORMATION] )
+ {
case TRAN_NONE:
break;
case TRAN_SPIDER:
@@ -301,8 +310,8 @@ int calc_your_to_hit( int heavy_armour,
// Calculates your heavy armour penalty. If random_factor is true,
// be stochastic; if false, deterministic (e.g. for chardumps.)
-int calc_heavy_armour_penalty( bool random_factor ) {
-
+int calc_heavy_armour_penalty( bool random_factor )
+{
const bool ur_armed = (you.equip[EQ_WEAPON] != -1);
int heavy_armour = 0;
@@ -350,12 +359,15 @@ int calc_heavy_armour_penalty( bool random_factor ) {
// ??? what is the reasoning behind this ??? {dlb}
// My guess is that its supposed to encourage monk-style play -- bwr
- if (!ur_armed) {
- if ( random_factor ) {
+ if (!ur_armed)
+ {
+ if ( random_factor )
+ {
heavy_armour *= (coinflip() ? 3 : 2);
}
// (2+3)/2
- else {
+ else
+ {
heavy_armour *= 5;
heavy_armour /= 2;
}
@@ -401,6 +413,10 @@ bool you_attack(int monster_attacked, bool unarmed_attacks)
// We're trying to hit a monster, break out of travel/explore now.
interrupt_activity(AI_HIT_MONSTER, defender);
+ if (ur_armed && (you.inv[weapon].base_type != OBJ_WEAPONS
+ || you.inv[weapon].sub_type == WPN_BOW || you.inv[weapon].sub_type == WPN_SLING))
+ learned_something_new(TUT_WIELD_WEAPON);
+
if (ur_armed && you.inv[weapon].base_type == OBJ_WEAPONS
&& is_random_artefact( you.inv[weapon] ))
{
@@ -2567,9 +2583,10 @@ void monster_attack(int monster_attacking)
if (you.paralysis > 0)
mpr("You still can't move!", MSGCH_WARN);
else
- mpr("You suddenly lose the ability to move!", MSGCH_WARN);
- if ( you.paralysis == 0 || mclas == MONS_RED_WASP )
- you.paralysis += 1 + random2(3);
+ mpr("You suddenly lose the ability to move!",
+ MSGCH_WARN);
+ if ( you.paralysis == 0 || mclas == MONS_RED_WASP )
+ you.paralysis += 1 + random2(3);
}
break;
@@ -2743,15 +2760,16 @@ void monster_attack(int monster_attacking)
}
break;
- case MONS_MOTH_OF_WRATH:
- if (one_chance_in(3)) {
- simple_monster_message(attacker, " infuriates you!");
- go_berserk(false);
- }
- break;
+ case MONS_MOTH_OF_WRATH:
+ if (one_chance_in(3))
+ {
+ simple_monster_message(attacker, " infuriates you!");
+ go_berserk(false);
+ }
+ break;
- default:
- break;
+ default:
+ break;
} // end of switch for special attacks.
/* use break for level drain, maybe with beam variables,
because so many creatures use it. */
diff --git a/crawl-ref/source/files.cc b/crawl-ref/source/files.cc
index b8b5aa518d..7ea4991c0d 100644
--- a/crawl-ref/source/files.cc
+++ b/crawl-ref/source/files.cc
@@ -76,6 +76,7 @@
#include "stuff.h"
#include "tags.h"
#include "travel.h"
+#include "tutorial.h"
#ifdef SHARED_FILES_CHMOD_PRIVATE
#define DO_CHMOD_PRIVATE(x) chmod( (x), SHARED_FILES_CHMOD_PRIVATE )
@@ -1202,6 +1203,16 @@ void save_game(bool leave_game)
DO_CHMOD_PRIVATE(notesFile.c_str());
}
+ /* tutorial */
+
+ std::string tutorFile = get_savedir_filename(you.your_name, "", "tut");
+ FILE *tutorf = fopen(tutorFile.c_str(), "wb");
+ if (tutorf) {
+ save_tutorial(tutorf);
+ fclose(tutorf);
+ DO_CHMOD_PRIVATE(tutorFile.c_str());
+ }
+
std::string charFile = get_savedir_filename(you.your_name, "", "sav");
FILE *charf = fopen(charFile.c_str(), "wb");
if (!charf)
@@ -1399,6 +1410,15 @@ void restore_game(void)
load_notes(notesf);
fclose(notesf);
}
+
+ /* tutorial */
+
+ std::string tutorFile = get_savedir_filename(you.your_name, "", "tut");
+ FILE *tutorf = fopen(tutorFile.c_str(), "rb");
+ if (tutorf) {
+ load_tutorial(tutorf);
+ fclose(tutorf);
+ }
}
static bool determine_version( FILE *restoreFile,
diff --git a/crawl-ref/source/food.cc b/crawl-ref/source/food.cc
index e042123eb0..0ac4a3801c 100644
--- a/crawl-ref/source/food.cc
+++ b/crawl-ref/source/food.cc
@@ -44,6 +44,7 @@
#include "skills2.h"
#include "spells2.h"
#include "stuff.h"
+#include "tutorial.h"
static int determine_chunk_effect(int which_chunk_type, bool rotten_chunk);
static void eat_chunk( int chunk_effect );
@@ -506,9 +507,11 @@ static bool food_change(bool suppress_message)
{
case HS_STARVING:
mpr("You are starving!", MSGCH_FOOD);
+ learned_something_new(TUT_YOU_STARVING);
break;
case HS_HUNGRY:
mpr("You are feeling hungry.", MSGCH_FOOD);
+ learned_something_new(TUT_YOU_HUNGRY);
break;
default:
break;
diff --git a/crawl-ref/source/initfile.h b/crawl-ref/source/initfile.h
index f5c05972e0..ae76fda4bc 100644
--- a/crawl-ref/source/initfile.h
+++ b/crawl-ref/source/initfile.h
@@ -57,22 +57,26 @@ std::string channel_to_str(int ch);
int str_to_channel(const std::string &);
-class InitLineInput {
+class InitLineInput
+{
public:
virtual ~InitLineInput() { }
virtual bool eof() = 0;
virtual std::string getline() = 0;
};
-class FileLineInput : public InitLineInput {
+class FileLineInput : public InitLineInput
+{
public:
FileLineInput(FILE *f) : file(f) { }
- bool eof() {
+ bool eof()
+ {
return !file || feof(file);
}
- std::string getline() {
+ std::string getline()
+ {
char s[256] = "";
if (!eof())
fgets(s, sizeof s, file);
@@ -82,15 +86,18 @@ private:
FILE *file;
};
-class StringLineInput : public InitLineInput {
+class StringLineInput : public InitLineInput
+{
public:
StringLineInput(const std::string &s) : str(s), pos(0) { }
- bool eof() {
+ bool eof()
+ {
return pos >= str.length();
}
- std::string getline() {
+ std::string getline()
+ {
if (eof())
return "";
std::string::size_type newl = str.find("\n", pos);
diff --git a/crawl-ref/source/invent.h b/crawl-ref/source/invent.h
index 9ce3fbc5ac..5696a2ea8a 100644
--- a/crawl-ref/source/invent.h
+++ b/crawl-ref/source/invent.h
@@ -69,7 +69,8 @@ private:
void add_class_hotkeys(const item_def &i);
};
-class InvShowPrices {
+class InvShowPrices
+{
public:
InvShowPrices(bool doshow = true);
~InvShowPrices();
diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc
index f6cb476c34..1f65e6233d 100644
--- a/crawl-ref/source/item_use.cc
+++ b/crawl-ref/source/item_use.cc
@@ -66,6 +66,7 @@
#include "spl-cast.h"
#include "stuff.h"
#include "transfor.h"
+#include "tutorial.h"
#include "view.h"
bool drink_fountain(void);
@@ -599,8 +600,8 @@ bool armour_prompt( const std::string & mesg, int *index, operation_types oper)
else
{
slot = prompt_invent_item( mesg.c_str(), MT_INVSELECT, OBJ_ARMOUR,
- true, true, true, 0, NULL,
- oper );
+ true, true, true, 0, NULL,
+ oper );
if (slot != PROMPT_ABORT)
{
@@ -1020,7 +1021,7 @@ void throw_anything(void)
throw_slot = prompt_invent_item( "Throw which item? (* to show all)",
MT_INVSELECT,
OBJ_MISSILES, true, true, true, 0, NULL,
- OPER_THROW );
+ OPER_THROW );
if (throw_slot == PROMPT_ABORT)
{
canned_msg( MSG_OK );
@@ -2054,8 +2055,11 @@ void jewellery_wear_effects(item_def &item)
set_ident_flags( item, ISFLAG_EQ_JEWELLERY_MASK );
if (item_cursed( item ))
+ {
mprf("Oops, that %s feels deathly cold.",
jewellery_is_amulet(item)? "amulet" : "ring");
+ learned_something_new(TUT_YOU_CURSED);
+ }
// cursed or not, we know that since we've put the ring on
set_ident_flags( item, ISFLAG_KNOW_CURSE );
@@ -2373,8 +2377,8 @@ bool remove_ring(int slot, bool announce)
int equipn =
slot == -1? prompt_invent_item( "Remove which piece of jewellery?",
MT_INVSELECT,
- OBJ_JEWELLERY, true, true, true,
- 0, NULL, OPER_REMOVE)
+ OBJ_JEWELLERY, true, true, true,
+ 0, NULL, OPER_REMOVE)
: slot;
if (equipn == PROMPT_ABORT)
@@ -2469,8 +2473,8 @@ void zap_wand(void)
item_slot = prompt_invent_item( "Zap which item?",
MT_INVSELECT,
OBJ_WANDS,
- true, true, true, 0, NULL,
- OPER_ZAP );
+ true, true, true, 0, NULL,
+ OPER_ZAP );
if (item_slot == PROMPT_ABORT)
{
canned_msg( MSG_OK );
@@ -2488,22 +2492,22 @@ void zap_wand(void)
if (you.equip[EQ_WEAPON] == item_slot)
you.wield_change = true;
- if ( you.inv[item_slot].plus < 1 ) {
- // it's an empty wand, inscribe it that way
+ if ( you.inv[item_slot].plus < 1 )
+ {
+ // it's an empty wand, inscribe it that way
canned_msg(MSG_NOTHING_HAPPENS);
you.turn_is_over = true;
- if ( !item_ident(you.inv[item_slot], ISFLAG_KNOW_PLUSES) ) {
-
- if ( you.inv[item_slot].inscription.find("empty") ==
- std::string::npos ) {
-
- if ( !you.inv[item_slot].inscription.empty() )
- you.inv[item_slot].inscription += ' ';
- you.inv[item_slot].inscription += "empty";
-
- }
- }
- return;
+ if ( !item_ident(you.inv[item_slot], ISFLAG_KNOW_PLUSES) )
+ {
+ if ( you.inv[item_slot].inscription.find("empty") ==
+ std::string::npos )
+ {
+ if ( !you.inv[item_slot].inscription.empty() )
+ you.inv[item_slot].inscription += ' ';
+ you.inv[item_slot].inscription += "empty";
+ }
+ }
+ return;
}
if (item_type_known( you.inv[item_slot] ))
@@ -2620,8 +2624,8 @@ void inscribe_item()
char buf[79];
if (inv_count() < 1)
{
- mpr("You don't have anything to inscribe.");
- return;
+ mpr("You don't have anything to inscribe.");
+ return;
}
item_slot = prompt_invent_item(
"Inscribe which item? ",
@@ -2669,8 +2673,8 @@ void drink(void)
item_slot = prompt_invent_item( "Drink which item?",
MT_INVSELECT, OBJ_POTIONS,
- true, true, true, 0, NULL,
- OPER_QUAFF );
+ true, true, true, 0, NULL,
+ OPER_QUAFF );
if (item_slot == PROMPT_ABORT)
{
canned_msg( MSG_OK );
@@ -3243,8 +3247,8 @@ void read_scroll(void)
case SCR_PAPER:
// remember paper scrolls handled as special case above, too:
mpr("This scroll appears to be blank.");
- if (you.mutation[MUT_BLURRY_VISION] == 3)
- id_the_scroll = false;
+ if (you.mutation[MUT_BLURRY_VISION] == 3)
+ id_the_scroll = false;
break;
case SCR_RANDOM_USELESSNESS:
@@ -3324,7 +3328,7 @@ void read_scroll(void)
case SCR_IMMOLATION:
mpr("The scroll explodes in your hands!");
- // we do this here to prevent it from blowing itself up
+ // we do this here to prevent it from blowing itself up
dec_inv_item_quantity( item_slot, 1 );
beam.type = SYM_BURST;
@@ -3345,7 +3349,8 @@ void read_scroll(void)
break;
case SCR_IDENTIFY:
- if ( get_ident_type( OBJ_SCROLLS, scroll_type ) != ID_KNOWN_TYPE ) {
+ if ( get_ident_type( OBJ_SCROLLS, scroll_type ) != ID_KNOWN_TYPE )
+ {
mpr("This is a scroll of identify!");
more();
}
@@ -3519,8 +3524,8 @@ void examine_object(void)
{
int item_slot = prompt_invent_item( "Examine which item?",
MT_INVSELECT, -1,
- true, true, true, 0, NULL,
- OPER_EXAMINE );
+ true, true, true, 0, NULL,
+ OPER_EXAMINE );
if (item_slot == PROMPT_ABORT)
{
canned_msg( MSG_OK );
diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc
index a9093da5fa..22bde71fe0 100644
--- a/crawl-ref/source/items.cc
+++ b/crawl-ref/source/items.cc
@@ -59,6 +59,7 @@
#include "spl-cast.h"
#include "stuff.h"
#include "stash.h"
+#include "tutorial.h"
static bool invisible_to_player( const item_def& item );
static void item_list_on_square( std::vector<const item_def*>& items,
@@ -826,8 +827,10 @@ void item_check(char keyin)
}
}
- if (counter_max > 5 && keyin != ';')
+ if (counter_max > 5 && keyin != ';') {
mpr("There are several objects here.");
+ learned_something_new(TUT_MULTI_PICKUP);
+ }
}
void show_items()
@@ -873,11 +876,13 @@ void pickup_menu(int item_link)
if (result == 0)
{
mpr("You can't carry that much weight.");
+ learned_something_new(TUT_HEAVY_LOAD);
return;
}
else if (result == -1)
{
mpr("You can't carry that many items.");
+ learned_something_new(TUT_HEAVY_LOAD);
return;
}
break;
@@ -1142,11 +1147,13 @@ bool pickup_single_item(int link, int qty)
if (num == -1)
{
mpr("You can't carry that many items.");
+ learned_something_new(TUT_HEAVY_LOAD);
return (false);
}
else if (num == 0)
{
mpr("You can't carry that much weight.");
+ learned_something_new(TUT_HEAVY_LOAD);
return (false);
}
@@ -1179,7 +1186,8 @@ void pickup()
{
if (inv_count() >= ENDOFPACK)
{
- mpr("There is a portable altar here, but you can't carry anything else.");
+ mpr("There is a portable altar here, "
+ "but you can't carry anything else.");
return;
}
@@ -1225,8 +1233,8 @@ void pickup()
}
else if (mitm[o].link == NON_ITEM) // just one item?
{
- // deliberately allowing the player to pick up
- // a killed item here
+ // deliberately allowing the player to pick up
+ // a killed item here
pickup_single_item(o, mitm[o].quantity);
}
else if (Options.pickup_mode != -1 &&
@@ -1243,9 +1251,10 @@ void pickup()
// must save this because pickup can destroy the item
next = mitm[o].link;
- if ( num_nonsquelched && invisible_to_player(mitm[o]) ) {
+ if ( num_nonsquelched && invisible_to_player(mitm[o]) )
+ {
o = next;
- continue;
+ continue;
}
if (keyin != 'a')
@@ -1542,7 +1551,6 @@ int move_item_to_player( int obj, int quant_got, bool quiet )
item.link = freeslot;
autoinscribe_item( item );
-
origin_freeze(item, you.x_pos, you.y_pos);
@@ -1555,7 +1563,14 @@ int move_item_to_player( int obj, int quant_got, bool quiet )
in_name( freeslot, DESC_INVENTORY, info );
mpr(info);
}
-
+
+ if (Options.tutorial_left)
+ {
+ taken_new_item(item.base_type);
+ if (is_random_artefact(you.inv[freeslot]))
+ learned_something_new(TUT_SEEN_RANDART);
+ }
+
if (item.base_type == OBJ_ORBS
&& you.char_direction == DIR_DESCENDING)
{
@@ -2856,6 +2871,7 @@ void handle_time( long time_delta )
MSGCH_ROTTEN_MEAT );
break;
}
+ learned_something_new(TUT_ROTTEN_FOOD);
}
// exercise armour *xor* stealth skill: {dlb}
diff --git a/crawl-ref/source/libutil.cc b/crawl-ref/source/libutil.cc
index 0a7caaaaa4..d380b92419 100644
--- a/crawl-ref/source/libutil.cc
+++ b/crawl-ref/source/libutil.cc
@@ -88,16 +88,20 @@ int unmangle_direction_keys(int keyin, int km)
/* FIXME haranp - hackiness */
const char DOSidiocy[10] = { "OPQKSMGHI" };
const char DOSunidiocy[10] = { "bjnh.lyku" };
- const int DOScontrolidiocy[9] = {
+ const int DOScontrolidiocy[9] =
+ {
117, 145, 118, 115, 76, 116, 119, 141, 132
};
keyin = getchm(keymap);
- for (int j = 0; j < 9; ++j ) {
- if (keyin == DOSidiocy[j]) {
+ for (int j = 0; j < 9; ++j )
+ {
+ if (keyin == DOSidiocy[j])
+ {
keyin = DOSunidiocy[j];
break;
}
- if (keyin == DOScontrolidiocy[j]) {
+ if (keyin == DOScontrolidiocy[j])
+ {
keyin = CONTROL(toupper(DOSunidiocy[j]));
break;
}
diff --git a/crawl-ref/source/libw32c.cc b/crawl-ref/source/libw32c.cc
index dea7c01fb9..f07f2ba00c 100644
--- a/crawl-ref/source/libw32c.cc
+++ b/crawl-ref/source/libw32c.cc
@@ -162,7 +162,8 @@ static void addcall(int i, LARGE_INTEGER &tm1, LARGE_INTEGER &tm2)
#define CLOCKOUT(x) {QueryPerformanceCounter(&t2); \
addcall((x), t1, t2);}
-static char *descrip[] = {
+static char *descrip[] =
+{
"bflush:WriteConsoleOutput",
"_setCursorType:SetConsoleCursorInfo",
"gotoxy:SetConsoleCursorPosition",
@@ -314,12 +315,14 @@ void setStringInput(bool value)
outmodes = 0;
}
- if ( SetConsoleMode( inbuf, inmodes ) == 0) {
+ if ( SetConsoleMode( inbuf, inmodes ) == 0)
+ {
fputs("Error initialising console input mode.", stderr);
exit(0);
}
- if ( SetConsoleMode( outbuf, outmodes ) == 0) {
+ if ( SetConsoleMode( outbuf, outmodes ) == 0)
+ {
fputs("Error initialising console output mode.", stderr);
exit(0);
}
@@ -354,7 +357,8 @@ void init_libw32c(void)
inbuf = GetStdHandle( STD_INPUT_HANDLE );
outbuf = GetStdHandle( STD_OUTPUT_HANDLE );
- if (inbuf == INVALID_HANDLE_VALUE || outbuf == INVALID_HANDLE_VALUE) {
+ if (inbuf == INVALID_HANDLE_VALUE || outbuf == INVALID_HANDLE_VALUE)
+ {
fputs("Could not initialise libw32c console support.", stderr);
exit(0);
}
@@ -370,7 +374,8 @@ void init_libw32c(void)
// by default, set string input to false: use char-input only
setStringInput( false );
- if (SetConsoleMode( outbuf, 0 ) == 0) {
+ if (SetConsoleMode( outbuf, 0 ) == 0)
+ {
fputs("Error initialising console output mode.", stderr);
exit(0);
}
@@ -715,7 +720,8 @@ static int vk_tr[4][VKEY_MAPPINGS] = // virtual key, unmodified, shifted, contro
{ CK_CTRL_END, CK_CTRL_DOWN, CK_CTRL_PGDN, CK_CTRL_LEFT, CK_CTRL_CLEAR, CK_CTRL_RIGHT, CK_CTRL_HOME, CK_CTRL_UP, CK_CTRL_PGUP, CK_CTRL_INSERT },
};
-static int ck_tr[] = {
+static int ck_tr[] =
+{
'k', 'j', 'h', 'l', '0', 'y', 'b', '.', 'u', 'n',
// 'b', 'j', 'n', 'h', '.', 'l', 'y', 'k', 'u' ,
'8', '2', '4', '6', '0', '7', '1', '5', '9', '3',
@@ -724,7 +730,8 @@ static int ck_tr[] = {
// 2, 10, 14, 8, 0, 12, 25, 11, 21 ,
};
-int key_to_command( int keyin ) {
+int key_to_command( int keyin )
+{
if (keyin >= CK_UP && keyin <= CK_CTRL_PGDN)
return ck_tr[ keyin - CK_UP ];
@@ -772,7 +779,8 @@ int vk_translate( WORD VirtCode, CHAR c, DWORD cKeys)
if (VirtCode == vk_tr[0][mkey]) break;
// step 4 - just return the damn key.
- if (mkey == VKEY_MAPPINGS) {
+ if (mkey == VKEY_MAPPINGS)
+ {
if (c)
return c;
@@ -884,9 +892,11 @@ int kbhit()
INPUT_RECORD ir[10];
DWORD read_count = 0;
PeekConsoleInput(inbuf, ir, sizeof ir / sizeof(ir[0]), &read_count);
- if (read_count > 0) {
+ if (read_count > 0)
+ {
for (unsigned i = 0; i < read_count; ++i)
- if (ir[i].EventType == KEY_EVENT) {
+ if (ir[i].EventType == KEY_EVENT)
+ {
KEY_EVENT_RECORD *kr;
kr = &(ir[i].Event.KeyEvent);
diff --git a/crawl-ref/source/makefile.obj b/crawl-ref/source/makefile.obj
index da61c5d0d2..89afdcb15b 100644
--- a/crawl-ref/source/makefile.obj
+++ b/crawl-ref/source/makefile.obj
@@ -67,6 +67,7 @@ stuff.o \
tags.o \
transfor.o \
travel.o \
+tutorial.o \
view.o \
Kills.o \
mt19937ar.o \
diff --git a/crawl-ref/source/message.cc b/crawl-ref/source/message.cc
index 9040a2d2ea..5e3d72c639 100644
--- a/crawl-ref/source/message.cc
+++ b/crawl-ref/source/message.cc
@@ -36,6 +36,7 @@
#include "notes.h"
#include "stash.h"
#include "religion.h"
+#include "tutorial.h"
// circular buffer for keeping past messages
message_item Store_Message[ NUM_STORED_MESSAGES ]; // buffer of old messages
@@ -136,7 +137,7 @@ int channel_to_colour( int channel, int param )
switch (channel)
{
case MSGCH_GOD:
- case MSGCH_PRAY:
+ case MSGCH_PRAY:
ret = (Options.channels[ channel ] == MSGCOL_DEFAULT)
? god_colour( param )
: god_message_altar_colour( param );
@@ -170,6 +171,10 @@ int channel_to_colour( int channel, int param )
ret = WHITE;
break;
+ case MSGCH_TUTORIAL:
+ ret = MAGENTA;
+ break;
+
case MSGCH_MONSTER_SPELL:
case MSGCH_MONSTER_ENCHANT:
ret = LIGHTMAGENTA;
@@ -316,8 +321,10 @@ static void base_mpr(const char *inf, int channel, int param)
std::string imsg = inf;
- for (unsigned i = 0; i < Options.note_messages.size(); ++i) {
- if (Options.note_messages[i].matches(imsg)) {
+ for (unsigned i = 0; i < Options.note_messages.size(); ++i)
+ {
+ if (Options.note_messages[i].matches(imsg))
+ {
take_note(Note(NOTE_MESSAGE, channel, param, inf));
break;
}
@@ -414,8 +421,10 @@ void formatted_mpr(const formatted_string& fs, int channel, int param)
const std::string imsg = fs.tostring();
- for (unsigned i = 0; i < Options.note_messages.size(); ++i) {
- if (Options.note_messages[i].matches(imsg)) {
+ for (unsigned i = 0; i < Options.note_messages.size(); ++i)
+ {
+ if (Options.note_messages[i].matches(imsg))
+ {
take_note(Note(NOTE_MESSAGE, channel, param, imsg.c_str()));
break;
}
@@ -544,13 +553,18 @@ void more(void)
{
char keypress = 0;
- message_out(get_message_window_height() - 1,
- LIGHTGREY, "--more--", 2, false);
+ if (Options.tutorial_left)
+ message_out(get_message_window_height() - 1,
+ LIGHTGREY,
+ "--more-- "
+ "Press Ctrl-P to reread old messages",
+ 2, false);
+ else
+ message_out(get_message_window_height() - 1,
+ LIGHTGREY, "--more--", 2, false);
do
- {
keypress = getch();
- }
while (keypress != ' ' && keypress != '\r' && keypress != '\n');
mesclr( !Options.delay_message_clear &&
@@ -650,6 +664,14 @@ void replay_messages(void)
#if DEBUG_DIAGNOSTICS
cprintf( "%d: %s", line, Store_Message[ line ].text.c_str() );
#else
+ /* TODO: allow colour changes in previous messages, as well
+ if (Store_Message[ line ].channel == MSGCH_TUTORIAL)
+ {
+ formatted_string help = formatted_string::parse_string(Store_Message[ line ].text);
+ help.display();
+ }
+ else
+ */
cprintf( "%s", Store_Message[ line ].text.c_str() );
#endif
diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc
index 3c9f0755fa..b3dd260423 100644
--- a/crawl-ref/source/misc.cc
+++ b/crawl-ref/source/misc.cc
@@ -61,6 +61,7 @@
#include "stuff.h"
#include "transfor.h"
#include "travel.h"
+#include "tutorial.h"
#include "view.h"
extern FixedVector<char, 10> Visible_Statue; // defined in acr.cc
@@ -1370,6 +1371,7 @@ void handle_traps(char trt, int i, bool trap_known)
75 + random2(100), 3, "a Zot trap" );
break;
}
+ learned_something_new(TUT_SEEN_TRAPS);
} // end handle_traps()
void disarm_trap( struct dist &disa )
@@ -1687,6 +1689,8 @@ bool go_berserk(bool intentional)
return false;
}
+ if (Options.tutorial_left)
+ Options.tut_berserk_counter++;
mpr("A red film seems to cover your vision as you go berserk!");
mpr("You feel yourself moving faster!");
mpr("You feel mighty!");
@@ -1918,13 +1922,15 @@ bool i_feel_safe(bool announce)
{
if (announce)
mons.push_back(mon);
- else
+ else {
+ tutorial_first_monster(*mon);
return false;
}
}
}
}
}
+ }
if (announce)
{
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index e6f9f17a24..dc56a2b137 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -53,6 +53,7 @@
#include "spells2.h"
#include "spells4.h"
#include "stuff.h"
+#include "tutorial.h"
#include "view.h"
#include "stash.h"
@@ -323,6 +324,8 @@ static void place_monster_corpse(const monsters *monster)
// Don't care if 'o' is changed, and it shouldn't be (corpses don't stack)
move_item_to_grid( &o, monster->x, monster->y );
+ if (you.hunger_state < HS_SATIATED)
+ learned_something_new(TUT_MAKE_CHUNKS);
} // end place_monster_corpse()
void monster_die(struct monsters *monster, char killer, int i)
@@ -446,6 +449,13 @@ void monster_die(struct monsters *monster, char killer, int i)
mpr("That felt strangely unrewarding.");
}
+ // killing triggers tutorial lesson
+ if (Options.tutorial_events[TUT_KILLED_MONSTER])
+ learned_something_new(TUT_KILLED_MONSTER);
+ else if (Options.tutorial_left && (you.religion == GOD_TROG || you.religion == GOD_OKAWARU || you.religion == GOD_MAKHLEB)
+ && !you.duration[DUR_PRAYER])
+ tutorial_prayer_reminder();
+
// Xom doesn't care who you killed:
if (you.religion == GOD_XOM
&& random2(70) <= 10 + monster->hit_dice)
diff --git a/crawl-ref/source/newgame.cc b/crawl-ref/source/newgame.cc
index 9b207ca2c6..a43f7ec5f9 100644
--- a/crawl-ref/source/newgame.cc
+++ b/crawl-ref/source/newgame.cc
@@ -87,6 +87,7 @@
#include "skills2.h"
#include "spl-util.h"
#include "stuff.h"
+#include "tutorial.h"
#include "version.h"
#include "view.h"
@@ -117,8 +118,6 @@ static void give_random_secondary_armour( int slot );
static bool give_wanderer_weapon( int slot, int wpn_skill );
static void create_wanderer(void);
static void give_items_skills(void);
-static bool choose_race(void);
-static bool choose_class(void);
static char letter_to_species(int keyn);
static char letter_to_class(int keyn);
@@ -326,7 +325,8 @@ static unsigned char random_potion_description()
{
int desc, nature, colour;
- do {
+ do
+ {
desc = random2( PDQ_NQUALS * PDC_NCOLOURS );
if (coinflip())
@@ -1423,68 +1423,69 @@ static void choose_book( item_def& book, int firstbook, int numbooks )
// fire books, CONJ_II and MINOR_MAGIC_II are both ice books
if ( Options.book && Options.book <= numbooks )
{
- book.sub_type = firstbook + Options.book - 1;
- ng_book = Options.book;
- return;
+ book.sub_type = firstbook + Options.book - 1;
+ ng_book = Options.book;
+ return;
}
if ( Options.prev_book > numbooks && Options.prev_book != SBT_RANDOM )
- Options.prev_book = SBT_NO_SELECTION;
+ Options.prev_book = SBT_NO_SELECTION;
if ( !Options.random_pick )
{
- textcolor( CYAN );
- cprintf(EOL " You have a choice of books:" EOL);
- textcolor( LIGHTGREY );
-
- for (int i=0; i < numbooks; ++i)
- {
- char buf[ITEMNAME_SIZE];
- book.sub_type = firstbook + i;
- item_name( book, DESC_PLAIN, buf );
- cprintf("%c - %s" EOL, 'a' + i, buf);
- }
-
- textcolor(BROWN);
- cprintf(EOL "? - Random" );
- if ( Options.prev_book != SBT_NO_SELECTION ) {
+ textcolor( CYAN );
+ cprintf(EOL " You have a choice of books:" EOL);
+ textcolor( LIGHTGREY );
+
+ for (int i=0; i < numbooks; ++i)
+ {
+ char buf[ITEMNAME_SIZE];
+ book.sub_type = firstbook + i;
+ item_name( book, DESC_PLAIN, buf );
+ cprintf("%c - %s" EOL, 'a' + i, buf);
+ }
+
+ textcolor(BROWN);
+ cprintf(EOL "? - Random" );
+ if ( Options.prev_book != SBT_NO_SELECTION )
+ {
cprintf("; Enter - %s",
- Options.prev_book == SBT_FIRE ? "Fire" :
- Options.prev_book == SBT_COLD ? "Cold" :
- Options.prev_book == SBT_SUMM ? "Summoning" :
+ Options.prev_book == SBT_FIRE ? "Fire" :
+ Options.prev_book == SBT_COLD ? "Cold" :
+ Options.prev_book == SBT_SUMM ? "Summoning" :
Options.prev_book == SBT_RANDOM ? "Random" :
- "Buggy Book");
- }
- cprintf(EOL);
+ "Buggy Book");
+ }
+ cprintf(EOL);
- do
- {
- textcolor( CYAN );
- cprintf(EOL "Which book? ");
- textcolor( LIGHTGREY );
-
- keyin = get_ch();
- } while (keyin != '?' &&
- ((keyin != '\r' && keyin != '\n') ||
- Options.prev_book == SBT_NO_SELECTION ) &&
- (keyin < 'a' || keyin > ('a' + numbooks)));
-
- if ( keyin == '\r' || keyin == '\n' )
- {
- if ( Options.prev_book == SBT_RANDOM )
- keyin = '?';
- else
- keyin = ('a' + Options.prev_book - 1);
- }
+ do
+ {
+ textcolor( CYAN );
+ cprintf(EOL "Which book? ");
+ textcolor( LIGHTGREY );
+
+ keyin = get_ch();
+ } while (keyin != '?' &&
+ ((keyin != '\r' && keyin != '\n') ||
+ Options.prev_book == SBT_NO_SELECTION ) &&
+ (keyin < 'a' || keyin > ('a' + numbooks)));
+
+ if ( keyin == '\r' || keyin == '\n' )
+ {
+ if ( Options.prev_book == SBT_RANDOM )
+ keyin = '?';
+ else
+ keyin = ('a' + Options.prev_book - 1);
+ }
}
if (Options.random_pick || Options.book == SBT_RANDOM || keyin == '?')
- ng_book = SBT_RANDOM;
+ ng_book = SBT_RANDOM;
else
- ng_book = keyin - 'a' + 1;
+ ng_book = keyin - 'a' + 1;
if ( Options.random_pick || keyin == '?' )
- keyin = random2(numbooks) + 'a';
+ keyin = random2(numbooks) + 'a';
book.sub_type = firstbook + keyin - 'a';
}
@@ -2595,12 +2596,12 @@ static void create_wanderer( void )
// Could only have learned spells in common schools...
const int school_list[5] =
{ SK_CONJURATIONS,
- SK_ENCHANTMENTS, SK_ENCHANTMENTS,
- SK_TRANSLOCATIONS, SK_NECROMANCY };
+ SK_ENCHANTMENTS, SK_ENCHANTMENTS,
+ SK_TRANSLOCATIONS, SK_NECROMANCY };
- //jmf: Two of those spells are gone due to their munchkinicity.
- // crush() and arc() are like having good melee capability.
- // Therefore giving them to "harder" class makes less-than-
+ //jmf: Two of those spells are gone due to their munchkinicity.
+ // crush() and arc() are like having good melee capability.
+ // Therefore giving them to "harder" class makes less-than-
// zero sense, and they're now gone.
const int spell_list[5] =
{ SPELL_MAGIC_DART,
@@ -2910,9 +2911,11 @@ spec_query:
textcolor( WHITE );
cprintf("You must be new here!");
}
+ cprintf(" (Press T to enter a tutorial.)");
cprintf(EOL EOL);
textcolor( CYAN );
cprintf("You can be:");
+ cprintf(" (Press ? for more information)");
cprintf(EOL EOL);
textcolor( LIGHTGREY );
@@ -2955,12 +2958,11 @@ spec_query:
"SPACE - Choose class first; * - Random Species; "
"! - Random Character"
EOL
- "? - Help; X - Quit"
+ "X - Quit"
EOL);
else
cprintf(EOL
- "? - Help * - Random; "
- "Bksp - Back to class selection; X - Quit"
+ "* - Random; Bksp - Back to class selection; X - Quit"
EOL);
if (Options.prev_race)
@@ -3037,6 +3039,10 @@ spec_query:
ng_random = true;
return false;
}
+ else if (keyn == 'T')
+ {
+ return !pick_tutorial();
+ }
if (!(you.species = letter_to_species(keyn)))
{
@@ -3119,10 +3125,12 @@ job_query:
textcolor( WHITE );
cprintf("You must be new here!");
}
+ cprintf(" (Press T to enter a tutorial.)");
cprintf(EOL EOL);
textcolor( CYAN );
cprintf("You can be:");
+ cprintf(" (Press ? for more information)");
cprintf(EOL EOL);
textcolor( LIGHTGREY );
@@ -3160,12 +3168,11 @@ job_query:
"SPACE - Choose species first; * - Random Class; "
"! - Random Character"
EOL
- "? - Help; X - Quit"
+ "X - Quit"
EOL);
else
cprintf(EOL
- "? - Help; * - Random; "
- "Bksp - Back to species selection; X - Quit"
+ "* - Random; Bksp - Back to species selection; X - Quit"
EOL);
if (Options.prev_cls)
@@ -3255,6 +3262,10 @@ job_query:
ng_random = true;
return true;
}
+ else if (keyn == 'T')
+ {
+ return pick_tutorial();
+ }
else if ((keyn == ' ' && !you.species) ||
keyn == 'x' || keyn == ESCAPE || keyn == CK_BKSP)
{
@@ -3483,7 +3494,7 @@ void give_items_skills()
you.equip[EQ_BODY_ARMOUR] = 1;
// extra items being tested:
- choose_book( you.inv[2], BOOK_MINOR_MAGIC_I, 3 );
+ choose_book( you.inv[2], BOOK_MINOR_MAGIC_I, 3 );
you.skills[SK_DODGING] = 1;
you.skills[SK_STEALTH] = 1;
@@ -4072,14 +4083,14 @@ void give_items_skills()
you.equip[EQ_WEAPON] = 0;
you.equip[EQ_BODY_ARMOUR] = 1;
- if ( you.char_class == JOB_CONJURER )
- choose_book( you.inv[2], BOOK_CONJURATIONS_I, 2 );
- else
- {
- you.inv[2].base_type = OBJ_BOOKS;
- // subtype will always be overridden
- you.inv[2].plus = 0;
- }
+ if ( you.char_class == JOB_CONJURER )
+ choose_book( you.inv[2], BOOK_CONJURATIONS_I, 2 );
+ else
+ {
+ you.inv[2].base_type = OBJ_BOOKS;
+ // subtype will always be overridden
+ you.inv[2].plus = 0;
+ }
switch (you.char_class)
{
diff --git a/crawl-ref/source/newgame.h b/crawl-ref/source/newgame.h
index 6b71e483ba..066bab2418 100644
--- a/crawl-ref/source/newgame.h
+++ b/crawl-ref/source/newgame.h
@@ -20,5 +20,7 @@
bool new_game();
int give_first_conjuration_book();
+bool choose_race(void);
+bool choose_class(void);
#endif
diff --git a/crawl-ref/source/notes.cc b/crawl-ref/source/notes.cc
index 94af4546e7..496b83f336 100644
--- a/crawl-ref/source/notes.cc
+++ b/crawl-ref/source/notes.cc
@@ -35,7 +35,8 @@ static int real_god_power( int religion, int idx )
return count;
}
-static bool is_noteworthy_skill_level( int level ) {
+static bool is_noteworthy_skill_level( int level )
+{
unsigned i;
for ( i = 0; i < Options.note_skill_levels.size(); ++i )
if ( level == Options.note_skill_levels[i] )
@@ -43,8 +44,10 @@ static bool is_noteworthy_skill_level( int level ) {
return false;
}
-static bool is_highest_skill( int skill ) {
- for ( int i = 0; i < NUM_SKILLS; ++i ) {
+static bool is_highest_skill( int skill )
+{
+ for ( int i = 0; i < NUM_SKILLS; ++i )
+ {
if ( i == skill )
continue;
if ( you.skills[i] >= you.skills[skill] )
@@ -53,18 +56,21 @@ static bool is_highest_skill( int skill ) {
return true;
}
-static bool is_noteworthy_hp( int hp, int maxhp ) {
+static bool is_noteworthy_hp( int hp, int maxhp )
+{
return (hp > 0 && Options.note_hp_percent &&
hp <= (maxhp * Options.note_hp_percent) / 100);
}
-static int dungeon_branch_depth( unsigned char branch ) {
+static int dungeon_branch_depth( unsigned char branch )
+{
if ( branch >= NUM_BRANCHES )
return -1;
return branches[branch].depth;
}
-static bool is_noteworthy_dlevel( unsigned short place ) {
+static bool is_noteworthy_dlevel( unsigned short place )
+{
unsigned const char branch = (unsigned char) ((place >> 8) & 0xFF);
const int lev = (place & 0xFF);
@@ -84,8 +90,8 @@ static bool is_noteworthy_dlevel( unsigned short place ) {
This function assumes that game state has not changed since
the note was taken, e.g. you.* is valid.
*/
-static bool is_noteworthy( const Note& note ) {
-
+static bool is_noteworthy( const Note& note )
+{
/* always noteworthy */
if ( note.type == NOTE_XP_LEVEL_CHANGE ||
note.type == NOTE_GET_GOD ||
@@ -143,11 +149,13 @@ static bool is_noteworthy( const Note& note ) {
if ( note.type == NOTE_LEARN_SPELL && Options.note_all_spells )
return true;
- for ( unsigned i = 0; i < note_list.size(); ++i ) {
+ for ( unsigned i = 0; i < note_list.size(); ++i )
+ {
if ( note_list[i].type != note.type )
continue;
const Note& rnote( note_list[i] );
- switch ( note.type ) {
+ switch ( note.type )
+ {
case NOTE_DUNGEON_LEVEL_CHANGE:
if ( rnote.packed_place == note.packed_place )
return false;
@@ -183,7 +191,8 @@ static bool is_noteworthy( const Note& note ) {
return true;
}
-const char* number_to_ordinal( int number ) {
+const char* number_to_ordinal( int number )
+{
const char* ordinals[5] = { "first", "second", "third", "fourth",
"fifth" };
if ( number < 1)
@@ -193,7 +202,8 @@ const char* number_to_ordinal( int number ) {
return ordinals[number-1];
}
-std::string Note::describe( bool when, bool where, bool what ) const {
+std::string Note::describe( bool when, bool where, bool what ) const
+{
std::string result;
@@ -215,7 +225,8 @@ std::string Note::describe( bool when, bool where, bool what ) const {
if ( what )
{
char buf[200];
- switch ( type ) {
+ switch ( type )
+ {
case NOTE_HP_CHANGE:
// [ds] Shortened HP change note from "Had X hitpoints" to
// accommodate the cause for the loss of hitpoints.
@@ -311,13 +322,15 @@ std::string Note::describe( bool when, bool where, bool what ) const {
return result;
}
-Note::Note() {
+Note::Note()
+{
turn = you.num_turns;
packed_place = get_packed_place();
}
Note::Note( NOTE_TYPES t, int f, int s, const char* n, const char* d ) :
- type(t), first(f), second(s) {
+ type(t), first(f), second(s)
+{
if (n)
name = std::string(n);
if (d)
@@ -326,7 +339,8 @@ Note::Note( NOTE_TYPES t, int f, int s, const char* n, const char* d ) :
packed_place = get_packed_place();
}
-void Note::save( FILE* fp ) const {
+void Note::save( FILE* fp ) const
+{
writeLong( fp, type );
writeLong( fp, turn );
writeShort( fp, packed_place );
@@ -336,7 +350,8 @@ void Note::save( FILE* fp ) const {
writeString( fp, desc );
}
-void Note::load( FILE* fp ) {
+void Note::load( FILE* fp )
+{
type = (NOTE_TYPES)(readLong( fp ));
turn = readLong( fp );
packed_place = readShort( fp );
@@ -348,39 +363,46 @@ void Note::load( FILE* fp ) {
bool notes_active = false;
-bool notes_are_active() {
+bool notes_are_active()
+{
return notes_active;
}
-void take_note( const Note& note, bool force ) {
+void take_note( const Note& note, bool force )
+{
if ( notes_active && (force || is_noteworthy(note)) )
note_list.push_back( note );
}
-void activate_notes( bool active ) {
+void activate_notes( bool active )
+{
notes_active = active;
}
-void save_notes( FILE* fp ) {
+void save_notes( FILE* fp )
+{
writeLong( fp, NOTES_VERSION_NUMBER );
writeLong( fp, note_list.size() );
for ( unsigned i = 0; i < note_list.size(); ++i )
note_list[i].save(fp);
}
-void load_notes( FILE* fp ) {
+void load_notes( FILE* fp )
+{
if ( readLong(fp) != NOTES_VERSION_NUMBER )
return;
const long num_notes = readLong(fp);
- for ( long i = 0; i < num_notes; ++i ) {
+ for ( long i = 0; i < num_notes; ++i )
+ {
Note new_note;
new_note.load(fp);
note_list.push_back(new_note);
}
}
-void make_user_note() {
+void make_user_note()
+{
mpr("Enter note: ", MSGCH_PROMPT);
char buf[400];
bool validline = !cancelable_get_line(buf, sizeof(buf));
diff --git a/crawl-ref/source/notes.h b/crawl-ref/source/notes.h
index 6dfa327992..0948334b06 100644
--- a/crawl-ref/source/notes.h
+++ b/crawl-ref/source/notes.h
@@ -15,7 +15,8 @@
#include <vector>
#include <stdio.h>
-enum NOTE_TYPES {
+enum NOTE_TYPES
+{
NOTE_HP_CHANGE = 0, /* needs: new hp, max hp */
NOTE_MAXHP_CHANGE, /* needs: new maxhp */
NOTE_MP_CHANGE, /* needs: new mp, max mp */
@@ -41,7 +42,8 @@ enum NOTE_TYPES {
NOTE_NUM_TYPES
};
-struct Note {
+struct Note
+{
Note();
Note( NOTE_TYPES t, int f = 0, int s = 0, const char* n = 0,
const char* d = 0);
diff --git a/crawl-ref/source/ouch.cc b/crawl-ref/source/ouch.cc
index 85cfe0ad83..e022bb8d44 100644
--- a/crawl-ref/source/ouch.cc
+++ b/crawl-ref/source/ouch.cc
@@ -70,6 +70,7 @@
#include "skills2.h"
#include "spells4.h"
#include "stuff.h"
+#include "tutorial.h"
#include "view.h"
@@ -871,7 +872,7 @@ void end_game( struct scorefile_entry &se )
#ifdef PACKAGE_SUFFIX
PACKAGE_SUFFIX ,
#endif
- ".st", ".kil", ".tc", ".nts", ".sav"
+ ".st", ".kil", ".tc", ".nts", ".tut", ".sav"
};
const int num_suffixes = sizeof(suffixes) / sizeof(const char*);
@@ -888,6 +889,9 @@ void end_game( struct scorefile_entry &se )
viewwindow(1, false); // don't do this for leaving/winning characters
}
+ if (Options.tutorial_left)
+ tutorial_death_screen();
+
if (!crawl_state.seen_hups)
more();
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index 962334975b..4cd501d463 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -54,6 +54,7 @@
#include "stuff.h"
#include "transfor.h"
#include "travel.h"
+#include "tutorial.h"
#include "view.h"
/*
@@ -2069,8 +2070,10 @@ int burden_change(void)
{
you.burden_state = BS_ENCUMBERED;
- if (old_burdenstate != you.burden_state)
+ if (old_burdenstate != you.burden_state) {
mpr("You are being weighed down by all of your possessions.");
+ learned_something_new(TUT_HEAVY_LOAD);
+ }
}
else
{
@@ -2155,6 +2158,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();
} // end gain_exp()
void level_change(void)
@@ -2732,9 +2737,13 @@ void level_change(void)
if (you.religion == GOD_XOM)
Xom_acts(true, you.experience_level, true);
+
+ learned_something_new(TUT_NEW_LEVEL);
+
}
redraw_skill( you.your_name, player_title() );
+
} // end level_change()
// here's a question for you: does the ordering of mods make a difference?
@@ -4174,6 +4183,7 @@ void poison_player( int amount, bool force )
// XXX: which message channel for this message?
mpr( info );
+ learned_something_new(TUT_YOU_POISON);
}
}
@@ -4219,6 +4229,7 @@ void confuse_player( int amount, bool resistable )
// XXX: which message channel for this message?
mpr( info );
+ learned_something_new(TUT_YOU_ENCHANTED);
}
}
@@ -4256,6 +4267,7 @@ void slow_player( int amount )
if (you.slow > 100)
you.slow = 100;
+ learned_something_new(TUT_YOU_ENCHANTED);
}
}
@@ -4339,6 +4351,7 @@ void disease_player( int amount )
const int tmp = you.disease + amount;
you.disease = (tmp > 210) ? 210 : tmp;
+ learned_something_new(TUT_YOU_SICK);
}
void dec_disease_player( void )
diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc
index 46574758b4..cf0a5b775a 100644
--- a/crawl-ref/source/religion.cc
+++ b/crawl-ref/source/religion.cc
@@ -61,6 +61,7 @@
#include "spells3.h"
#include "spl-cast.h"
#include "stuff.h"
+#include "tutorial.h"
#include "view.h"
const char *sacrifice[] = {
@@ -1605,6 +1606,7 @@ void gain_piety(char pgn)
god_speaks(you.religion, info);
}
}
+ learned_something_new(TUT_NEW_ABILITY);
}
}
diff --git a/crawl-ref/source/skills.cc b/crawl-ref/source/skills.cc
index 3bb6a54119..24387490ad 100644
--- a/crawl-ref/source/skills.cc
+++ b/crawl-ref/source/skills.cc
@@ -27,6 +27,7 @@
#include "player.h"
#include "skills2.h"
#include "stuff.h"
+#include "tutorial.h"
// MAX_COST_LIMIT is the maximum XP amount it will cost to raise a skill
@@ -416,6 +417,7 @@ static int exercise2( int exsk )
}
mpr( info, MSGCH_INTRINSIC_GAIN );
+ learned_something_new(TUT_SKILL_RAISE);
// Recalculate this skill's order for tie breaking skills
// at its new level. See skills2.cc::init_skill_order()
diff --git a/crawl-ref/source/skills2.cc b/crawl-ref/source/skills2.cc
index 810f8e6df7..0bbc35dace 100644
--- a/crawl-ref/source/skills2.cc
+++ b/crawl-ref/source/skills2.cc
@@ -48,7 +48,8 @@
// the character's race will be listed on the next line. Its only really
// intended for cases where things might be really awkward without it. -- bwr
-const char *skills[50][6] = {
+const char *skills[50][6] =
+{
{"Fighting", "Skirmisher", "Grunt", "Veteran", "Warrior", "Slayer"}, // 0
{"Short Blades", "Stabber", "Cutter", "Knifefighter", "Eviscerator", "Blademaster"},
{"Long Blades", "Slasher", "Slicer", "Fencer", "Swordfighter", "Swordmaster"},
@@ -123,7 +124,8 @@ const char *martial_arts_titles[6] =
3.10: but it never is, and CLASSES is probably broken now. Anyway,
the Spellcasting skill (25) is actually about 130% of what is shown here.
*/
-const int spec_skills[ NUM_SPECIES ][40] = {
+const int spec_skills[ NUM_SPECIES ][40] =
+{
{ // SP_HUMAN (1)
100, // SK_FIGHTING
100, // SK_SHORT_BLADES
@@ -1788,7 +1790,8 @@ JOB_PALADIN:
************************************************************* */
-static const int skill_display_order[] = {
+static const int skill_display_order[] =
+{
SK_FIGHTING, SK_SHORT_BLADES, SK_LONG_SWORDS, SK_AXES,
SK_MACES_FLAILS, SK_POLEARMS, SK_STAVES, SK_UNARMED_COMBAT,
diff --git a/crawl-ref/source/stash.cc b/crawl-ref/source/stash.cc
index 890d6a141c..eca4bf4da6 100644
--- a/crawl-ref/source/stash.cc
+++ b/crawl-ref/source/stash.cc
@@ -26,6 +26,7 @@
#include "stuff.h"
#include "tags.h"
#include "travel.h"
+#include "tutorial.h"
#include <cctype>
#include <cstdio>
@@ -283,6 +284,12 @@ void Stash::update()
// There's something on this square. Take a squint at it.
const item_def &item = mitm[objl];
+ // note item when first entering field of vision
+ // (only works with stashes enabled)
+// learned_something_new(TUT_SEEN_FIRST_OBJECT, x, y);
+// learned_something_new(TUT_SEEN_FIRST_OBJECT, item);
+ tutorial_first_item(item);
+
if (item.link == NON_ITEM)
items.clear();
diff --git a/crawl-ref/source/stuff.cc b/crawl-ref/source/stuff.cc
index 0c492f03ee..4aa1315c1a 100644
--- a/crawl-ref/source/stuff.cc
+++ b/crawl-ref/source/stuff.cc
@@ -50,7 +50,7 @@
#include "delay.h"
#include "externs.h"
-
+#include "items.h"
#include "macro.h"
#include "misc.h"
#include "monstuff.h"
@@ -60,6 +60,7 @@
#include "output.h"
#include "player.h"
#include "skills2.h"
+#include "tutorial.h"
#include "view.h"
// Crude, but functional.
@@ -643,9 +644,9 @@ int yesnoquit( const char* str, bool safe, int safeanswer, bool clear_after )
tmp = (unsigned char) getch();
- if ( tmp == 27 || tmp == 'q' || tmp == 'Q' )
- return -1;
-
+ if ( tmp == 27 || tmp == 'q' || tmp == 'Q' )
+ return -1;
+
if ((tmp == ' ' || tmp == '\r' || tmp == '\n') && safeanswer)
tmp = safeanswer;
@@ -769,9 +770,9 @@ unsigned char random_colour(void)
unsigned char random_uncommon_colour()
{
unsigned char result;
- do {
+ do
result = random_colour();
- } while ( result == LIGHTCYAN || result == CYAN || result == BROWN );
+ while ( result == LIGHTCYAN || result == CYAN || result == BROWN );
return result;
}
@@ -1051,6 +1052,16 @@ void zap_los_monsters()
continue;
int imon = mgrd[gx][gy];
+
+ // at tutorial beginning disallow items in line of sight
+ if (Options.tutorial_events[TUT_SEEN_FIRST_OBJECT])
+ {
+ int item = igrd[gx][gy];
+
+ if (item != NON_ITEM && is_valid_item(mitm[item]) )
+ destroy_item(item);
+ }
+
if (imon == NON_MONSTER || imon == MHITYOU)
continue;
diff --git a/crawl-ref/source/travel.cc b/crawl-ref/source/travel.cc
index a3983a4145..4fa02e4764 100644
--- a/crawl-ref/source/travel.cc
+++ b/crawl-ref/source/travel.cc
@@ -27,6 +27,7 @@
#include "stash.h"
#include "stuff.h"
#include "travel.h"
+#include "tutorial.h"
#include "view.h"
#include <algorithm>
@@ -832,8 +833,10 @@ static void explore_find_target_square()
// No place to go? Report to the player.
const int estatus = find_explore_status(tp);
- if (!estatus)
+ if (!estatus) {
mpr("Done exploring.");
+ learned_something_new(TUT_DONE_EXPLORE);
+ }
else
{
std::vector<std::string> inacc;
@@ -3462,6 +3465,10 @@ void explore_discoveries::add_item(const item_def &i)
}
items.push_back( named_thing<item_def>(item_name(i, DESC_NOCAP_A), i) );
+
+ // first item of this type?
+ // only works when travelling
+ tutorial_first_item(i);
}
void explore_discoveries::found_item(const coord_def &pos, const item_def &i)
diff --git a/crawl-ref/source/tutorial.cc b/crawl-ref/source/tutorial.cc
new file mode 100644
index 0000000000..c2557ef8d9
--- /dev/null
+++ b/crawl-ref/source/tutorial.cc
@@ -0,0 +1,1351 @@
+/*
+ * Created for Crawl Reference by JPEG on $Date: 2007-01-11$
+ */
+
+#include "AppHdr.h"
+#include "tutorial.h"
+#include <cstring>
+
+#include "command.h"
+#include "files.h"
+#include "itemprop.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"
+ EOL "* - Random tutorial; X - Quit"
+ 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_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 += "<magenta>Welcome to Dungeon Crawl!</magenta>" 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 += " <white>?</white> - shows the items and the commands" EOL;
+ result += " <white>S</white> - saves the game, to be resumed later (but note that death is permanent)" EOL;
+ result += " <white>x</white> - 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 += " <darkgray>readme.txt</darkgray> - A very short guide to Crawl." EOL;
+ result += " <darkgray>manual.txt</darkgray> - This contains all details on races, magic, skills, etc." EOL;
+ result += " <darkgray>crawl_options.txt</darkgray> - 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 <white>Space</white> to proceed to the basics (the screen division and movement)." EOL;
+ result += "Press <white>Esc</white> 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 += "<white>";
+ result += info;
+ result += "</white>" EOL EOL;
+
+ result += "<darkgray>";
+ 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 += "</darkgray>" EOL 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 += "<magenta>";
+ result += "What you see here is the typical Crawl screen. The upper left map shows your " EOL;
+ result += "hero as the <white>@<magenta> in the center. The parts of the map you remember but cannot " EOL;
+ result += "currently see, will be greyed out." EOL;
+ result += "</magenta>";
+ result += "[--more-- Press <white>Escape</white> to skip the basics]";
+
+ return formatted_string::parse_string(result);
+}
+
+static formatted_string tutorial_stats_intro()
+{
+ std::string result;
+ result += "<magenta>";
+
+ 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, "<white>HP: %d/%d<magenta>. ", 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, "<white>Magic: %d/%d<magenta>", 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, <white>Str<magenta>ength, <white>Dex<magenta>terity and " EOL;
+ result += "<white>Int<magenta>elligence are shown and provide an " EOL;
+ result += "all-around account of the character's " EOL;
+ result += "attributes. " EOL;
+
+ result += "</magenta>";
+ result += " " EOL;
+ result += "[--more-- Press <white>Escape</white> 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 += "<magenta>";
+ result += "This lower part of the screen is reserved for messages. Everything related to " EOL;
+ result += "the tutorial is shown in this colour. If you missed something, previous messages " EOL;
+ result += "can be shown again with <white>Ctrl-P<magenta>. " EOL;
+ result += "</magenta>";
+ result += "[--more-- Press <white>Escape</white> to skip the basics]";
+
+ return formatted_string::parse_string(result);
+}
+
+static void tutorial_movement_info()
+{
+ snprintf(info, INFO_SIZE, "To move your character, use the numpad; try Numlock both on and off. If your ");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "system has no number pad, or if you are familiar with the vi keys, movement is ");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "also possible with <w>hjklyubn<magenta>. A basic command list can be found under <w>?<magenta>, and the ");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "most important commands will be explained to you as it becomes necessary. ");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+}
+
+// copied from display_mutations and adapted
+void tut_starting_screen()
+{
+#ifdef DOS_TERM
+ char buffer[4800];
+#endif
+ 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 = 19; // message window
+ }
+ else if (i==2)
+ {
+ x2 = 40; // map window
+ y2 = 18;
+ }
+
+#ifdef DOS_TERM
+ window(x1, y1, x2, y2);
+ gettext(x1, y1, x2, y2, buffer);
+#endif
+ 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();
+
+#ifdef DOS_TERM
+ puttext(x1, y1, x2, y2, buffer);
+#endif
+ redraw_screen();
+ if (ch == ESCAPE)
+ break;
+
+ }
+ // too important to break off early
+
+}
+
+void tutorial_death_screen()
+{
+ Options.tutorial_left = 0;
+
+ mpr( "Welcome to Crawl! Stuff like this happens all the time.", MSGCH_TUTORIAL, 0);
+ mpr( "Okay, that didn't go too well. Maybe you need to change your tactics. Here's one out of several hints:", MSGCH_TUTORIAL);
+ more();
+
+ int hint = random2(6);
+ switch(hint)
+ {
+ case 0:
+ snprintf(info2, INFO2_SIZE, "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.");
+ mpr(info2, MSGCH_TUTORIAL);
+ break;
+ case 1:
+ snprintf(info2, INFO2_SIZE, "Learn when to run away from things you can't handle - this is important! "
+ "It is often wise to skip a dangerous level. But don't overdo this as monsters will only get harder "
+ "the deeper you get.");
+ mpr(info2, MSGCH_TUTORIAL);
+ break;
+ case 2:
+// 1 5 10 5 20 5 30 5 40 5 50 5 60 5 70 5 80
+ snprintf(info, INFO_SIZE, "Rest between encounters. In Crawl, searching and resting are one and the same.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "To search for one turn, press <w>s<magenta>, <w>.<magenta>, <w>delete<magenta> or <w>keypad-5<magenta>. Pressing <w>5<magenta> or");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "<w>shift-and-keypad-5<magenta> will let you rest for a longer time (you will stop resting");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "when fully healed).");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case 3:
+ snprintf(info2, INFO2_SIZE, "Always consider using projectiles before engaging monsters in close combat.");
+ mpr(info2, MSGCH_TUTORIAL);
+ break; // check Options.tut_throw_counter
+ case 4:
+ snprintf(info2, INFO2_SIZE, "If you are badly wounded, you can run away from monsters to buy some time. "
+ "Try losing them in corridors or find a place where you can run around in circles to heal while the "
+ "monster chases you.");
+ mpr(info2, MSGCH_TUTORIAL);
+ break;
+ case 5:
+ snprintf(info2, INFO2_SIZE, "Never fight more than one monster if you can help it. Always back into a "
+ "corridor so that they must fight you one on one.");
+ mpr(info2, MSGCH_TUTORIAL);
+ break;
+ default:
+ snprintf(info2, INFO2_SIZE, "Sorry, no hint this time, although there should have been one.");
+ mpr(info2, MSGCH_TUTORIAL);
+ }
+ more();
+ if (Options.tutorial_type == TUT_MAGIC_CHAR && Options.tut_spell_counter < 10 )
+ mpr( "As a Conjurer your main weapon should be offensive magic. Cast spells more often!", MSGCH_TUTORIAL);
+ else if (Options.tutorial_type == TUT_BERSERK_CHAR && Options.tut_berserk_counter < 1 )
+ mpr( "Also remember to use Berserk when fighting stronger foes.", MSGCH_TUTORIAL);
+ else if (Options.tutorial_type == TUT_RANGER_CHAR && Options.tut_throw_counter < 10 )
+ mpr( "Also remember to use your bow and arrows against distant monsters.", MSGCH_TUTORIAL);
+
+ mpr( "See you next game!", MSGCH_TUTORIAL);
+
+ for ( long i = 0; i < TUT_EVENTS_NUM; ++i )
+ Options.tutorial_events[i] = 0;
+}
+
+void tutorial_finished()
+{
+ Options.tutorial_left = 0;
+ snprintf(info, INFO_SIZE, "That's it for now: For the time being you've learned enough, so we'll let you ");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "find out the rest for yourself. If you haven't already done so you might try a ");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "different tutorial sometime to gain an insight into other aspects of Crawl. ");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "Note that from now on the help screen (<w>?<magenta>) will look a bit different. Let us say");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "goodbye with a playing hint:");
+ mpr(info, MSGCH_TUTORIAL);
+ more();
+
+ if (Options.tut_explored)
+ {
+ snprintf(info, INFO_SIZE, "Walking around and exploring levels gets easier by using auto-explore (<w>Ctrl-O<magenta>).");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ }
+ else if (Options.tut_travel)
+ {
+// 1 5 10 5 20 5 30 5 40 5 50 5 60 5 70 5 80
+ snprintf(info, INFO_SIZE, "There is a convenient way for travelling between far away dungeon levels: press");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "<w>Ctrl-G<magenta> and enter the desired destination. If your travel gets interrupted,");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "issueing <w>Ctrl-G Enter<magenta> will continue it.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ }
+ else if (Options.tut_stashes)
+ {
+ snprintf(info, INFO_SIZE, "To keep an account on the great many items scattered throughout the dungeon,");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "use the <w>Ctrl-F<magenta> command. This lets you find anything that matches the word you");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "entered. For example, <w>Ctrl-F \"knife\"<magenta> will list all places where knives have");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "been seen. Also, by pressing the letter of one the spots, you will be able to");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "travel there automatically. It is even possible to search for dungeon features");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "like <w>\"shop\"<magenta>, <w>\"altar\"<magenta>, or for more general terms like <w>\"blade\"<magenta>. The latter");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "will search for all weapons falling into either the Short Blades or the Long");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "Blades classes.");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "For those familiar with regular expressions: these can be used here as well.");
+ mpr(info, MSGCH_TUTORIAL);
+ }
+ else
+ {
+ switch (random2(2)+3)
+ {
+ case 3:
+ snprintf(info, INFO_SIZE, "The game will keep an automated logbook for your characters. Use <w>?:<magenta> to read it.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "You can also enter notes manually with the <w>:<magenta> command. Once your character");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "perishes, two morgue files will be left (look in the <w>/morgue<magenta> directory). The");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "one ending in txt contains a copy of your logbook.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ case 4:
+ snprintf(info, INFO_SIZE, "Crawl has a macro function built in: press <w>~m<magenta> to define a macro by first");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "specifying a trigger key (say, <w>F<magenta>) and a command sequence. So, for example");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "<w>~mFZa+.<magenta> will make the <w>F<magenta> key always zap the spell in slot a on the nearest");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "monster. Do not overwrite crucial commands! The functions keys F1-F12 are");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "generally safe. The <w>~<magenta> also lets you define keybindings. These are useful when");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "some keys do not work as expected on your system. In addition, you can define");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "macro-type sequences for the level map or targeting modes only.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ /*
+ case 5:
+ snprintf(info2, INFO2_SIZE, "You can use the <w>{<magenta> command to inscribe some words on one of your items. "
+ "Besides general item-related notes, there are some further functions for this: inscribing <w>@w1<magenta> on a weapon "
+ "lets you wield it with <w>w1<magenta>, regardless of the inventory letter (<w>1<magenta> can be replaced by any digit and <w>w<magenta> "
+ "could be any action key or <w>*<magenta>, meaning all actions). Inscribing <w>!d<magenta> will ask you before dropping the item.");
+ break;
+ case 6:
+ snprintf(info2, INFO2_SIZE, "The interface can be greatly customised. All options are explained in the file "
+ "<w>crawl_options.txt<magenta> which should be in the <w>/docs<magenta> directory. The options are set in <w>init.txt<magenta> or <w>white .crawlrc<magenta>. "
+ "Crawl will complain when it can't find either.\n"
+ "Options of the yes/no (or true/false) type appear the opposite of their default, e.g. <w>\"#show_turns = false\"<magenta>. "
+ "Just removing the comment sign <w><magenta> will change the behaviour of such options. Other options require numbers/words as "
+ "arguments - read the doc file for these.");
+ break;
+ */
+ default:
+ snprintf(info2, INFO2_SIZE, "Oops... No hint for now. Better luck next time!");
+ }
+ }
+ mpr("Have a crunchy Crawling!", MSGCH_TUTORIAL, 0);
+ more();
+
+ Options.tutorial_left = 0;
+ for ( long i = 0; i < TUT_EVENTS_NUM; ++i )
+ Options.tutorial_events[i] = 0;
+}
+
+void tutorial_prayer_reminder()
+{
+ if (coinflip())
+ {// always would be too annoying
+ snprintf(info, INFO_SIZE, "Remember to <w>p<magenta>ray before battle, so as to dedicate your kills to %s. ", god_name(you.religion));
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "Should the monster leave a corpse, consider <w>D<magenta>issecting it as a sacrifice to");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "%s, as well.", god_name(you.religion));
+ mpr(info, MSGCH_TUTORIAL);
+ }
+}
+
+void tutorial_healing_reminder()
+{
+ if (you.poison)
+ {
+ if (Options.tutorial_events[TUT_NEED_POISON_HEALING])
+ return;
+ 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.poison)
+ {
+ snprintf(info, INFO_SIZE, "Remember to rest between fights and to enter unexplored terrain with full");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "hitpoints and/or magic. To do this, press <w>5<magenta>.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ }
+ Options.tut_last_healed = you.num_turns;
+ }
+}
+
+void taken_new_item(unsigned char item_type)
+{
+/*
+ if (Options.tutorial_events[TUT_SEEN_FIRST_OBJECT])
+ learned_something_new(TUT_SEEN_FIRST_OBJECT);
+*/
+ 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 help = "<magenta>That ";
+ formatted_string st = formatted_string::parse_string(help);
+ st.formatted_string::textcolor(channel_to_colour(MSGCH_TUTORIAL));
+ st.formatted_string::add_glyph(&mon);
+ help = " is a monster, usually depicted by letters, e.g. typical early monsters ";
+ st += formatted_string::parse_string(help);
+ formatted_mpr(st, MSGCH_TUTORIAL);
+
+ learned_something_new(TUT_SEEN_MONSTER);
+}
+
+void tutorial_first_item(item_def item)
+{
+ if (!Options.tutorial_events[TUT_SEEN_FIRST_OBJECT] || Options.tut_just_triggered)
+ return;
+
+ std::string help = "<magenta>That ";
+ formatted_string st = formatted_string::parse_string(help);
+ st.formatted_string::textcolor(channel_to_colour(MSGCH_TUTORIAL));
+ st.formatted_string::add_glyph(&item);
+ help = " is an item. If you move to this spot and press <w>g<magenta> or <w>,<magenta> you can pick it up.";
+ st += formatted_string::parse_string(help);
+ formatted_mpr(st, MSGCH_TUTORIAL);
+
+ learned_something_new(TUT_SEEN_FIRST_OBJECT);
+}
+
+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 && seen_what != TUT_SEEN_MONSTER)
+ return;
+
+ // If seeing something right at game start wait for input
+/* if (!you.num_turns && !you.turn_is_over && getch() == 0)
+ getch();
+*/
+ switch(seen_what)
+ {
+ case TUT_SEEN_FIRST_OBJECT:
+// 1 5 10 5 20 5 30 5 40 5 50 5 60 5 70 5 80
+ snprintf(info, INFO_SIZE, "Generally, items are shown by non-letter symbols like <w>%%?!\"=()[<magenta>. Once picked up,");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "you can drop it again with <w>d<magenta>. Depending on the current options, several types of");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "objects will be picked up automatically.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case TUT_SEEN_POTION:
+ snprintf(info, INFO_SIZE, "You have picked up your first potion ('<w>!<magenta>'). Use <w>q<magenta> to quaff (drink) it.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case TUT_SEEN_SCROLL:
+ snprintf(info, INFO_SIZE, "You have picked up your first scroll ('<w>?<magenta>'). Type <w>r<magenta> to read it.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case TUT_SEEN_WAND:
+ snprintf(info, INFO_SIZE, "You have picked up your first wand ('<w>/<magenta>'). Type <w>z<magenta> to zap it.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case TUT_SEEN_SPBOOK:
+ snprintf(info, INFO_SIZE, "You have picked up a spellbook ('<w>+<magenta>' or '<w>:<magenta>'). You can read it by typing <w>r<magenta>,");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "memorise spells via <w>M<magenta> and cast a memorised spell with <w>Z<magenta>.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ if (you.skills[SK_SPELLCASTING])
+ {
+ snprintf(info, INFO_SIZE, "However, for now you will be unable to do so. First your mind has to become");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "accustomed to using magic by reading lots of scrolls.");
+ mpr(info, MSGCH_TUTORIAL);
+ }
+ break;
+ case TUT_SEEN_WEAPON:
+ snprintf(info, INFO_SIZE, "This is the first weapon ('<w>(<magenta>') you've picked up. Use <w>w<magenta> to wield it, but be");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "aware that this weapon might use a different skill from the one you've been");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "training with your current one. You can use <w>v<magenta> (followed by the weapon's letter)");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "to find out about that and other extras the weapon might have. To take a look at");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "your skills, press <w>m<magenta>.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case TUT_SEEN_MISSILES:
+ snprintf(info, INFO_SIZE, "This is the first stack of missiles ('<w>)<magenta>') you've picked up. Darts can be thrown");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "by hand, but other missile types like arrows and needles require a launcher (and");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "training in using it) to be really effective. <w>v<magenta> gives more information about");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "both missiles and launcher.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ if (Options.tutorial_type == TUT_RANGER_CHAR)
+ {
+ snprintf(info, INFO_SIZE, "As 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)
+ {
+ snprintf(info2, INFO2_SIZE, "However, as a spellslinger you don't really need another type of ranged attack, unless there's another effect in addition to damage.");
+ }
+ else
+ {
+ snprintf(info2, INFO2_SIZE, "For now you might be best off with sticking to darts or stones for ranged attacks.");
+ }
+ mpr(info2, MSGCH_TUTORIAL, 1);
+ break;
+ case TUT_SEEN_ARMOUR:
+ snprintf(info, INFO_SIZE, "This is the first piece of armour ('<w>[<magenta>') you've picked up. Use <w>W<magenta> to wear it and");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "<w>T<magenta> to take it off again. You can also use <w>v<magenta> (followed by the armour's letter)");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "to get some information about it.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ if (you.species == SP_CENTAUR)
+ {
+ snprintf( info, INFO_SIZE, "Note that as a %s, you will be unable to wear boots.", species_name(you.species, 1) );
+ mpr(info, MSGCH_TUTORIAL);
+ }
+ else if (you.species == SP_MINOTAUR)
+ {
+ snprintf( info, INFO_SIZE, "Note that as a %s, you will be unable to wear helmets.", species_name(you.species, 1) );
+ mpr(info, MSGCH_TUTORIAL);
+ }
+ break;
+ case TUT_SEEN_RANDART:
+ snprintf(info, INFO_SIZE, "Weapons and armour that have descriptions like this are more likely to be of higher"
+ "enchantment or have special properties, good or bad.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ case TUT_SEEN_FOOD:
+ snprintf(info, INFO_SIZE, "You have picked up some food ('<w>%%<magenta>'). You can eat it by typing <w>e<magenta>.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case TUT_SEEN_CARRION:
+ snprintf(info, INFO_SIZE, "You have picked up a corpse ('<w>%%<magenta>'). You can dissect it with <w>D<magenta> - it needs to be");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "on the ground for that to work, though - and <w>e<magenta>at the resulting chunks (though");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "they may not be healthy). During prayer you can offer corpses to your god by dissecting them, as well.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ case TUT_SEEN_JEWELLERY:
+ snprintf(info, INFO_SIZE, "You have picked up a a piece of jewellery, either a ring ('<w>=<magenta>') or an amulet");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "an amulet ('<w>\"<magenta>'). Type <w>P<magenta> to put it on and <w>R<magenta> to remove it. You can also use <w>v<magenta>");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "(followed by the appropriate letter) to get some information about it.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case TUT_SEEN_MISC:
+ snprintf(info, INFO_SIZE, "This is a strange object. You can play around with it to find out what it does");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "by <w>w<magenta>ielding and <w>E<magenta>voking it.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case TUT_SEEN_MONSTER:
+// snprintf(info, INFO_SIZE, "The %s is a monster, usually depicted by letters, e.g. typical early monsters are", st.c_str());
+ snprintf(info, INFO_SIZE, "are <brown>r<magenta>, <w>g<magenta>, <lightgray>b<magenta> or <brown>K<magenta>. You can gain some information about it by pressing <w>x<magenta>, moving");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "the cursor on the monster and then pressing <w>v<magenta>. To attack it with your current");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "weapon, simply move into it.");
+ mpr(info, MSGCH_TUTORIAL);
+
+ if (Options.tutorial_type == TUT_RANGER_CHAR)
+ {
+ snprintf(info, INFO_SIZE, "However, as a hunter you might want to deal with it using your bow. Do this");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "with the following keypresses: <w>wbf+.<magenta> Here, <w>wb<magenta> wields the bow, <w>f<magenta> fires");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "appropriate ammunition (your arrows) and enters targeting mode. A very simple");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "way to aim is by pressing <w>+<magenta> and <w>-<magenta> until you target the proper monster. Finally,");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "<w>.<magenta> or <w>Enter<magenta> will fire. If you miss, <w>ff<magenta> will fire at the previous target.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ }
+ else if (Options.tutorial_type == TUT_MAGIC_CHAR)
+ {
+ snprintf(info, INFO_SIZE, "However, as a conjurer you might want to deal with it using magic. Do this");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "with the following key presses: <w>Za+.<magenta> Here, <w>Za<magenta> zaps the first spell you know,");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "%s, and enters targeting mode. A very simple way to aim is by ", spell_title(get_spell_by_letter('a')));
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "pressing <w>+<magenta> and <w>-<magenta> until you target the proper monster. Finally, fire by pressing");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "<w>.<magenta> or <w>Enter<magenta>. If you miss, <w>Zap<magenta> will fire at the previous target.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ }
+ break;
+ case TUT_SEEN_STAIRS:
+ snprintf(info, INFO_SIZE, "These are some downstairs. You can enter the next (deeper) level by following");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "them down (<w>><magenta>). To get back to this level again, press <w><<<magenta> while standing on the");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "upstairs.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ case TUT_SEEN_TRAPS:
+ snprintf(info, INFO_SIZE, "Oops... you just entered a trap. An unwary adventurer will occasionally stumble");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "into one of these nasty constructions depicted by <w>^<magenta>. They can do physical");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "damage (with darts or needles, for example) or have other, more magical "
+ "effects, e.g. teleportation.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ case TUT_SEEN_ALTAR:
+ snprintf(info, INFO_SIZE, "The <w>_<magenta> is an altar. You can get information about it or join this god by pressing");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "<w>p<magenta> while standing on the square. Don't worry: You'll be asked for confirmation!");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case TUT_SEEN_SHOP:
+ snprintf(info, INFO_SIZE, "The <w>%c<magenta> is a shop. You can enter it same as you would a new level, by typing <w><<<magenta>.", get_screen_glyph(x,y));
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case TUT_SEEN_DOOR:
+ snprintf(info, INFO_SIZE, "The <w>%c<magenta> is a closed door. You can open it by walking into it.", get_screen_glyph(x,y));
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "Sometimes it is useful to close a door. Do so by pressing <w>c<magenta>, followed by the direction.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case TUT_KILLED_MONSTER:
+ snprintf(info, INFO_SIZE, "Congratulations, your character just gained some experience by killing this "
+ "monster! By taking actions, certain skills will be trained and become better.");
+ mpr(info, MSGCH_TUTORIAL);
+ if (/*Options.tutorial_type == TUT_MELEE_CHAR ||*/ Options.tutorial_type == TUT_BERSERK_CHAR)
+ snprintf(info, INFO_SIZE, "For example, killing monsters in melee battle will raise your Axes and Fighting skills.");
+ else if (Options.tutorial_type == TUT_RANGER_CHAR)
+ snprintf(info, INFO_SIZE, "For example, killing monsters using bow and arrows will raise your Bows and Ranged Combat skills.");
+ else //if (Options.tutorial_type == TUT_MAGIC_CHAR)
+ snprintf(info, INFO_SIZE, "For example, killing monsters with offensive magic will raise your Conjurations" "and Spellcasting skills.");
+ mpr(info, MSGCH_TUTORIAL);
+
+ if (you.religion != GOD_NO_GOD)
+ {
+ snprintf(info, INFO_SIZE, "To dedicate your kills to %s, pray (<w>p<magenta>) before battle. ", god_name(you.religion));
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "Not all gods will be equally pleased at doing this, nor will they accept every kill.");
+ mpr(info, MSGCH_TUTORIAL);
+ }
+ break;
+ case TUT_NEW_LEVEL:
+ snprintf(info2, INFO2_SIZE, "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).");
+ mpr(info2, MSGCH_TUTORIAL);
+ if (Options.tutorial_type == TUT_MAGIC_CHAR)
+ {
+ snprintf(info, INFO_SIZE, "New experience levels will let you learn more spells (the Spellcasting skill");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "also does this). For now, you should try to memorise the second spell of your");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "starting book with <w>M<magenta>, the letter of the book, and <w>b<magenta>. Once learnt, you can zap");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "it with <w>Zb<magenta>.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ }
+ break;
+ case TUT_SKILL_RAISE:
+ snprintf(info, INFO_SIZE, "One of your skills just got raised. Type <w>m<magenta> to take a look at your skills screen.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case TUT_YOU_ENCHANTED:
+ snprintf(info, INFO_SIZE, "Enchantments of all types can befall you temporarily. Abbreviated signalisation");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "appears at the lower end of the stats area. Press <w>@<magenta> for more details. A list of");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "all possible enchantments is given in the manual.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ case TUT_YOU_SICK:
+ learned_something_new(TUT_YOU_ENCHANTED);
+ snprintf(info, INFO_SIZE, "Sometimes corpses become spoiled or inedible, resulting in sickness. Also, some");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "monsters' flesh is less palatable than others'. While sick, your hitpoints will");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "not regenerate, and sickness often takes a toll on your body. It wears off with");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "time (wait with <w>5<magenta>) or, if you've got the means, you could heal yourself.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case TUT_YOU_POISON:
+ learned_something_new(TUT_YOU_ENCHANTED);
+ snprintf(info, INFO_SIZE, "Poison will slowly reduce your hp. It wears off with time (wait with <w>5<magenta>) or, if");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "you've got the means, you could heal yourself.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ case TUT_YOU_CURSED:
+ snprintf(info2, INFO2_SIZE, "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 to remove the curse.");
+ mpr(info2, MSGCH_TUTORIAL);
+ break;
+ case TUT_YOU_HUNGRY:
+ snprintf(info, INFO_SIZE, "There are two ways to overcome hunger: food rations you started with or found,");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "and selfmade food from corpses. To get the latter, all you need to do is chop");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "up a corpse, that is, <w>D<magenta>issect it while standing on the corpse in question. You");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "need to wield a sharp weapon to do that. Your starting weapon will do nicely.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ case TUT_YOU_STARVING:
+ snprintf(info, INFO_SIZE, "You are now suffering from terrible hunger. You'll need to <w>e<magenta>at something");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "quickly, or you'll starve. The safest way to deal with this is to simply eat something from");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "your inventory rather than wait for a monster to leave a corpse.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ case TUT_MULTI_PICKUP:
+ snprintf(info, INFO_SIZE, "There's a more comfortable way to pick up several items at the same time. If");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "you press <w>,<magenta> or <w>g<magenta> twice you can choose items from a menu. This takes");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "less keystrokes but has no influence on the number of turns needed. Multi-pickup will be");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "interrupted by a monster attacking you or similar potentially dangerous events.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ case TUT_HEAVY_LOAD:
+ if (you.burden_state != BS_UNENCUMBERED)
+ snprintf(info, INFO_SIZE, "It is not usually a good idea to run around encumbered; it slows you down and increases your hunger.");
+ else
+ snprintf(info, INFO_SIZE, "Sadly, your inventory is limited to 52 items, and it appears your knapsack is full.");
+ mpr(info, MSGCH_TUTORIAL);
+
+ snprintf(info, INFO_SIZE, "However, this is easy enough to rectify: simply <w>d<magenta>rop some of the stuff you");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "don't need or that's too heavy to lug around permanently.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ case TUT_ROTTEN_FOOD:
+ snprintf(info, INFO_SIZE, "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 <w>d<magenta>rop them now.");
+ mpr(info, MSGCH_TUTORIAL);
+ if (you.religion == GOD_TROG || you.religion == GOD_MAKHLEB || you.religion == GOD_OKAWARU)
+ {
+ snprintf(info, INFO_SIZE, "Also, if it is a rotting corpse you carry now might be a good time to <w>d<magenta>rop");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "and <w>D<magenta>issect it during prayer (<w>p<magenta>) as an offer to %s.", god_name(you.religion));
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ }
+ break;
+ case TUT_MAKE_CHUNKS:
+ snprintf(info, INFO_SIZE, "How lucky! You just killed a monster which left a corpse. Standing over the");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "corpse, you can press <w>D<magenta> to dissect it. One or more chunks will appear, which");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "can be eaten with <w>e<magenta>.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "Be warned that not all chunks are edible, some never, others only sometimes. Only experience will "
+ "help you, here. But remember that you are what you eat.");
+ mpr(info, MSGCH_TUTORIAL);
+
+ if (you.duration[DUR_PRAYER] &&
+ (you.religion == GOD_OKAWARU || you.religion == GOD_MAKHLEB || you.religion == GOD_TROG || you.religion == GOD_ELYVILON))
+ {
+ snprintf(info, INFO_SIZE, "Note that doing this while praying will instead sacrifice it to %s, ", god_name(you.religion));
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "which a god may or may not like. (Type <w>^<magenta> to get a hint about that.)");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ }
+ break;
+ case TUT_SHIFT_RUN:
+ snprintf(info, INFO_SIZE, "Walking around takes less keystrokes if you press <w>Shift<magenta> while moving. That will");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "let you run into the specified direction until a monster comes into sight or your character "
+ "sees something interesting. Shift-running does not reduce the number of turns needed to move.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ case TUT_MAP_VIEW:
+ snprintf(info, INFO_SIZE, "As you explore more and more of a level, orientation becomes rather difficult.");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "Here the map overview comes in handy: Press <w>X<magenta> to see a much larger portion of");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "the map. You can easily return to the main screen with a number of keys. If you");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "move your cursor while on the level map and then press <w>.<magenta> your character will");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "attempt to move there on his own. Travel will be interrupted by monsters");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "stepping into sight, but can be resumed by pressing <w>X.<magenta> once more. Also, <w><<<magenta> and <w>><magenta>");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "can be used as shortcuts for cycling through the known staircases.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+/*
+ case TUT_AUTOEXPLORE:
+ snprintf(info2, INFO2_SIZE, "Exploration of a level gets even easier by another shortcut command: <w>Ctrl-O<magenta> will automatically move towards the "
+ "nearest unexplored part of the map, but stop whenever you find something interesting or a monster moves into sight.");
+ break;
+*/
+ case TUT_DONE_EXPLORE:
+ snprintf(info, INFO_SIZE, "You have explored the dungeon on this level. Search for some downstairs, which");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "look like this: '<w>><magenta>'. In order to descend, press <w>><magenta> when standing on such a");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "staircase.");
+ mpr(info, MSGCH_TUTORIAL);
+ if (Options.tutorial_events[TUT_SEEN_STAIRS])
+ {
+ snprintf(info, INFO_SIZE, "In rare cases, you may have found no downstairs at all. Try searching for");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "secret doors in suspicious looking spots; use <w>s<magenta>, <w>.<magenta> or <w>5<magenta> to do so.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ }
+ else
+ {
+ snprintf(info, INFO_SIZE, "Each level of Crawl has at least three up and three down stairs. If you've");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "encountered less than that, you've explored only part of this level. The rest");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "may be accessible via another level or through secret doors. To find the");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "latter, try searching the walls with <w>s<magenta>, <w>.<magenta> (search for one turn) or <w>5<magenta> (search");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "longer).");
+ mpr(info, MSGCH_TUTORIAL);
+ }
+ break;
+ case TUT_NEED_HEALING:
+ snprintf(info, INFO_SIZE, "If you're low on hitpoints or magic and there's no urgent need to move, you can");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "rest for a bit. Press <w>5<magenta> or <w>shift-numpad-5<magenta> to do so.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case TUT_NEED_POISON_HEALING:
+// 1 5 10 5 20 5 30 5 40 5 50 5 60 5 70 5 80
+ snprintf(info, INFO_SIZE, "Your poisoning could easily kill you, so now would be a good time to <w>q<magenta>uaff a");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "potion of heal wounds, or better yet, a potion of healing. If you haven't seen");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "one of these so far, try unknown ones in your inventory. Good luck!");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ case TUT_POSTBERSERK:
+ snprintf(info, INFO_SIZE, "Berserking is extremely exhausting! It burns a lot of nutrition, and afterwards");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "you are slowed down and occasionally even pass out.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ case TUT_RUN_AWAY:
+ snprintf(info2, INFO2_SIZE, "Sometimes, when you've got only few hitpoints left and you're in danger of dying, retreat might be "
+ "the best option. %sTry to shake off any monster following you by leading them through corridors, or once there's some space between you "
+ "and your pursuer, change levels.", (you.species == SP_CENTAUR ? "As a four-legged centaur you are particularly well equipped "
+ "for doing so. " : "") );
+ mpr(info2, MSGCH_TUTORIAL);
+ if (Options.tutorial_type == TUT_BERSERK_CHAR && !you.berserker)
+ {
+ snprintf(info, INFO_SIZE, "Also, with %s's support you can use your Berserk ability (<w>a<magenta>) to ", god_name(you.religion));
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "temporarily gain more hitpoints and greater strength. Be warned that going into");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "a frenzy like that also has its drawbacks.");
+ mpr(info, MSGCH_TUTORIAL);
+ }
+ break;
+ case TUT_YOU_MUTATED:
+ snprintf(info2, INFO_SIZE, "Mutations can be obtained from several sources, among them potions of "
+ "mutations, spell miscasts, and overuse of strong enchantments like Haste or "
+ "Invisibility. The only reliable way to get rid of mutations is with potions of "
+ "cure mutation. Almost all mutations occur in three degrees. There are about as "
+ "many harmful as beneficial mutations.");
+ mpr(info2, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "To have a look at your mutations press <w>A<magenta>.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ break;
+ case TUT_NEW_ABILITY:
+ snprintf(info, INFO_SIZE, "You just gained a new ability. Press <w>a<magenta> to take a look at your abilities or use");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "one of them.");
+ mpr(info, MSGCH_TUTORIAL);
+ break;
+ case TUT_WIELD_WEAPON:
+ snprintf(info, INFO_SIZE, "You might want to <w>w<magenta>ield a more suitable implement when attacking monsters.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ if (item_cursed( you.inv[ you.equip[EQ_WEAPON] ] ))
+ {
+ snprintf(info, INFO_SIZE, "To get rid of the curse you need to read a scroll of remove curse or enchant weapon.");
+ mpr(info, MSGCH_TUTORIAL);
+ }
+ else if (Options.tutorial_type == TUT_RANGER_CHAR && you.inv[ you.equip[EQ_WEAPON] ].sub_type == WPN_BOW)
+ {
+// 1 5 10 5 20 5 30 5 40 5 50 5 60 5 70 5 80
+ snprintf(info, INFO_SIZE, "You can easily switch between your primary (a) and secondary (b) weapon by ");
+ mpr(info, MSGCH_TUTORIAL);
+ snprintf(info, INFO_SIZE, "pressing <w>'<magenta>.");
+ formatted_mpr(formatted_string::parse_string(info), MSGCH_TUTORIAL);
+ }
+ break;
+ default:
+// 1 5 10 5 20 5 30 5 40 5 50 5 60 5 70 5 80
+ snprintf(info, INFO_SIZE, "You've found something new (but I don't know what)!");
+ mpr(info, MSGCH_TUTORIAL);
+ }
+ more();
+
+ Options.tut_just_triggered = true;
+
+ Options.tutorial_events[seen_what] = 0;
+ Options.tutorial_left--;
+
+ // there are so many triggers out there, chances that all will be called are small
+/* if (Options.tutorial_left < 10)
+ tutorial_finished(); */
+}
diff --git a/crawl-ref/source/tutorial.h b/crawl-ref/source/tutorial.h
new file mode 100644
index 0000000000..7c2029f07c
--- /dev/null
+++ b/crawl-ref/source/tutorial.h
@@ -0,0 +1,100 @@
+/*
+ * File: tutorial.h
+ * Summary: Stuff needed for tutorial
+ * Written by: JPEG
+ *
+ * Created on 2007-01-11.
+ */
+
+#ifndef TUTORIAL_H
+#define TUTORIAL_H
+
+// for formatted_string
+#include "menu.h"
+
+#include <string>
+#include <vector>
+#include <stdio.h>
+#include <stdlib.h>
+
+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);
+
+enum tutorial_event
+{
+ TUT_SEEN_FIRST_OBJECT, // 0
+ /* seen certain items */
+ TUT_SEEN_POTION,
+ TUT_SEEN_SCROLL,
+ TUT_SEEN_WAND,
+ TUT_SEEN_SPBOOK,
+ TUT_SEEN_JEWELLERY, // 5
+ TUT_SEEN_MISC,
+ TUT_SEEN_WEAPON,
+ TUT_SEEN_MISSILES,
+ TUT_SEEN_ARMOUR,
+ TUT_SEEN_RANDART, // 10
+ TUT_SEEN_FOOD,
+ TUT_SEEN_CARRION,
+ TUT_SEEN_STAIRS,
+ TUT_SEEN_TRAPS,
+ TUT_SEEN_ALTAR, // 15
+ TUT_SEEN_SHOP, // not tested so far
+ TUT_SEEN_DOOR,
+ /* other 'first events */
+ TUT_SEEN_MONSTER,
+ TUT_KILLED_MONSTER,
+ TUT_NEW_LEVEL, // 20
+ TUT_SKILL_RAISE,
+ TUT_YOU_ENCHANTED,
+ TUT_YOU_SICK,
+ TUT_YOU_POISON,
+ TUT_YOU_CURSED, // 25
+ TUT_YOU_HUNGRY,
+ TUT_YOU_STARVING,
+ TUT_MULTI_PICKUP,
+ TUT_HEAVY_LOAD,
+ TUT_ROTTEN_FOOD, // 30
+ TUT_NEED_HEALING,
+ TUT_NEED_POISON_HEALING,
+ TUT_RUN_AWAY,
+ TUT_MAKE_CHUNKS,
+ TUT_POSTBERSERK, // 35
+ TUT_SHIFT_RUN,
+ TUT_MAP_VIEW,
+// TUT_AUTOEXPLORE,
+ TUT_DONE_EXPLORE,
+ TUT_YOU_MUTATED, // 39
+ TUT_NEW_ABILITY,
+ TUT_WIELD_WEAPON,
+ TUT_EVENTS_NUM // 42
+}; // 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
+};
+
+#endif
diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc
index afc0c56bb1..fef73784fc 100644
--- a/crawl-ref/source/view.cc
+++ b/crawl-ref/source/view.cc
@@ -53,6 +53,7 @@
#include "spells4.h"
#include "stash.h"
#include "travel.h"
+#include "tutorial.h"
// These are hidden from the rest of the world... use the functions
// below to get information about the map grid.
@@ -1016,7 +1017,8 @@ bool get_bit_in_long_array( const unsigned long* data, int where )
return ((data[wordloc] & (1UL << bitloc)) != 0);
}
-static void set_bit_in_long_array( unsigned long* data, int where ) {
+static void set_bit_in_long_array( unsigned long* data, int where )
+{
int wordloc = where / LONGSIZE;
int bitloc = where % LONGSIZE;
data[wordloc] |= (1UL << bitloc);
@@ -1053,13 +1055,16 @@ static int find_next_intercept(double* accx, double* accy, const double slope)
const double distdiff = (xdistance * slope - ydistance);
// exact corner
- if ( double_is_zero( distdiff ) ) {
+ if ( double_is_zero( distdiff ) )
+ {
// move somewhat away from the corner
- if ( slope > 1.0 ) {
+ if ( slope > 1.0 )
+ {
*accx = xtarget + EPSILON_VALUE * 2;
*accy = ytarget + EPSILON_VALUE * 2 * slope;
}
- else {
+ else
+ {
*accx = xtarget + EPSILON_VALUE * 2 / slope;
*accy = ytarget + EPSILON_VALUE * 2;
}
@@ -1068,11 +1073,13 @@ static int find_next_intercept(double* accx, double* accy, const double slope)
double traveldist;
int rc = -1;
- if ( distdiff > 0.0 ) {
+ if ( distdiff > 0.0 )
+ {
traveldist = ydistance / slope;
rc = 1;
}
- else {
+ else
+ {
traveldist = xdistance;
rc = 0;
}
@@ -1087,7 +1094,8 @@ static int find_next_intercept(double* accx, double* accy, const double slope)
void ray_def::advance_and_bounce()
{
// 0 = down-right, 1 = down-left, 2 = up-left, 3 = up-right
- int bouncequad[4][3] = {
+ int bouncequad[4][3] =
+ {
{ 1, 3, 2 }, { 0, 2, 3 }, { 3, 1, 0 }, { 2, 0, 1 }
};
int oldx = x(), oldy = y();
@@ -1425,15 +1433,17 @@ void raycast()
// of x/y: in that case, every step on the X axis means an increase
// of 1 in the Y axis at the intercept point. We can assume gcd(x,y)=1,
// so we look at steps of 1/y.
- for ( xangle = 1; xangle <= LOS_MAX_RANGE; ++xangle ) {
- for ( yangle = 1; yangle <= LOS_MAX_RANGE; ++yangle ) {
-
+ for ( xangle = 1; xangle <= LOS_MAX_RANGE; ++xangle )
+ {
+ for ( yangle = 1; yangle <= LOS_MAX_RANGE; ++yangle )
+ {
if ( gcd(xangle, yangle) != 1 )
continue;
const double slope = ((double)(yangle)) / xangle;
const double rslope = ((double)(xangle)) / yangle;
- for ( int intercept = 0; intercept <= yangle; ++intercept ) {
+ for ( int intercept = 0; intercept <= yangle; ++intercept )
+ {
double xstart = ((double)(intercept)) / yangle;
if ( intercept == 0 )
xstart += EPSILON_VALUE / 10.0;
@@ -1544,12 +1554,14 @@ bool find_ray( int sourcex, int sourcey, int targetx, int targety,
}
}
}
- if ( allow_fallback ) {
+ if ( allow_fallback )
+ {
ray.accx = sourcex + 0.5;
ray.accy = sourcey + 0.5;
if ( targetx == sourcex )
ray.slope = 10000.0;
- else {
+ else
+ {
ray.slope = targety - sourcey;
ray.slope /= targetx - sourcex;
if ( ray.slope < 0 )
@@ -1612,7 +1624,8 @@ void losight(FixedArray < unsigned int, 19, 19 > &sh,
const unsigned int num_cellrays = compressed_ray_x.size();
const unsigned int num_words = (num_cellrays + LONGSIZE - 1) / LONGSIZE;
- for ( int quadrant = 0; quadrant < 4; ++quadrant ) {
+ for ( int quadrant = 0; quadrant < 4; ++quadrant )
+ {
const int xmult = quadrant_x[quadrant];
const int ymult = quadrant_y[quadrant];
@@ -1621,9 +1634,11 @@ void losight(FixedArray < unsigned int, 19, 19 > &sh,
// kill all blocked rays
const unsigned long* inptr = los_blockrays;
- for ( int xdiff = 0; xdiff <= LOS_MAX_RANGE_X; ++xdiff ) {
+ for ( int xdiff = 0; xdiff <= LOS_MAX_RANGE_X; ++xdiff )
+ {
for (int ydiff = 0; ydiff <= LOS_MAX_RANGE_Y;
- ++ydiff, inptr += num_words ) {
+ ++ydiff, inptr += num_words )
+ {
const int realx = x_p + xdiff * xmult;
const int realy = y_p + ydiff * ymult;
@@ -1632,7 +1647,8 @@ void losight(FixedArray < unsigned int, 19, 19 > &sh,
continue;
// if this cell is opaque...
- if ( grid_is_opaque(gr[realx][realy])) {
+ if ( grid_is_opaque(gr[realx][realy]))
+ {
// then block the appropriate rays
for ( unsigned int i = 0; i < num_words; ++i )
dead_rays[i] |= inptr[i];
@@ -1643,12 +1659,15 @@ void losight(FixedArray < unsigned int, 19, 19 > &sh,
// ray calculation done, now work out which cells in this
// quadrant are visible
unsigned int rayidx = 0;
- for ( unsigned int wordloc = 0; wordloc < num_words; ++wordloc ) {
+ for ( unsigned int wordloc = 0; wordloc < num_words; ++wordloc )
+ {
const unsigned long curword = dead_rays[wordloc];
// Note: the last word may be incomplete
- for ( unsigned int bitloc = 0; bitloc < LONGSIZE; ++bitloc) {
+ for ( unsigned int bitloc = 0; bitloc < LONGSIZE; ++bitloc)
+ {
// make the cells seen by this ray at this point visible
- if ( ((curword >> bitloc) & 1UL) == 0 ) {
+ if ( ((curword >> bitloc) & 1UL) == 0 )
+ {
// this ray is alive!
const int realx = xmult * compressed_ray_x[rayidx];
const int realy = ymult * compressed_ray_y[rayidx];
@@ -1705,7 +1724,8 @@ void draw_border(void)
// 3. '^' for traps
// 4. '_' for altars
// 5. Anything else will look for the exact same character in the level map.
-bool is_feature(int feature, int x, int y) {
+bool is_feature(int feature, int x, int y)
+{
unsigned char envfeat = (unsigned char) env.map[x - 1][y - 1];
if (!envfeat)
return false;
@@ -1714,7 +1734,8 @@ bool is_feature(int feature, int x, int y) {
// warnings about out-of-range case values.
short grid = grd[x][y];
- switch (feature) {
+ switch (feature)
+ {
case 'X':
return (travel_point_distance[x][y] == PD_EXCLUDED);
case 'F':
@@ -1723,7 +1744,8 @@ bool is_feature(int feature, int x, int y) {
case 'I':
return is_stash(x, y);
case '_':
- switch (grid) {
+ switch (grid)
+ {
case DNGN_ALTAR_ZIN:
case DNGN_ALTAR_SHINING_ONE:
case DNGN_ALTAR_KIKUBAAQUDGHA:
@@ -1743,7 +1765,8 @@ bool is_feature(int feature, int x, int y) {
}
case '\t':
case '\\':
- switch (grid) {
+ switch (grid)
+ {
case DNGN_ENTER_HELL:
case DNGN_ENTER_LABYRINTH:
case DNGN_ENTER_SHOP:
@@ -1764,7 +1787,8 @@ bool is_feature(int feature, int x, int y) {
return false;
}
case '<':
- switch (grid) {
+ switch (grid)
+ {
case DNGN_ROCK_STAIRS_UP:
case DNGN_STONE_STAIRS_UP_I:
case DNGN_STONE_STAIRS_UP_II:
@@ -1786,7 +1810,8 @@ bool is_feature(int feature, int x, int y) {
return false;
}
case '>':
- switch (grid) {
+ switch (grid)
+ {
case DNGN_ROCK_STAIRS_DOWN:
case DNGN_STONE_STAIRS_DOWN_I:
case DNGN_STONE_STAIRS_DOWN_II:
@@ -1808,7 +1833,8 @@ bool is_feature(int feature, int x, int y) {
return false;
}
case '^':
- switch (grid) {
+ switch (grid)
+ {
case DNGN_TRAP_MECHANICAL:
case DNGN_TRAP_MAGICAL:
case DNGN_TRAP_III:
@@ -1823,7 +1849,8 @@ bool is_feature(int feature, int x, int y) {
static int find_feature(unsigned char feature, int curs_x, int curs_y,
int start_x, int start_y, int anchor_x, int anchor_y,
- int ignore_count, char *move_x, char *move_y) {
+ int ignore_count, char *move_x, char *move_y)
+{
int cx = anchor_x,
cy = anchor_y;
@@ -1832,30 +1859,38 @@ static int find_feature(unsigned char feature, int curs_x, int curs_y,
// Find the first occurrence of feature 'feature', spiralling around (x,y)
int maxradius = GXM > GYM? GXM : GYM;
- for (int radius = 1; radius < maxradius; ++radius) {
- for (int axis = -2; axis < 2; ++axis) {
+ for (int radius = 1; radius < maxradius; ++radius)
+ {
+ for (int axis = -2; axis < 2; ++axis)
+ {
int rad = radius - (axis < 0);
- for (int var = -rad; var <= rad; ++var) {
+ for (int var = -rad; var <= rad; ++var)
+ {
int dx = radius, dy = var;
if (axis % 2)
dx = -dx;
- if (axis < 0) {
+ if (axis < 0)
+ {
int temp = dx;
dx = dy;
dy = temp;
}
int x = cx + dx, y = cy + dy;
- if (x < 0 || y < 0 || x >= GXM || y >= GYM) continue;
- if (is_feature(feature, x + 1, y + 1)) {
+ if (x < 0 || y < 0 || x >= GXM || y >= GYM)
+ continue;
+ if (is_feature(feature, x + 1, y + 1))
+ {
++matchcount;
- if (!ignore_count--) {
+ if (!ignore_count--)
+ {
// We want to cursor to (x,y)
*move_x = x - (start_x + curs_x - 1);
*move_y = y - (start_y + curs_y - 1);
return matchcount;
}
- else if (firstx == -1) {
+ else if (firstx == -1)
+ {
firstx = x;
firsty = y;
}
@@ -1865,7 +1900,8 @@ static int find_feature(unsigned char feature, int curs_x, int curs_y,
}
// We found something, but ignored it because of an ignorecount
- if (firstx != -1) {
+ if (firstx != -1)
+ {
*move_x = firstx - (start_x + curs_x - 1);
*move_y = firsty - (start_y + curs_y - 1);
return 1;
@@ -1874,8 +1910,10 @@ static int find_feature(unsigned char feature, int curs_x, int curs_y,
}
void find_features(const std::vector<coord_def>& features,
- unsigned char feature, std::vector<coord_def> *found) {
- for (unsigned feat = 0; feat < features.size(); ++feat) {
+ unsigned char feature, std::vector<coord_def> *found)
+{
+ for (unsigned feat = 0; feat < features.size(); ++feat)
+ {
const coord_def& coord = features[feat];
if (is_feature(feature, coord.x, coord.y))
found->push_back(coord);
@@ -1887,22 +1925,27 @@ static int find_feature( const std::vector<coord_def>& features,
int start_x, int start_y,
int ignore_count,
char *move_x, char *move_y,
- bool forward) {
+ bool forward)
+{
int firstx = -1, firsty = -1, firstmatch = -1;
int matchcount = 0;
- for (unsigned feat = 0; feat < features.size(); ++feat) {
+ for (unsigned feat = 0; feat < features.size(); ++feat)
+ {
const coord_def& coord = features[feat];
- if (is_feature(feature, coord.x, coord.y)) {
+ if (is_feature(feature, coord.x, coord.y))
+ {
++matchcount;
- if (forward? !ignore_count-- : --ignore_count == 1) {
+ if (forward? !ignore_count-- : --ignore_count == 1)
+ {
// We want to cursor to (x,y)
*move_x = coord.x - (start_x + curs_x);
*move_y = coord.y - (start_y + curs_y);
return matchcount;
}
- else if (!forward || firstx == -1) {
+ else if (!forward || firstx == -1)
+ {
firstx = coord.x;
firsty = coord.y;
firstmatch = matchcount;
@@ -1911,7 +1954,8 @@ static int find_feature( const std::vector<coord_def>& features,
}
// We found something, but ignored it because of an ignorecount
- if (firstx != -1) {
+ if (firstx != -1)
+ {
*move_x = firstx - (start_x + curs_x);
*move_y = firsty - (start_y + curs_y);
return firstmatch;
@@ -2295,11 +2339,13 @@ void show_map( FixedVector<int, 2> &spec_place, bool travel_mode )
move_x = 0;
move_y = 0;
- if (anchor_x == -1) {
+ if (anchor_x == -1)
+ {
anchor_x = start_x + curs_x - 1;
anchor_y = start_y + curs_y - 1;
}
- if (search_feat != getty) {
+ if (search_feat != getty)
+ {
search_feat = getty;
search_found = 0;
}
@@ -2325,13 +2371,15 @@ void show_map( FixedVector<int, 2> &spec_place, bool travel_mode )
int x = start_x + curs_x, y = start_y + curs_y;
if (travel_mode && x == you.x_pos && y == you.y_pos)
{
- if (you.travel_x > 0 && you.travel_y > 0) {
+ if (you.travel_x > 0 && you.travel_y > 0)
+ {
move_x = you.travel_x - x;
move_y = you.travel_y - y;
}
break;
}
- else {
+ else
+ {
spec_place[0] = x;
spec_place[1] = y;
map_alive = false;
@@ -3168,7 +3216,7 @@ void init_feature_table( void )
}
}
-static int get_screen_glyph( int x, int y )
+int get_screen_glyph( int x, int y )
{
const int ex = x - you.x_pos + 9;
const int ey = y - you.y_pos + 9;
@@ -3357,6 +3405,18 @@ void viewwindow(bool draw_it, bool do_updates)
const int gx = count_x + you.x_pos - 16;
const int gy = count_y + you.y_pos - 8;
+ if (Options.tutorial_left && in_bounds(gx, gy))
+ {
+ if (is_feature('>',gx,gy))
+ learned_something_new(TUT_SEEN_STAIRS);
+ else if (is_feature('_',gx,gy))
+ learned_something_new(TUT_SEEN_ALTAR);
+ else if (grd[gx][gy] == DNGN_CLOSED_DOOR)
+ learned_something_new(TUT_SEEN_DOOR,gx,gy);
+ else if (grd[gx][gy] == DNGN_ENTER_SHOP)
+ learned_something_new(TUT_SEEN_SHOP,gx,gy);
+ }
+
// order is important here
if (!map_bounds( gx, gy ))
{
diff --git a/crawl-ref/source/view.h b/crawl-ref/source/view.h
index bd7e63ecf0..9b9f0f34c3 100644
--- a/crawl-ref/source/view.h
+++ b/crawl-ref/source/view.h
@@ -116,6 +116,7 @@ void get_item_glyph(const item_def *item, unsigned short *glych,
unsigned short *glycol);
void get_mons_glyph(const monsters *mons, unsigned short *glych,
unsigned short *glycol);
+int get_screen_glyph( int x, int y );
void set_envmap_char( int x, int y, unsigned char chr );
unsigned get_envmap_char(int x, int y);