summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
authordshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2007-03-25 13:23:23 +0000
committerdshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2007-03-25 13:23:23 +0000
commitbdb45bf27b86ff63c5afdff311b4065c83faf37d (patch)
treecf3a914532ecc372932b8d897e8c9abf57fbe2a4 /crawl-ref
parentc500c6b3582877a10bd362722bde81eb9b94b917 (diff)
downloadcrawl-ref-bdb45bf27b86ff63c5afdff311b4065c83faf37d.tar.gz
crawl-ref-bdb45bf27b86ff63c5afdff311b4065c83faf37d.zip
Merged in crawl.akrasiac.org patches (simple messaging, milestones). These take
effect only if compiled with -DDGAMELAUNCH. Simple messaging: interacts with dgamelaunch's messaging facility allowing viewers to send messages to the player. Milestones: Writes a milestones.txt file (in xlogfile format) for things like the player killing uniques, reaching the end of a dungeon branch, etc. (similar to notes). milestones.txt is used for game announcements by an IRC bot. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1095 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref')
-rw-r--r--crawl-ref/source/AppHdr.h19
-rw-r--r--crawl-ref/source/acr.cc163
-rw-r--r--crawl-ref/source/beam.cc17
-rw-r--r--crawl-ref/source/decks.cc5
-rw-r--r--crawl-ref/source/describe.cc6
-rw-r--r--crawl-ref/source/dungeon.cc5
-rw-r--r--crawl-ref/source/effects.cc18
-rw-r--r--crawl-ref/source/effects.h2
-rw-r--r--crawl-ref/source/enum.h2
-rw-r--r--crawl-ref/source/externs.h28
-rw-r--r--crawl-ref/source/fight.cc3
-rw-r--r--crawl-ref/source/files.cc14
-rw-r--r--crawl-ref/source/files.h6
-rw-r--r--crawl-ref/source/hiscores.cc103
-rw-r--r--crawl-ref/source/hiscores.h11
-rw-r--r--crawl-ref/source/initfile.cc22
-rw-r--r--crawl-ref/source/items.cc29
-rw-r--r--crawl-ref/source/misc.cc13
-rw-r--r--crawl-ref/source/misc.h2
-rw-r--r--crawl-ref/source/mon-util.cc12
-rw-r--r--crawl-ref/source/monplace.cc8
-rw-r--r--crawl-ref/source/monstuff.cc31
-rw-r--r--crawl-ref/source/mstuff2.cc2
-rw-r--r--crawl-ref/source/notes.cc37
-rw-r--r--crawl-ref/source/notes.h1
-rw-r--r--crawl-ref/source/output.cc14
-rw-r--r--crawl-ref/source/output.h4
-rw-r--r--crawl-ref/source/player.cc7
-rw-r--r--crawl-ref/source/religion.cc2
-rw-r--r--crawl-ref/source/spl-cast.cc10
-rw-r--r--crawl-ref/source/stuff.cc3
31 files changed, 504 insertions, 95 deletions
diff --git a/crawl-ref/source/AppHdr.h b/crawl-ref/source/AppHdr.h
index 45849093a7..24fe957646 100644
--- a/crawl-ref/source/AppHdr.h
+++ b/crawl-ref/source/AppHdr.h
@@ -209,6 +209,18 @@
// parsers.
#define DGL_EXTENDED_LOGFILES
+ // Basic messaging for dgamelaunch, based on SIMPLE_MAIL for
+ // NetHack and Slash'EM. I'm calling this "messaging" because that's
+ // closer to reality.
+ #define SIMPLE_MESSAGING
+
+ // How often we check for messages. This is not once per turn, but once
+ // per player-input. Message checks are not performed if the keyboard
+ // buffer is full, so messages should not interrupt macros.
+ #define MESSAGE_CHECK_INTERVAL 1
+
+ // Record game milestones in an xlogfile.
+ #define MILESTONES
#endif
// =========================================================================
@@ -367,11 +379,8 @@
#endif
-// ===========================================================================
-// Misc
-// ===========================================================================
-#if HAS_NAMESPACES
- using namespace std;
+#if defined(SIMPLE_MESSAGING) && !defined(USE_FILE_LOCKING)
+# error Must define USE_FILE_LOCKING for SIMPLE_MESSAGING
#endif
// Uncomment these if you can't find these functions on your system
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index 3fa7142d3c..5987d4d0d0 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -166,6 +166,10 @@ static command_type get_next_cmd();
static keycode_type get_next_keycode();
static command_type keycode_to_command( keycode_type key );
+#ifdef SIMPLE_MESSAGING
+static void read_messages();
+#endif
+
/*
It all starts here. Some initialisations are run first, then straight to
new_game and then input.
@@ -980,6 +984,14 @@ static void go_downstairs()
return;
}
+#ifdef MILESTONES
+ // Not entirely accurate - the player could die before reaching the Abyss.
+ if (grd[you.x_pos][you.y_pos] == DNGN_ENTER_ABYSS)
+ mark_milestone("abyss.enter", "entered the Abyss!");
+ else if (grd[you.x_pos][you.y_pos] == DNGN_EXIT_ABYSS)
+ mark_milestone("abyss.exit", "escaped from the Abyss!");
+#endif
+
tag_followers(); // only those beside us right now can follow
start_delay( DELAY_DESCENDING_STAIRS,
1 + (you.burden_state > BS_UNENCUMBERED),
@@ -1109,6 +1121,13 @@ void process_command( command_type cmd )
make_user_note();
break;
+ case CMD_READ_MESSAGES:
+#ifdef SIMPLE_MESSAGING
+ if (SysEnv.have_messages)
+ read_messages();
+#endif
+ break;
+
case CMD_CLEAR_MAP:
if (you.level_type != LEVEL_LABYRINTH &&
you.level_type != LEVEL_ABYSS)
@@ -2145,12 +2164,12 @@ static void check_banished()
if (you.banished)
{
you.banished = false;
-
if (you.level_type != LEVEL_ABYSS)
{
mpr("You are cast into the Abyss!");
- banished(DNGN_ENTER_ABYSS);
+ banished(DNGN_ENTER_ABYSS, you.banished_by);
}
+ you.banished_by.clear();
}
}
@@ -2349,8 +2368,147 @@ static void world_reacts()
return;
}
+#ifdef SIMPLE_MESSAGING
+
+static struct stat mfilestat;
+
+static void show_message_line(std::string line)
+{
+ const std::string::size_type sender_pos = line.find(":");
+ if (sender_pos == std::string::npos)
+ mpr(line.c_str());
+ else
+ {
+ std::string sender = line.substr(0, sender_pos);
+ line = line.substr(sender_pos + 1);
+ trim_string(line);
+ // XXX: Eventually fix mpr so it can do a different colour for
+ // the sender.
+ mprf("%s: %s", sender.c_str(), line.c_str());
+ }
+}
+
+static void read_each_message()
+{
+ bool say_got_msg = true;
+ FILE *mf = fopen(SysEnv.messagefile.c_str(), "r+");
+ if (!mf)
+ {
+ mprf(MSGCH_WARN, "Couldn't read %s: %s", SysEnv.messagefile.c_str(),
+ strerror(errno));
+ goto kill_messaging;
+ }
+
+ // Read messages, code borrowed from the SIMPLEMAIL patch.
+ char line[120];
+
+ if (!lock_file_handle(mf, F_RDLCK))
+ {
+ mprf(MSGCH_WARN, "Failed to lock %s: %s", SysEnv.messagefile.c_str(),
+ strerror(errno));
+ goto kill_messaging;
+ }
+
+ while (fgets(line, sizeof line, mf))
+ {
+ unlock_file_handle(mf);
+
+ const int len = strlen(line);
+ if (len)
+ {
+ if (line[len - 1] == '\n')
+ line[len - 1] = 0;
+
+ if (say_got_msg)
+ {
+ mprf(MSGCH_PROMPT, "Your messages:");
+ say_got_msg = false;
+ }
+
+ show_message_line(line);
+ }
+
+ if (!lock_file_handle(mf, F_RDLCK))
+ {
+ mprf(MSGCH_WARN, "Failed to lock %s: %s",
+ SysEnv.messagefile.c_str(),
+ strerror(errno));
+ goto kill_messaging;
+ }
+ }
+ if (!lock_file_handle(mf, F_WRLCK))
+ {
+ mprf(MSGCH_WARN, "Unable to write lock %s: %s",
+ SysEnv.messagefile.c_str(),
+ strerror(errno));
+ }
+ if (!ftruncate(fileno(mf), 0))
+ mfilestat.st_mtime = 0;
+ unlock_file_handle(mf);
+ fclose(mf);
+
+ SysEnv.have_messages = false;
+ return;
+
+kill_messaging:
+ if (mf)
+ fclose(mf);
+ SysEnv.have_messages = false;
+ Options.messaging = false;
+}
+
+static void read_messages()
+{
+ read_each_message();
+ update_message_status();
+}
+
+static void announce_messages()
+{
+ // XXX: We could do a NetHack-like mail daemon here at some point.
+ mprf("Beep! Your pager goes off! Use _ to check your messages.");
+}
+
+static void check_messages()
+{
+ if (!Options.messaging
+ || SysEnv.have_messages
+ || SysEnv.messagefile.empty()
+ || kbhit()
+ || (SysEnv.message_check_tick++ % MESSAGE_CHECK_INTERVAL))
+ {
+ return;
+ }
+
+ const bool had_messages = SysEnv.have_messages;
+ struct stat st;
+ if (stat(SysEnv.messagefile.c_str(), &st))
+ {
+ mfilestat.st_mtime = 0;
+ return;
+ }
+
+ if (st.st_mtime > mfilestat.st_mtime)
+ {
+ if (st.st_size)
+ SysEnv.have_messages = true;
+ mfilestat.st_mtime = st.st_mtime;
+ }
+
+ if (SysEnv.have_messages && !had_messages)
+ {
+ announce_messages();
+ update_message_status();
+ }
+}
+#endif
+
static command_type get_next_cmd()
{
+#ifdef SIMPLE_MESSAGING
+ check_messages();
+#endif
+
#if DEBUG_DIAGNOSTICS
// save hunger at start of round
// for use with hunger "delta-meter" in output.cc
@@ -2439,6 +2597,7 @@ command_type keycode_to_command( keycode_type key )
case '%': return CMD_RESISTS_SCREEN;
case ',': return CMD_PICKUP;
case ':': return CMD_MAKE_NOTE;
+ case '_': return CMD_READ_MESSAGES;
case ';': return CMD_INSPECT_FLOOR;
case '!': return CMD_SHOUT;
case '^': return CMD_DISPLAY_RELIGION;
diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc
index 601019f7b3..c9c77dbb2a 100644
--- a/crawl-ref/source/beam.cc
+++ b/crawl-ref/source/beam.cc
@@ -71,6 +71,8 @@ static FixedArray < bool, 19, 19 > explode_map;
static void sticky_flame_monster( int mn, kill_category who, int hurt_final );
static bool affectsWall(const bolt &beam, int wall_feature);
static bool isBouncy(struct bolt &beam, unsigned char gridtype);
+static int beam_source(const bolt &beam);
+static std::string beam_zapper(const bolt &beam);
static void beam_drop_object( struct bolt &beam, item_def *item, int x, int y );
static bool beam_term_on_target(struct bolt &beam, int x, int y);
static void beam_explodes(struct bolt &beam, int x, int y);
@@ -2895,7 +2897,19 @@ static bool fuzz_invis_tracer(bolt &beem)
// 4.0.
bool test_beam_hit(int attack, int defence)
{
- return (random2(attack) >= random2avg(defence, 2));
+ return (attack == AUTOMATIC_HIT
+ || random2(attack) >= random2avg(defence, 2));
+}
+
+static std::string beam_zapper(const bolt &beam)
+{
+ const int bsrc = beam_source(beam);
+ if (bsrc == MHITYOU)
+ return ("self");
+ else if (bsrc == MHITNOT)
+ return ("");
+ else
+ return ptr_monam( &menv[bsrc], DESC_PLAIN );
}
// return amount of extra range used up by affectation of the player
@@ -3091,6 +3105,7 @@ static int affect_player( struct bolt &beam )
break;
}
you.banished = true;
+ you.banished_by = beam_zapper(beam);
beam.obvious_effect = true;
break; // banishment to the abyss
diff --git a/crawl-ref/source/decks.cc b/crawl-ref/source/decks.cc
index deb8b1629c..2167676223 100644
--- a/crawl-ref/source/decks.cc
+++ b/crawl-ref/source/decks.cc
@@ -512,16 +512,15 @@ static void cards(unsigned char which_card)
case CARD_GATE:
mpr("You have drawn the Gate!");
- more();
if (you.level_type == LEVEL_ABYSS)
- banished(DNGN_EXIT_ABYSS);
+ banished(DNGN_EXIT_ABYSS, "drew the Gate");
else if (you.level_type == LEVEL_LABYRINTH)
canned_msg(MSG_NOTHING_HAPPENS);
else
{
mpr("You are cast into the Abyss!");
- banished(DNGN_ENTER_ABYSS);
+ banished(DNGN_ENTER_ABYSS, "drew the Gate");
}
break;
diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc
index da5e0ee85f..05830e781b 100644
--- a/crawl-ref/source/describe.cc
+++ b/crawl-ref/source/describe.cc
@@ -6355,7 +6355,7 @@ std::string ghost_description(const monsters &mons, bool concise)
}
snprintf( tmp_buff, sizeof(tmp_buff),
- "%s the %s, a%s %s %s",
+ "%s the %s, a%s %s%s%s",
ghost.name.c_str(),
skill_title( ghost.values[GVAL_BEST_SKILL],
@@ -6375,7 +6375,9 @@ std::string ghost_description(const monsters &mons, bool concise)
( concise? get_species_abbrev(ghost.values[GVAL_SPECIES]) :
species_name( ghost.values[GVAL_SPECIES],
ghost.values[GVAL_EXP_LEVEL] ) ),
-
+
+ ( concise? "" : " " ),
+
( concise? get_class_abbrev(ghost.values[GVAL_CLASS]) :
get_class_name( ghost.values[GVAL_CLASS] ) ) );
diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index 5a1300406f..f5538c45ab 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -4736,11 +4736,8 @@ static int pick_unique(int lev)
(lev > 3) ? random_range(MONS_IJYB, MONS_EDMUND) :
random_range(MONS_TERENCE, MONS_SIGMUND));
- if (player_in_branch(BRANCH_VESTIBULE_OF_HELL)
- && one_chance_in(7))
- {
+ if (player_in_branch(BRANCH_VESTIBULE_OF_HELL) && one_chance_in(7))
which_unique = MONS_MURRAY;
- }
if (player_in_branch(BRANCH_HALL_OF_ZOT) && one_chance_in(3))
which_unique = MONS_TIAMAT;
diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc
index bd9e8452bf..5cbf2ed192 100644
--- a/crawl-ref/source/effects.cc
+++ b/crawl-ref/source/effects.cc
@@ -21,6 +21,7 @@
#include "beam.h"
#include "direct.h"
#include "dungeon.h"
+#include "hiscores.h"
#include "itemname.h"
#include "itemprop.h"
#include "items.h"
@@ -128,11 +129,24 @@ void torment(int caster, int tx, int ty)
apply_area_within_radius(torment_monsters, tx, ty, 0, 8, caster);
} // end torment()
-void banished(int gate_type)
+static std::string who_banished(const std::string &who)
{
+ return (who.empty()? who : " (" + who + ")");
+}
+
+void banished(int gate_type, const std::string &who)
+{
+#ifdef MILESTONES
+ if (gate_type == DNGN_ENTER_ABYSS)
+ mark_milestone("abyss.enter", who_banished(who));
+ else if (gate_type == DNGN_EXIT_ABYSS)
+ mark_milestone("abyss.exit", who_banished(who));
+#endif
+
if (gate_type == DNGN_ENTER_ABYSS)
{
- take_note(Note(NOTE_USER_NOTE, 0, 0, "Cast into the Abyss"), true);
+ const std::string what = "Cast into the Abyss" + who_banished(who);
+ take_note(Note(NOTE_USER_NOTE, 0, 0, what.c_str()), true);
}
down_stairs(true, you.your_level, gate_type); // heh heh
diff --git a/crawl-ref/source/effects.h b/crawl-ref/source/effects.h
index 59f663154b..c9817d2f4d 100644
--- a/crawl-ref/source/effects.h
+++ b/crawl-ref/source/effects.h
@@ -22,7 +22,7 @@
/* ***********************************************************************
* called from: ability - acr - beam - decks - fight - religion - spells
* *********************************************************************** */
-void banished(int gate_type);
+void banished(int gate_type, const std::string &who = "");
// last updated 12may2000 {dlb}
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index f574919992..c990b255bc 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -627,6 +627,8 @@ enum command_type
CMD_MAKE_NOTE,
CMD_RESISTS_SCREEN,
+ CMD_READ_MESSAGES,
+
/* overmap commands */
CMD_MAP_CLEAR_MAP,
CMD_MAP_ADD_WAYPOINT,
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index b474245858..f8fbda99a1 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -145,7 +145,7 @@ public:
virtual void go_berserk(bool intentional) = 0;
virtual void hurt(actor *attacker, int amount) = 0;
virtual void heal(int amount, bool max_too = false) = 0;
- virtual void banish() = 0;
+ virtual void banish(const std::string &who = "") = 0;
virtual void blink() = 0;
virtual void teleport(bool right_now = false, bool abyss_shift = false) = 0;
virtual void poison(actor *attacker, int amount = 1) = 0;
@@ -501,8 +501,9 @@ class player : public actor
public:
bool turn_is_over; // flag signaling that player has performed a timed action
- bool banished; // flag signaling that the player is due a visit to the
- // Abyss.
+ // If true, player is headed to the Abyss.
+ bool banished;
+ std::string banished_by;
bool just_autoprayed; // autopray just kicked in
@@ -731,7 +732,7 @@ public:
bool has_usable_claws() const;
item_def *weapon(int which_attack = -1);
item_def *shield();
-
+
std::string name(description_level_type type) const;
std::string pronoun(pronoun_type pro) const;
std::string conj_verb(const std::string &verb) const;
@@ -741,7 +742,7 @@ public:
void attacking(actor *other);
void go_berserk(bool intentional);
- void banish();
+ void banish(const std::string &who = "");
void blink();
void teleport(bool right_now = false, bool abyss_shift = false);
void drain_stat(int stat, int amount);
@@ -945,6 +946,7 @@ public:
item_def *weapon(int which_attack = -1);
item_def *shield();
std::string name(description_level_type type) const;
+ std::string name(description_level_type type, bool force_visible) const;
std::string pronoun(pronoun_type pro) const;
std::string conj_verb(const std::string &verb) const;
@@ -955,7 +957,7 @@ public:
void attacking(actor *other);
void go_berserk(bool intentional);
- void banish();
+ void banish(const std::string &who = "");
void expose_to_element(beam_type element, int strength = 0);
bool visible() const;
@@ -1119,8 +1121,14 @@ struct system_environment
char *crawl_pizza;
char *crawl_rc;
char *crawl_dir;
- char *home; // only used by MULTIUSER systems
- bool board_with_nail; // Easter Egg silliness
+ char *home; // only used by MULTIUSER systems
+ bool board_with_nail; // Easter Egg silliness
+
+#ifdef SIMPLE_MESSAGING
+ std::string messagefile; // File containing messages from other users.
+ bool have_messages; // There are messages waiting to be read.
+ unsigned message_check_tick;
+#endif
std::string scorefile;
};
@@ -1208,6 +1216,10 @@ public:
std::string player_name;
+#ifdef SIMPLE_MESSAGING
+ bool messaging; // Check for messages.
+#endif
+
bool autopickup_on;
bool autoprayer_on;
diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc
index d50d64d5b4..c2664ce6ba 100644
--- a/crawl-ref/source/fight.cc
+++ b/crawl-ref/source/fight.cc
@@ -1725,7 +1725,8 @@ bool melee_attack::apply_damage_brand()
if (you.level_type != LEVEL_ABYSS && coinflip())
{
emit_nodmg_hit_message();
- defender->banish();
+ defender->banish( atk? atk->name(DESC_PLAIN, true)
+ : attacker->name(DESC_PLAIN) );
return (true);
}
break;
diff --git a/crawl-ref/source/files.cc b/crawl-ref/source/files.cc
index 3beb2d137e..dc2b2dcbbd 100644
--- a/crawl-ref/source/files.cc
+++ b/crawl-ref/source/files.cc
@@ -85,12 +85,6 @@
#define DO_CHMOD_PRIVATE(x) // empty command
#endif
-// file locking stuff
-#ifdef USE_FILE_LOCKING
-static bool lock_file_handle( FILE *handle, int type );
-static bool unlock_file_handle( FILE *handle );
-#endif // USE_FILE_LOCKING
-
void save_level(int level_saved, bool was_a_labyrinth, char where_were_you);
#define GHOST_MINOR_VERSION 1
@@ -1526,7 +1520,7 @@ long readLong(FILE *file)
// first, some file locking stuff for multiuser crawl
#ifdef USE_FILE_LOCKING
-static bool lock_file_handle( FILE *handle, int type )
+bool lock_file_handle( FILE *handle, int type )
{
struct flock lock;
int status;
@@ -1537,11 +1531,8 @@ static bool lock_file_handle( FILE *handle, int type )
lock.l_type = type;
#ifdef USE_BLOCKING_LOCK
-
status = fcntl( fileno( handle ), F_SETLKW, &lock );
-
#else
-
for (int i = 0; i < 30; i++)
{
status = fcntl( fileno( handle ), F_SETLK, &lock );
@@ -1557,13 +1548,12 @@ static bool lock_file_handle( FILE *handle, int type )
perror( "Problems locking file... retrying..." );
delay( 1000 );
}
-
#endif
return (status == 0);
}
-static bool unlock_file_handle( FILE *handle )
+bool unlock_file_handle( FILE *handle )
{
struct flock lock;
int status;
diff --git a/crawl-ref/source/files.h b/crawl-ref/source/files.h
index c2563cd479..ff0af7c108 100644
--- a/crawl-ref/source/files.h
+++ b/crawl-ref/source/files.h
@@ -94,4 +94,10 @@ long readLong(FILE *file);
FILE *lk_open(const char *mode, const std::string &file);
void lk_close(FILE *handle, const char *mode, const std::string &file);
+// file locking stuff
+#ifdef USE_FILE_LOCKING
+bool lock_file_handle( FILE *handle, int type );
+bool unlock_file_handle( FILE *handle );
+#endif // USE_FILE_LOCKING
+
#endif
diff --git a/crawl-ref/source/hiscores.cc b/crawl-ref/source/hiscores.cc
index 3455c3d5e1..81271e0cdc 100644
--- a/crawl-ref/source/hiscores.cc
+++ b/crawl-ref/source/hiscores.cc
@@ -744,16 +744,13 @@ void scorefile_entry::init_with_fields()
num_runes = fields->int_field("nrune");
}
-void scorefile_entry::set_score_fields() const
+void scorefile_entry::set_base_xlog_fields() const
{
- fields.reset(new xlog_fields);
-
if (!fields.get())
- return;
+ fields.reset(new xlog_fields);
fields->add_field("v", VER_NUM);
fields->add_field("lv", SCORE_VERSION);
- fields->add_field("sc", "%ld", points);
fields->add_field("name", "%s", name.c_str());
fields->add_field("uid", "%d", uid);
fields->add_field("race", "%s", species_name(race, lvl));
@@ -763,10 +760,6 @@ void scorefile_entry::set_score_fields() const
fields->add_field("sklev", "%d", best_skill_lvl);
fields->add_field("title", "%s", skill_title( best_skill, best_skill_lvl,
race, str, dex, god ) );
- fields->add_field("ktyp", ::kill_method_name(kill_method_type(death_type)));
- fields->add_field("killer", death_source_desc());
-
- fields->add_field("kaux", "%s", auxkilldata.c_str());
fields->add_field("place", "%s",
place_name(get_packed_place(branch, dlvl, level_type),
@@ -778,7 +771,6 @@ void scorefile_entry::set_score_fields() const
fields->add_field("hp", "%d", final_hp);
fields->add_field("mhp", "%d", final_max_hp);
fields->add_field("mmhp", "%d", final_max_max_hp);
- fields->add_field("dam", "%d", damage);
fields->add_field("str", "%d", str);
fields->add_field("int", "%d", intel);
fields->add_field("dex", "%d", dex);
@@ -786,15 +778,11 @@ void scorefile_entry::set_score_fields() const
// Don't write No God to save some space.
if (god != -1)
fields->add_field("god", "%s", god == GOD_NO_GOD? "" : god_name(god));
- if (piety > 0)
- fields->add_field("piety", "%d", piety);
- if (penance > 0)
- fields->add_field("pen", "%d", penance);
+
if (wiz_mode)
fields->add_field("wiz", "%d", wiz_mode);
fields->add_field("start", "%s", make_date_string(birth_time).c_str());
- fields->add_field("end", "%s", make_date_string(death_time).c_str());
fields->add_field("dur", "%ld", real_time);
fields->add_field("turn", "%ld", num_turns);
@@ -802,7 +790,30 @@ void scorefile_entry::set_score_fields() const
fields->add_field("urune", "%d", num_diff_runes);
if (num_runes)
- fields->add_field("nrune", "%d", num_runes);
+ fields->add_field("nrune", "%d", num_runes);
+}
+
+void scorefile_entry::set_score_fields() const
+{
+ fields.reset(new xlog_fields);
+
+ if (!fields.get())
+ return;
+
+ set_base_xlog_fields();
+
+ fields->add_field("sc", "%ld", points);
+ fields->add_field("ktyp", ::kill_method_name(kill_method_type(death_type)));
+ fields->add_field("killer", death_source_desc());
+
+ fields->add_field("kaux", "%s", auxkilldata.c_str());
+
+ if (piety > 0)
+ fields->add_field("piety", "%d", piety);
+ if (penance > 0)
+ fields->add_field("pen", "%d", penance);
+
+ fields->add_field("end", "%s", make_date_string(death_time).c_str());
#ifdef DGL_EXTENDED_LOGFILES
const std::string short_msg = short_kill_message();
@@ -1099,34 +1110,35 @@ void scorefile_entry::init()
for (int i = 0; i < NUM_RUNE_TYPES; i++)
rune_array[i] = 0;
+ const bool calc_item_values =
+ (death_type == KILLED_BY_LEAVING || death_type == KILLED_BY_WINNING);
+
// Calculate value of pack and runes when character leaves dungeon
- if (death_type == KILLED_BY_LEAVING || death_type == KILLED_BY_WINNING)
+ for (int d = 0; d < ENDOFPACK; d++)
{
- for (int d = 0; d < ENDOFPACK; d++)
+ if (is_valid_item( you.inv[d] ))
{
- if (is_valid_item( you.inv[d] ))
- {
+ if (calc_item_values)
points += item_value( you.inv[d], temp_id, true );
- if (you.inv[d].base_type == OBJ_MISCELLANY
- && you.inv[d].sub_type == MISC_RUNE_OF_ZOT)
- {
- if (rune_array[ you.inv[d].plus ] == 0)
- num_diff_runes++;
+ if (you.inv[d].base_type == OBJ_MISCELLANY
+ && you.inv[d].sub_type == MISC_RUNE_OF_ZOT)
+ {
+ if (rune_array[ you.inv[d].plus ] == 0)
+ num_diff_runes++;
- num_runes += you.inv[d].quantity;
- rune_array[ you.inv[d].plus ] += you.inv[d].quantity;
- }
+ num_runes += you.inv[d].quantity;
+ rune_array[ you.inv[d].plus ] += you.inv[d].quantity;
}
}
-
- // Bonus for exploring different areas, not for collecting a
- // huge stack of demonic runes in Pandemonium (gold value
- // is enough for those). -- bwr
- if (num_diff_runes >= 3)
- points += ((num_diff_runes + 2) * (num_diff_runes + 2) * 1000);
}
+ // Bonus for exploring different areas, not for collecting a
+ // huge stack of demonic runes in Pandemonium (gold value
+ // is enough for those). -- bwr
+ if (calc_item_values && num_diff_runes >= 3)
+ points += ((num_diff_runes + 2) * (num_diff_runes + 2) * 1000);
+
// Players will have a hard time getting 1/10 of this (see XP cap):
if (points > 99999999)
points = 99999999;
@@ -2071,3 +2083,26 @@ std::string xlog_fields::xlog_line() const
return (line);
}
+
+///////////////////////////////////////////////////////////////////////////////
+// Milestones
+
+#ifdef MILESTONES
+
+void mark_milestone(const std::string &type, const std::string &milestone)
+{
+ const std::string milestone_file = Options.save_dir + "milestones.txt";
+ if (FILE *fp = lk_open("a", milestone_file))
+ {
+ const scorefile_entry se(0, 0, KILL_MISC, NULL);
+ se.set_base_xlog_fields();
+ xlog_fields xl = *se.fields;
+ xl.add_field("time", "%s", make_date_string(se.death_time).c_str());
+ xl.add_field("type", "%s", type.c_str());
+ xl.add_field("milestone", "%s", milestone.c_str());
+ fprintf(fp, "%s\n", xl.xlog_line().c_str());
+ lk_close(fp, "a", milestone_file);
+ }
+}
+
+#endif // MILESTONES
diff --git a/crawl-ref/source/hiscores.h b/crawl-ref/source/hiscores.h
index 3acf677c89..d6faee4c70 100644
--- a/crawl-ref/source/hiscores.h
+++ b/crawl-ref/source/hiscores.h
@@ -41,6 +41,10 @@ std::string hiscores_format_single( const scorefile_entry &se );
std::string hiscores_format_single_long( const scorefile_entry &se,
bool verbose = false );
+#ifdef MILESTONES
+void mark_milestone(const std::string &type, const std::string &milestone);
+#endif
+
class xlog_fields
{
public:
@@ -113,6 +117,8 @@ public:
int num_diff_runes; // number of rune types in inventory
int num_runes; // total number of runes in inventory
+ mutable std::auto_ptr<xlog_fields> fields;
+
public:
scorefile_entry();
scorefile_entry(int damage, int death_source, int death_type,
@@ -145,9 +151,8 @@ public:
std::string death_place(death_desc_verbosity) const;
std::string game_time(death_desc_verbosity) const;
-private:
- mutable std::auto_ptr<xlog_fields> fields;
-
+ void set_base_xlog_fields() const;
+
private:
std::string single_cdesc() const;
std::string strip_article_a(const std::string &s) const;
diff --git a/crawl-ref/source/initfile.cc b/crawl-ref/source/initfile.cc
index 6e4cde4035..b2605acc73 100644
--- a/crawl-ref/source/initfile.cc
+++ b/crawl-ref/source/initfile.cc
@@ -550,6 +550,10 @@ void game_options::reset_options()
player_name.clear();
+#ifdef SIMPLE_MESSAGING
+ messaging = true;
+#endif
+
autopickup_on = true;
autoprayer_on = false;
@@ -1824,7 +1828,13 @@ void game_options::read_option_line(const std::string &str, bool runscript)
else if (key == "autopickup_no_burden")
{
autopickup_no_burden = read_bool( field, autopickup_no_burden );
- }
+ }
+#ifdef SIMPLE_MESSAGING
+ else if (key == "messaging")
+ {
+ messaging = read_bool(field, messaging);
+ }
+#endif
else if (key == "use_notes")
{
use_notes = read_bool( field, use_notes );
@@ -2362,6 +2372,16 @@ void get_system_environment(void)
// This should end with the appropriate path delimiter.
SysEnv.crawl_dir = getenv("CRAWL_DIR");
+#ifdef SIMPLE_MESSAGING
+ // Enable SIMPLE_MESSAGING only if SIMPLEMAIL and MAIL are set.
+ const char *simplemail = getenv("SIMPLEMAIL");
+ if (simplemail && strcmp(simplemail, "0"))
+ {
+ const char *mail = getenv("MAIL");
+ SysEnv.messagefile = mail? mail : "";
+ }
+#endif
+
// The full path to the init file -- this over-rides CRAWL_DIR
SysEnv.crawl_rc = getenv("CRAWL_RC");
diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc
index 734bec9fd6..81e99d05a0 100644
--- a/crawl-ref/source/items.cc
+++ b/crawl-ref/source/items.cc
@@ -38,6 +38,7 @@
#include "debug.h"
#include "delay.h"
#include "effects.h"
+#include "hiscores.h"
#include "invent.h"
#include "it_use2.h"
#include "item_use.h"
@@ -959,6 +960,26 @@ static int first_corpse_monnum(int x, int y)
return (0);
}
+#ifdef MILESTONES
+static std::string milestone_rune(const item_def &item)
+{
+ return std::string("found ") + item_name( item, DESC_NOCAP_A ) + ".";
+}
+
+static void milestone_check(const item_def &item)
+{
+ if (item.base_type == OBJ_MISCELLANY
+ && item.sub_type == MISC_RUNE_OF_ZOT)
+ {
+ mark_milestone("rune", milestone_rune(item));
+ }
+ else if (item.base_type == OBJ_ORBS && item.sub_type == ORB_ZOT)
+ {
+ mark_milestone("orb", "found the Orb of Zot!");
+ }
+}
+#endif // MILESTONES
+
void origin_set(int x, int y)
{
int monnum = first_corpse_monnum(x, y);
@@ -968,9 +989,14 @@ void origin_set(int x, int y)
item_def &item = mitm[link];
if (origin_known(item))
continue;
+
if (!item.orig_monnum)
item.orig_monnum = static_cast<short>( monnum );
item.orig_place = pplace;
+
+#ifdef MILESTONES
+ milestone_check(item);
+#endif
}
}
@@ -986,6 +1012,9 @@ void origin_freeze(item_def &item, int x, int y)
if (!item.orig_monnum && x != -1 && y != -1)
origin_set_monstercorpse(item, x, y);
item.orig_place = get_packed_place();
+#ifdef MILESTONES
+ milestone_check(item);
+#endif
}
}
diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc
index 121077393a..cfb2b9bfec 100644
--- a/crawl-ref/source/misc.cc
+++ b/crawl-ref/source/misc.cc
@@ -1940,6 +1940,19 @@ std::string short_place_name(level_id id)
return id.describe();
}
+int place_branch(unsigned short place)
+{
+ const unsigned branch = (unsigned) ((place >> 8) & 0xFF);
+ const int lev = place & 0xFF;
+ return lev == 0xFF? -1 : branch;
+}
+
+int place_depth(unsigned short place)
+{
+ const int lev = place & 0xFF;
+ return lev == 0xFF? -1 : lev;
+}
+
unsigned short get_packed_place( unsigned char branch, int subdepth,
char level_type )
{
diff --git a/crawl-ref/source/misc.h b/crawl-ref/source/misc.h
index cd706748c9..7eeac53e24 100644
--- a/crawl-ref/source/misc.h
+++ b/crawl-ref/source/misc.h
@@ -168,6 +168,8 @@ unsigned short get_packed_place();
unsigned short get_packed_place( unsigned char branch, int subdepth,
char level_type );
+int place_branch(unsigned short place);
+int place_depth(unsigned short place);
std::string short_place_name(unsigned short place);
std::string short_place_name(level_id id);
std::string place_name( unsigned short place, bool long_name = false,
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index d7a3e922b0..4eb797b81f 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -2555,6 +2555,16 @@ std::string monsters::name(description_level_type desc) const
return (ptr_monam(this, desc));
}
+std::string monsters::name(description_level_type desc, bool force_vis) const
+{
+ if (!force_vis || !has_ench(ENCH_INVIS))
+ return (name(desc));
+
+ monsters m = *this;
+ m.del_ench(ENCH_INVIS, true);
+ return (m.name(desc));
+}
+
std::string monsters::pronoun(pronoun_type pro) const
{
return (mons_pronoun(type, pro));
@@ -2612,7 +2622,7 @@ void monsters::expose_to_element(beam_type, int)
{
}
-void monsters::banish()
+void monsters::banish(const std::string &)
{
monster_die(this, KILL_RESET, 0);
}
diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc
index 66ac729387..f24325525b 100644
--- a/crawl-ref/source/monplace.cc
+++ b/crawl-ref/source/monplace.cc
@@ -857,8 +857,8 @@ static int choose_band( int mon_type, int power, int &band_size )
case MONS_PALE_DRACONIAN:
if (power > 18 && one_chance_in(3) && you.level_type == LEVEL_DUNGEON)
{
- band = BAND_DRACONIAN;
- band_size = random_range(2, 4);
+ band = BAND_DRACONIAN;
+ band_size = random_range(2, 4);
}
break;
case MONS_DRACONIAN_CALLER:
@@ -870,8 +870,8 @@ static int choose_band( int mon_type, int power, int &band_size )
case MONS_DRACONIAN_SHIFTER:
if (power > 20 && you.level_type == LEVEL_DUNGEON)
{
- band = BAND_DRACONIAN;
- band_size = random_range(3, 6);
+ band = BAND_DRACONIAN;
+ band_size = random_range(3, 6);
}
break;
case MONS_TIAMAT:
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index 22374c4f0c..985e40c1ab 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -34,8 +34,10 @@
#include "beam.h"
#include "cloud.h"
#include "debug.h"
+#include "describe.h"
#include "dungeon.h"
#include "fight.h"
+#include "hiscores.h"
#include "itemname.h"
#include "items.h"
#include "itemprop.h"
@@ -342,6 +344,31 @@ static void tutorial_inspect_kill()
}
}
+#ifdef MILESTONES
+static std::string milestone_kill_verb(int killer)
+{
+ return (killer == KILL_RESET? "banished " : "killed ");
+}
+
+static void check_kill_milestone(const monsters *mons, int killer, int i)
+{
+ if (mons->type == MONS_PLAYER_GHOST)
+ {
+ std::string milestone = milestone_kill_verb(killer) + "the ghost of ";
+ milestone += ghost_description(*mons, true);
+ milestone += ".";
+ mark_milestone("ghost", milestone);
+ }
+ else if (mons_is_unique(mons->type))
+ {
+ mark_milestone("unique",
+ milestone_kill_verb(killer)
+ + mons->name(DESC_NOCAP_THE, true)
+ + ".");
+ }
+}
+#endif // MILESTONES
+
void monster_die(monsters *monster, char killer, int i, bool silent)
{
if (monster->type == -1)
@@ -354,6 +381,10 @@ void monster_die(monsters *monster, char killer, int i, bool silent)
bool in_transit = false;
const bool hard_reset = testbits(monster->flags, MF_HARD_RESET);
+#ifdef MILESTONES
+ check_kill_milestone(monster, killer, i);
+#endif
+
// From time to time Trog gives you a little bonus
if (killer == KILL_YOU && you.berserker)
{
diff --git a/crawl-ref/source/mstuff2.cc b/crawl-ref/source/mstuff2.cc
index 3a43422221..9c52cb784d 100644
--- a/crawl-ref/source/mstuff2.cc
+++ b/crawl-ref/source/mstuff2.cc
@@ -1303,7 +1303,7 @@ bolt mons_spells( int spell_cast, int power )
beam.range = 6;
beam.rangeMax = 10;
beam.damage = dice_def( 3, 4 + (power / 100) );
- beam.hit = 1500;
+ beam.hit = AUTOMATIC_HIT;
beam.type = SYM_ZAP;
beam.thrower = KILL_MON_MISSILE;
beam.flavour = BEAM_MMISSILE;
diff --git a/crawl-ref/source/notes.cc b/crawl-ref/source/notes.cc
index 286bdab320..3bde493572 100644
--- a/crawl-ref/source/notes.cc
+++ b/crawl-ref/source/notes.cc
@@ -10,6 +10,7 @@
#include "branch.h"
#include "files.h"
#include "Kills.h"
+#include "hiscores.h"
#include "message.h"
#include "misc.h"
#include "mon-pick.h"
@@ -339,8 +340,37 @@ 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::check_milestone() const {
+#ifdef MILESTONES
+ if (type == NOTE_DUNGEON_LEVEL_CHANGE) {
+ const int br = place_branch(packed_place),
+ dep = place_depth(packed_place);
+
+ if (br != -1)
+ {
+ std::string branch = place_name(packed_place, true, false).c_str();
+ if (branch.find("The ") == 0)
+ branch[0] = tolower(branch[0]);
+
+ if (dep == 1)
+ mark_milestone("enter", "entered " + branch + ".");
+ else if (dep == dungeon_branch_depth(br))
+ {
+ char branch_finale[500];
+ std::string level = place_name(packed_place, true, true);
+ if (level.find("Level ") == 0)
+ level[0] = tolower(level[0]);
+
+ snprintf(branch_finale, sizeof branch_finale,
+ "reached %s.", level.c_str());
+ mark_milestone("branch-finale", branch_finale);
+ }
+ }
+ }
+#endif
+}
+
+void Note::save( FILE* fp ) const {
writeLong( fp, type );
writeLong( fp, turn );
writeShort( fp, packed_place );
@@ -371,7 +401,10 @@ bool notes_are_active()
void take_note( const Note& note, bool force )
{
if ( notes_active && (force || is_noteworthy(note)) )
+ {
note_list.push_back( note );
+ note.check_milestone();
+ }
}
void activate_notes( bool active )
diff --git a/crawl-ref/source/notes.h b/crawl-ref/source/notes.h
index 97518a277c..2b6b4bc584 100644
--- a/crawl-ref/source/notes.h
+++ b/crawl-ref/source/notes.h
@@ -57,6 +57,7 @@ struct Note
void save( FILE* fp ) const;
std::string describe( bool when = true, bool where = true,
bool what = true ) const;
+ void check_milestone() const;
};
extern std::vector<Note> note_list;
diff --git a/crawl-ref/source/output.cc b/crawl-ref/source/output.cc
index 6ab31669be..46f70d8739 100644
--- a/crawl-ref/source/output.cc
+++ b/crawl-ref/source/output.cc
@@ -60,6 +60,20 @@ static void dur_colour( int colour, bool running_out )
}
}
+#ifdef SIMPLE_MESSAGING
+void update_message_status()
+{
+ textcolor(LIGHTBLUE);
+
+ gotoxy(75, 2);
+ if (SysEnv.have_messages)
+ cprintf("(msg)");
+ else
+ cprintf(" ");
+ textcolor(LIGHTGREY);
+}
+#endif
+
void update_turn_count()
{
// Don't update turn counter when running/resting/traveling to
diff --git a/crawl-ref/source/output.h b/crawl-ref/source/output.h
index 71a8955d0d..637d66cb5b 100644
--- a/crawl-ref/source/output.h
+++ b/crawl-ref/source/output.h
@@ -16,6 +16,10 @@
#include "menu.h"
+#ifdef SIMPLE_MESSAGING
+void update_message_status();
+#endif
+
void update_turn_count();
void print_stats(void);
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index c929776c85..29566cf85f 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -4501,6 +4501,8 @@ void player::init()
your_name[0] = 0;
banished = false;
+ banished_by.clear();
+
just_autoprayed = false;
berserk_penalty = 0;
berserker = 0;
@@ -4944,9 +4946,10 @@ void player::god_conduct(int thing_done, int level)
::did_god_conduct(thing_done, level);
}
-void player::banish()
+void player::banish(const std::string &who)
{
- banished = true;
+ banished = true;
+ banished_by = who;
}
void player::make_hungry(int hunger_increase, bool silent)
diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc
index ae3bf6419c..8731ff6629 100644
--- a/crawl-ref/source/religion.cc
+++ b/crawl-ref/source/religion.cc
@@ -1005,7 +1005,7 @@ void Xom_acts(bool niceness, int sever, bool force_sever)
(temp_rand == 1) ? "Xom casts you into the Abyss!"
: "The world seems to spin as Xom's maniacal laughter rings in your ears.");
- banished(DNGN_ENTER_ABYSS);
+ banished(DNGN_ENTER_ABYSS, "Xom");
done_bad = true;
}
diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc
index b37d51b5bf..86d269dac8 100644
--- a/crawl-ref/source/spl-cast.cc
+++ b/crawl-ref/source/spl-cast.cc
@@ -1922,12 +1922,12 @@ void exercise_spell( int spell, bool spc, bool success )
did_god_conduct( DID_SPELL_PRACTISE, exer_norm );
} // end exercise_spell()
-static bool send_abyss()
+static bool send_abyss(const char *cause)
{
if (you.level_type != LEVEL_ABYSS)
{
mpr("You are cast into the Abyss!");
- banished(DNGN_ENTER_ABYSS); // sends you to the abyss
+ banished(DNGN_ENTER_ABYSS, cause? cause : "");
return (true);
}
else
@@ -2326,7 +2326,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail,
}
break;
case 6:
- send_abyss();
+ send_abyss(cause);
break;
}
break;
@@ -2347,7 +2347,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail,
potion_effect(POT_CONFUSION, 60);
break;
case 2:
- send_abyss();
+ send_abyss(cause);
break;
case 3:
mpr("You feel saturated with unharnessed energies!");
@@ -2520,7 +2520,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail,
break;
case 3:
- send_abyss();
+ send_abyss(cause);
break;
}
break;
diff --git a/crawl-ref/source/stuff.cc b/crawl-ref/source/stuff.cc
index 4aa1315c1a..0f09465a5f 100644
--- a/crawl-ref/source/stuff.cc
+++ b/crawl-ref/source/stuff.cc
@@ -407,6 +407,9 @@ void redraw_screen(void)
bool note_status = notes_are_active();
activate_notes(false);
new_level();
+#ifdef SIMPLE_MESSAGING
+ update_message_status();
+#endif
update_turn_count();
activate_notes(note_status);