From bdb45bf27b86ff63c5afdff311b4065c83faf37d Mon Sep 17 00:00:00 2001 From: dshaligram Date: Sun, 25 Mar 2007 13:23:23 +0000 Subject: 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 --- crawl-ref/source/AppHdr.h | 19 +++-- crawl-ref/source/acr.cc | 163 ++++++++++++++++++++++++++++++++++++++++++- crawl-ref/source/beam.cc | 17 ++++- crawl-ref/source/decks.cc | 5 +- crawl-ref/source/describe.cc | 6 +- crawl-ref/source/dungeon.cc | 5 +- crawl-ref/source/effects.cc | 18 ++++- crawl-ref/source/effects.h | 2 +- crawl-ref/source/enum.h | 2 + crawl-ref/source/externs.h | 28 +++++--- crawl-ref/source/fight.cc | 3 +- crawl-ref/source/files.cc | 14 +--- crawl-ref/source/files.h | 6 ++ crawl-ref/source/hiscores.cc | 103 ++++++++++++++++++--------- crawl-ref/source/hiscores.h | 11 ++- crawl-ref/source/initfile.cc | 22 +++++- crawl-ref/source/items.cc | 29 ++++++++ crawl-ref/source/misc.cc | 13 ++++ crawl-ref/source/misc.h | 2 + crawl-ref/source/mon-util.cc | 12 +++- crawl-ref/source/monplace.cc | 8 +-- crawl-ref/source/monstuff.cc | 31 ++++++++ crawl-ref/source/mstuff2.cc | 2 +- crawl-ref/source/notes.cc | 37 +++++++++- crawl-ref/source/notes.h | 1 + crawl-ref/source/output.cc | 14 ++++ crawl-ref/source/output.h | 4 ++ crawl-ref/source/player.cc | 7 +- crawl-ref/source/religion.cc | 2 +- crawl-ref/source/spl-cast.cc | 10 +-- crawl-ref/source/stuff.cc | 3 + 31 files changed, 504 insertions(+), 95 deletions(-) (limited to 'crawl-ref/source') 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 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 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( 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_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); -- cgit v1.2.3-54-g00ecf