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
TV3ZC6WOZKSQQJQN26JIVKCHK6UK7WMDBYZDUYRWEAZ4JB4YVNAAC BIZDHHK5LIO57S5AKHEPJFLWV5DAFKZIKYBGOUNGICSWTX7DCXKAC 6L4EP4ZRWWYLT55PD5KTTJON5J2JB5VV5MWNHF5VPZQZ5BKEYZ4QC DODCHP2S4I6VZKQAVXX6D76OPNFI2YWZ4XH3HZTMAJZXA2RJ3XRQC TAVHZJPVNJBZR7CUURAOYNDZPNVQ3AGHTXDEP5K4JGYETBLQJDRQC K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC YIIILIV4ZPRIPWWT4GL7YWSJCUVH6RJJLXV4XIHY6SF3H7Y3EAYAC SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC GBUB77EAYHOFY6GQ5IY3ZSBC7FSQFZZKYNBD5QMCQFIKFLYLWHOQC QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC R22TTMI6WXWULC7ODKFF3QCB7MOTETQQ6IR4BUCUPOCQKQNCTT5AC 43ZTEB57FU7KE5EVMYWZONNVJBZCGF3JEAJZIY25LC4LGE65PG5QC RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC 77H4BWWPPGLM3PLZH4QTAJRXIZTSDVNCOKZE223I437FN2UJ34RQC ZK5H5YBD3R7H3KKNKSJYE65TUEPPWIWUJLU567FKSZYQYIW6ZVFAC 74LQ7JXVLAFSHLI7LCBKFX47CNTYSKGUQSXNX5FCIUIGCC2JTR3QC NNG27Y5ZQAZX6UD7F7M4F6KEZBEDFXPEEC3LFUSX4ESKT7K6UJQAC CAHE52HL2ZGRJPBYZ3DS4BVKUD2XC7N3SG25TGG7JGHGJDST4P3QC RISMOCQM6BKK4XSIRKYLOBB2UPDYJNDAL6OGIIR5GGNZQAK5YSZAC 7AMQN7MITMXBNVDAK5VOXTQ4TZIAOD6ZLOFJG7GQMBTY23Y2BKSAC RIMJO42HB75SAZH44KZ2UH2QULOPIJLMEN4CR2CYNR2EKEKLFHSQC 3WRAJZ5ZLOSIZHFBUH5552QC4F3GAK7AXF6VIQFVN6VY7PUO6HPQC void Note::save( FILE* fp ) const{
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 {
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
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
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("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());
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++;
// Bonus for exploring different areas, not for collecting a// huge stack of demonic runes in Pandemonium (gold value// is enough for those). -- bwrif (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). -- bwrif (calc_item_values && num_diff_runes >= 3)points += ((num_diff_runes + 2) * (num_diff_runes + 2) * 1000);
///////////////////////////////////////////////////////////////////////////////// Milestones#ifdef MILESTONESvoid 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
char *home; // only used by MULTIUSER systemsbool board_with_nail; // Easter Egg silliness
char *home; // only used by MULTIUSER systemsbool board_with_nail; // Easter Egg silliness#ifdef SIMPLE_MESSAGINGstd::string messagefile; // File containing messages from other users.bool have_messages; // There are messages waiting to be read.unsigned message_check_tick;#endif
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 ("");elsereturn ptr_monam( &menv[bsrc], DESC_PLAIN );
#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
}#ifdef SIMPLE_MESSAGINGstatic 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
// 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
// ===========================================================================// Misc// ===========================================================================#if HAS_NAMESPACESusing namespace std;
#if defined(SIMPLE_MESSAGING) && !defined(USE_FILE_LOCKING)# error Must define USE_FILE_LOCKING for SIMPLE_MESSAGING