diff options
-rw-r--r-- | crawl-ref/source/AppHdr.h | 39 | ||||
-rw-r--r-- | crawl-ref/source/acr.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/enum.h | 4 | ||||
-rw-r--r-- | crawl-ref/source/externs.h | 54 | ||||
-rw-r--r-- | crawl-ref/source/fight.cc | 9 | ||||
-rw-r--r-- | crawl-ref/source/hiscores.cc | 1208 | ||||
-rw-r--r-- | crawl-ref/source/initfile.cc | 6 | ||||
-rw-r--r-- | crawl-ref/source/misc.cc | 11 | ||||
-rw-r--r-- | crawl-ref/source/misc.h | 1 | ||||
-rw-r--r-- | crawl-ref/source/mon-data.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/monstuff.cc | 21 | ||||
-rw-r--r-- | crawl-ref/source/mstuff2.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/player.cc | 38 | ||||
-rw-r--r-- | crawl-ref/source/player.h | 3 | ||||
-rw-r--r-- | crawl-ref/source/skills2.cc | 9 | ||||
-rw-r--r-- | crawl-ref/source/skills2.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/tags.cc | 15 | ||||
-rw-r--r-- | crawl-ref/source/tags.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/travel.h | 14 | ||||
-rw-r--r-- | crawl-ref/source/version.h | 5 |
20 files changed, 826 insertions, 621 deletions
diff --git a/crawl-ref/source/AppHdr.h b/crawl-ref/source/AppHdr.h index f68fdd97c7..45849093a7 100644 --- a/crawl-ref/source/AppHdr.h +++ b/crawl-ref/source/AppHdr.h @@ -84,20 +84,6 @@ // // #define DGAMELAUNCH - // DGL_CLEAR_SCREEN specifies the escape sequence to use to clear - // the screen (used only when DGAMELAUNCH is defined). We make no - // attempt to discover an appropriate escape sequence for the - // term, assuming that dgamelaunch admins can adjust this as - // needed. - // - // Why this is necessary: dgamelaunch's ttyplay initialises - // playback by jumping to the last screen clear and playing back - // from there. For that to work, ttyplay must be able to recognise - // the clear screen sequence, and ncurses clear()+refresh() - // doesn't do the trick. - // - #define DGL_CLEAR_SCREEN "\033[2J" - #define MULTIUSER #define USE_UNIX_SIGNALS @@ -199,6 +185,31 @@ #error Missing platform #define or unsupported compiler. #endif +// ========================================================================= +// Defines for dgamelaunch-specific things. +// ========================================================================= + +#ifdef DGAMELAUNCH + // DGL_CLEAR_SCREEN specifies the escape sequence to use to clear + // the screen (used only when DGAMELAUNCH is defined). We make no + // attempt to discover an appropriate escape sequence for the + // term, assuming that dgamelaunch admins can adjust this as + // needed. + // + // Why this is necessary: dgamelaunch's ttyplay initialises + // playback by jumping to the last screen clear and playing back + // from there. For that to work, ttyplay must be able to recognise + // the clear screen sequence, and ncurses clear()+refresh() + // doesn't do the trick. + // + #define DGL_CLEAR_SCREEN "\033[2J" + + // If defined, the hiscores code dumps preformatted verbose and terse + // death message strings in the logfile for the convenience of logfile + // parsers. + #define DGL_EXTENDED_LOGFILES + +#endif // ========================================================================= // Debugging Defines diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc index ed5b69385e..022a3ba037 100644 --- a/crawl-ref/source/acr.cc +++ b/crawl-ref/source/acr.cc @@ -210,7 +210,7 @@ int main( int argc, char *argv[] ) // now parse the args again, looking for everything else. parse_args( argc, argv, false ); - if (Options.sc_entries != 0) + if (Options.sc_entries != 0 || !SysEnv.scorefile.empty()) { hiscores_print_all( Options.sc_entries, Options.sc_format ); exit(0); diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index 1655e98edc..9495c20fb1 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -1684,7 +1684,9 @@ enum level_area_type // you.level_type LEVEL_DUNGEON, // 0 LEVEL_LABYRINTH, LEVEL_ABYSS, - LEVEL_PANDEMONIUM + LEVEL_PANDEMONIUM, + + NUM_LEVEL_AREA_TYPES }; enum load_mode_type diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index f3c2f261b0..c5d6b6f3f4 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -19,8 +19,8 @@ #include <vector> #include <list> #include <string> - #include <map> +#include <memory> #include <time.h> @@ -721,6 +721,7 @@ public: size_type body_size(int psize = PSIZE_TORSO, bool base = false) const; int damage_type(int attk = -1); int damage_brand(int attk = -1); + bool has_usable_claws() const; item_def *weapon(int which_attack = -1); item_def *shield(); @@ -1350,19 +1351,19 @@ public: char version; char release; long points; - char name[kNameLen]; + std::string name; long uid; // for multiuser systems char race; char cls; - char race_class_name[5]; // overrides race & cls if non-null + std::string race_class_name; // overrides race & cls if non-empty. char lvl; // player level. char best_skill; // best skill # char best_skill_lvl; // best skill level int death_type; int death_source; // 0 or monster TYPE int mon_num; // sigh... - char death_source_name[40]; // overrides death_source - char auxkilldata[ITEMNAME_SIZE]; // weapon wielded, spell cast, etc + std::string death_source_name; // overrides death_source + std::string auxkilldata; // weapon wielded, spell cast, etc char dlvl; // dungeon level (relative) char level_type; // what kind of level died on.. char branch; // dungeon branch @@ -1388,7 +1389,10 @@ public: scorefile_entry(); scorefile_entry(int damage, int death_source, int death_type, const char *aux, bool death_cause_only = false); + scorefile_entry(const scorefile_entry &se); + scorefile_entry &operator = (const scorefile_entry &other); + void init_death_cause(int damage, int death_source, int death_type, const char *aux); void init(); @@ -1398,29 +1402,63 @@ public: DDV_TERSE, DDV_ONELINE, DDV_NORMAL, - DDV_VERBOSE + DDV_VERBOSE, + DDV_LOGVERBOSE // Semi-verbose for logging purposes }; + std::string raw_string() const; + bool parse(const std::string &line); + std::string hiscore_line(death_desc_verbosity verbosity) const; std::string character_description(death_desc_verbosity) const; // Full description of death: Killed by an xyz wielding foo std::string death_description(death_desc_verbosity) const; - std::string death_place(death_desc_verbosity) const; - std::string game_time(death_desc_verbosity) const; private: + typedef std::vector< std::pair<std::string, std::string> > hs_fields; + typedef std::map<std::string, std::string> hs_map; + + mutable std::auto_ptr<hs_fields> fields; + mutable std::auto_ptr<hs_map> fieldmap; + +private: std::string single_cdesc() const; std::string strip_article_a(const std::string &s) const; std::string terse_missile_cause() const; + std::string terse_missile_name() const; std::string terse_beam_cause() const; std::string terse_wild_magic() const; std::string terse_trap() const; const char *damage_verb() const; const char *death_source_desc() const; std::string damage_string(bool terse = false) const; + + bool parse_obsolete_scoreline(const std::string &line); + bool parse_scoreline(const std::string &line); + + void init_with_fields(); + void add_field(const std::string &key, + const char *format, ...) const; + void add_auxkill_field() const; + void set_score_fields() const; + + std::string short_kill_message() const; + std::string long_kill_message() const; + std::string make_oneline(const std::string &s) const; + + std::string str_field(const std::string &key) const; + int int_field(const std::string &key) const; + long long_field(const std::string &key) const; + std::string xlog_escape(const std::string &s) const; + std::string xlog_unescape(const std::string &s) const; + void read_auxkill_field(); + void map_fields(); + void init_from(const scorefile_entry &other); + + int kludge_branch(int branch_01) const; }; extern const struct coord_def Compass[8]; diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index defd36c88f..835bdee2d8 100644 --- a/crawl-ref/source/fight.cc +++ b/crawl-ref/source/fight.cc @@ -693,7 +693,6 @@ bool melee_attack::player_aux_unarmed() } unarmed_attack = "punch"; - /* applied twice */ aux_damage = 5 + you.skills[SK_UNARMED_COMBAT] / 3; @@ -702,6 +701,12 @@ bool melee_attack::player_aux_unarmed() unarmed_attack = "slash"; aux_damage += 6; } + else if (you.has_usable_claws()) + { + unarmed_attack = "claw"; + aux_damage += roll_dice(1, 3); + } + break; /* To add more, add to while part of loop below as well */ @@ -1218,7 +1223,7 @@ int melee_attack::player_weapon_type_modify(int damage) break; case -1: // unarmed - if (you.species == SP_TROLL || you.mutation[MUT_CLAWS]) + if (you.damage_type() == DVORP_CLAWING) { if (damage < HIT_MED) attack_verb = "claw"; diff --git a/crawl-ref/source/hiscores.cc b/crawl-ref/source/hiscores.cc index 0c2f343896..2aa17937c2 100644 --- a/crawl-ref/source/hiscores.cc +++ b/crawl-ref/source/hiscores.cc @@ -35,6 +35,7 @@ #include "AppHdr.h" #include "externs.h" +#include "branch.h" #include "hiscores.h" #include "itemname.h" #include "itemprop.h" @@ -47,10 +48,13 @@ #include "shopping.h" #include "stuff.h" #include "tags.h" +#include "version.h" #include "view.h" #include "skills2.h" +#define SCORE_VERSION "0.1" + #ifdef MULTIUSER // includes to get passwd file access: #include <pwd.h> @@ -68,21 +72,11 @@ static FILE *hs_open(const char *mode, const std::string &filename); static void hs_close(FILE *handle, const char *mode, const std::string &filename); static bool hs_read(FILE *scores, scorefile_entry &dest); -static void hs_parse_numeric(char *inbuf, scorefile_entry &dest); -static void hs_parse_string(char *inbuf, scorefile_entry &dest); static void hs_write(FILE *scores, scorefile_entry &entry); -static void hs_nextstring(char *&inbuf, char *dest, size_t bufsize); -static int hs_nextint(char *&inbuf); -static long hs_nextlong(char *&inbuf); - -// functions dealing with old scorefile entries -static void hs_parse_generic_1(char *&inbuf, char *outbuf, size_t outsz, - const char *stopvalues); -static void hs_parse_generic_2(char *&inbuf, char *outbuf, size_t outsz, - const char *continuevalues); -static void hs_stripblanks(char *buf); -static void hs_search_death(char *inbuf, struct scorefile_entry &se); -static void hs_search_where(char *inbuf, struct scorefile_entry &se); +static void hs_nextstring(const char *&inbuf, char *dest, size_t bufsize); +static int hs_nextint(const char *&inbuf); +static long hs_nextlong(const char *&inbuf); +static time_t parse_time(const std::string &st); // file locking stuff #ifdef USE_FILE_LOCKING @@ -245,7 +239,10 @@ void hiscores_print_all(int display_count, int format) if (!hs_read(scores, se)) break; - hiscores_print_entry(se, entry, format, printf); + if (format == -1) + printf("%s\n", se.raw_string().c_str()); + else + hiscores_print_entry(se, entry, format, printf); } hs_close( scores, "r", score_file_name() ); @@ -493,42 +490,31 @@ void hs_close( FILE *handle, const char *mode, const std::string &scores ) bool hs_read( FILE *scores, scorefile_entry &dest ) { - char inbuf[600]; - int c = EOF; + char inbuf[1300]; + if (!scores || feof(scores)) + return (false); memset(inbuf, 0, sizeof inbuf); dest.reset(); - // get a character.. - if (scores != NULL) - c = fgetc(scores); - - // check for NULL scores file or EOF - if (scores == NULL || c == EOF) - return false; + if (!fgets(inbuf, sizeof inbuf, scores)) + return (false); - // get a line - this is tricky. "Lines" come in three flavors: - // 1) old-style lines which were 80 character blocks - // 2) 4.0 pr1 through pr7 versions which were newline terminated - // 3) 4.0 pr8 and onwards which are 'current' ASCII format, and - // may exceed 80 characters! - - // put 'c' in first spot - inbuf[0] = c; - - if (fgets(&inbuf[1], (c==':') ? (sizeof(inbuf) - 2) : 81, scores) == NULL) - return false; - - // check type; lines starting with a colon are new-style scores. - if (c == ':') - hs_parse_numeric(inbuf, dest); - else - hs_parse_string(inbuf, dest); + return (dest.parse(inbuf)); +} - return true; +static std::string hs_nextstring(const char *&inbuf, size_t destsize = 800) +{ + char *buf = new char[destsize]; + if (!buf) + return (""); + hs_nextstring(inbuf, buf, destsize); + const std::string res = buf; + delete [] buf; + return (res); } -static void hs_nextstring(char *&inbuf, char *dest, size_t destsize) +static void hs_nextstring(const char *&inbuf, char *dest, size_t destsize) { ASSERT(destsize > 0); @@ -554,7 +540,7 @@ static void hs_nextstring(char *&inbuf, char *dest, size_t destsize) *p = 0; } -static int hs_nextint(char *&inbuf) +static int hs_nextint(const char *&inbuf) { char num[20]; hs_nextstring(inbuf, num, sizeof num); @@ -562,7 +548,7 @@ static int hs_nextint(char *&inbuf) return (num[0] == 0 ? 0 : atoi(num)); } -static long hs_nextlong(char *&inbuf) +static long hs_nextlong(const char *&inbuf) { char num[20]; hs_nextstring(inbuf, num, sizeof num); @@ -575,134 +561,39 @@ static int val_char( char digit ) return (digit - '0'); } -static time_t hs_nextdate(char *&inbuf) +static time_t hs_nextdate(const char *&inbuf) { char buff[20]; - struct tm date; - hs_nextstring(inbuf, buff, sizeof buff); - if (strlen( buff ) < 15) - return (static_cast<time_t>(0)); - - date.tm_year = val_char( buff[0] ) * 1000 + val_char( buff[1] ) * 100 - + val_char( buff[2] ) * 10 + val_char( buff[3] ) - 1900; - - date.tm_mon = val_char( buff[4] ) * 10 + val_char( buff[5] ); - date.tm_mday = val_char( buff[6] ) * 10 + val_char( buff[7] ); - date.tm_hour = val_char( buff[8] ) * 10 + val_char( buff[9] ); - date.tm_min = val_char( buff[10] ) * 10 + val_char( buff[11] ); - date.tm_sec = val_char( buff[12] ) * 10 + val_char( buff[13] ); - date.tm_isdst = (buff[14] == 'D'); - - return (mktime( &date )); + return parse_time(buff); } -static void hs_parse_numeric(char *inbuf, struct scorefile_entry &se) +static time_t parse_time(const std::string &st) { - se.version = hs_nextint(inbuf); - se.release = hs_nextint(inbuf); - - // this would be a good point to check for version numbers and branch - // appropriately - - // acceptable versions are 0 (converted from old hiscore format) and 4 - if (se.version != 0 && se.version != 4) - return; - - se.points = hs_nextlong(inbuf); - - hs_nextstring(inbuf, se.name, sizeof se.name); - - se.uid = hs_nextlong(inbuf); - se.race = hs_nextint(inbuf); - se.cls = hs_nextint(inbuf); - - hs_nextstring(inbuf, se.race_class_name, sizeof se.race_class_name); - - se.lvl = hs_nextint(inbuf); - se.best_skill = hs_nextint(inbuf); - se.best_skill_lvl = hs_nextint(inbuf); - se.death_type = hs_nextint(inbuf); - se.death_source = hs_nextint(inbuf); - se.mon_num = hs_nextint(inbuf); - - hs_nextstring(inbuf, se.death_source_name, sizeof se.death_source_name); - - // To try and keep the scorefile backwards compatible, - // we'll branch on version > 4.0 to read the auxkilldata - // text field. - if (se.version == 4 && se.release >= 1) - hs_nextstring( inbuf, se.auxkilldata, sizeof se.auxkilldata ); - else - se.auxkilldata[0] = 0; - - se.dlvl = hs_nextint(inbuf); - se.level_type = hs_nextint(inbuf); - se.branch = hs_nextint(inbuf); - - // Trying to fix some bugs that have been around since at - // least pr19, if not longer. From now on, dlvl should - // be calculated on death and need no further modification. - if (se.version < 4 || se.release < 2) - { - if (se.level_type == LEVEL_DUNGEON) - { - if (se.branch == BRANCH_MAIN_DUNGEON) - se.dlvl += 1; - else if (se.branch < BRANCH_ORCISH_MINES) // ie the hells - se.dlvl -= 1; - } - } + struct tm date; - se.final_hp = hs_nextint(inbuf); - if (se.version == 4 && se.release >= 2) - { - se.final_max_hp = hs_nextint(inbuf); - se.final_max_max_hp = hs_nextint(inbuf); - se.damage = hs_nextint(inbuf); - se.str = hs_nextint(inbuf); - se.intel = hs_nextint(inbuf); - se.dex = hs_nextint(inbuf); - se.god = hs_nextint(inbuf); - se.piety = hs_nextint(inbuf); - se.penance = hs_nextint(inbuf); - } - else - { - se.final_max_hp = -1; - se.final_max_max_hp = -1; - se.damage = -1; - se.str = -1; - se.intel = -1; - se.dex = -1; - se.god = -1; - se.piety = -1; - se.penance = -1; - } + if (st.length() < 15) + return (static_cast<time_t>(0)); - se.wiz_mode = hs_nextint(inbuf); + date.tm_year = val_char( st[0] ) * 1000 + val_char( st[1] ) * 100 + + val_char( st[2] ) * 10 + val_char( st[3] ) - 1900; - se.birth_time = hs_nextdate(inbuf); - se.death_time = hs_nextdate(inbuf); + date.tm_mon = val_char( st[4] ) * 10 + val_char( st[5] ); + date.tm_mday = val_char( st[6] ) * 10 + val_char( st[7] ); + date.tm_hour = val_char( st[8] ) * 10 + val_char( st[9] ); + date.tm_min = val_char( st[10] ) * 10 + val_char( st[11] ); + date.tm_sec = val_char( st[12] ) * 10 + val_char( st[13] ); + date.tm_isdst = (st[14] == 'D'); - if (se.version == 4 && se.release >= 2) - { - se.real_time = hs_nextint(inbuf); - se.num_turns = hs_nextint(inbuf); - } - else - { - se.real_time = -1; - se.num_turns = -1; - } - - se.num_diff_runes = hs_nextint(inbuf); - se.num_runes = hs_nextint(inbuf); + return (mktime( &date )); } static void hs_write( FILE *scores, scorefile_entry &se ) { + fprintf(scores, "%s\n", se.raw_string().c_str()); + + /* char buff[80]; // should be more than enough for date stamps se.version = 4; @@ -730,380 +621,577 @@ static void hs_write( FILE *scores, scorefile_entry &se ) fprintf( scores, ":%ld:%ld:%d:%d:\n", se.real_time, se.num_turns, se.num_diff_runes, se.num_runes ); -} -// ------------------------------------------------------------------------- -// functions dealing with old-style scorefile entries. -// ------------------------------------------------------------------------- + */ +} -static void hs_parse_string(char *inbuf, struct scorefile_entry &se) +static const char *kill_method_names[] = +{ + "mon", "pois", "cloud", "beam", "deaths_door", "lava", "water", + "stupidity", "weakness", "clumsiness", "trap", "leaving", "winning", + "quitting", "draining", "starvation", "freezing", "burning", "wild_magic", + "xom", "statue", "rotting", "targetting", "spore", "tso_smiting", + "petrification", "unknown", "something", "falling_down_stairs", "acid", + "curare", "melting", "bleeding", +}; + +const char *kill_method_name(kill_method_type kmt) { - /* old entries are of the following format (Brent introduced some - spacing at one point, we have to take this into account): + ASSERT(NUM_KILLBY == + (int) sizeof(kill_method_names) / sizeof(*kill_method_names)); - // Actually, I believe it might have been Brian who added the spaces, - // I was quite happy with the condensed version, given the 80 column - // restriction. -- bwr + if (kmt == NUM_KILLBY) + return (""); -6263 BoBo - DSD10 Wiz, killed by an acid blob on L1 of the Slime Pits. -5877 Aldus-DGM10, killed by a lethal dose of poison on L10. -5419 Yarf - Koa10, killed by a warg on L1 of the Mines. + return kill_method_names[kmt]; +} - 1. All numerics up to the first non-numeric are the score - 2. All non '-' characters are the name. Strip spaces. - 3. All alphabetics up to the first numeric are race/class - 4. All numerics up to the comma are the clevel - 5. From the comma, search for known fixed substrings and - translate to death_type. Leave death source = 0 for old - scores, and just copy in the monster name. - 6. Look for the branch type (again, substring search for - fixed strings) and level. +kill_method_type str_to_kill_method(const std::string &s) +{ + ASSERT(NUM_KILLBY == + (int) sizeof(kill_method_names) / sizeof(*kill_method_names)); + + for (int i = 0; i < NUM_KILLBY; ++i) + { + if (s == kill_method_names[i]) + return static_cast<kill_method_type>(i); + } - Very ugly and time consuming. + return (NUM_KILLBY); +} - */ +////////////////////////////////////////////////////////////////////////// +// scorefile_entry - char scratch[80]; - const int inlen = strlen(inbuf); - char *start = inbuf; +scorefile_entry::scorefile_entry(int dam, int dsource, int dtype, + const char *aux, bool death_cause_only) +{ + reset(); - // 1. get score - hs_parse_generic_2(inbuf, scratch, sizeof scratch, "0123456789"); + init_death_cause(dam, dsource, dtype, aux); + if (!death_cause_only) + init(); +} - se.version = 0; // version # of converted score - se.release = 0; - se.points = atoi(scratch); +scorefile_entry::scorefile_entry() +{ + // Completely uninitialized, caveat user. + reset(); +} - // 2. get name - hs_parse_generic_1(inbuf, scratch, sizeof scratch, "-"); - hs_stripblanks(scratch); - strncpy(se.name, scratch, sizeof se.name); - se.name[ sizeof(se.name) - 1 ] = 0; +scorefile_entry::scorefile_entry(const scorefile_entry &se) +{ + init_from(se); +} - // 3. get race, class - // skip '-' - if (++inbuf - start >= inlen) - return; +scorefile_entry &scorefile_entry::operator = (const scorefile_entry &se) +{ + init_from(se); + return (*this); +} - hs_parse_generic_1(inbuf, scratch, sizeof scratch, "0123456789"); - hs_stripblanks(scratch); - strncpy(se.race_class_name, scratch, sizeof se.race_class_name); - se.race_class_name[ sizeof(se.race_class_name) - 1 ] = 0; - se.race = 0; - se.cls = 0; - - // 4. get clevel - hs_parse_generic_2(inbuf, scratch, sizeof scratch, "0123456789"); - se.lvl = atoi(scratch); - - // 4a. get wizard mode - hs_parse_generic_1(inbuf, scratch, sizeof scratch, ","); - if (strstr(scratch, "Wiz") != NULL) - se.wiz_mode = 1; +void scorefile_entry::init_from(const scorefile_entry &se) +{ + version = se.version; + release = se.release; + points = se.points; + name = se.name; + uid = se.uid; + race = se.race; + cls = se.cls; + race_class_name = se.race_class_name; + lvl = se.lvl; + best_skill = se.best_skill; + best_skill_lvl = se.best_skill_lvl; + death_type = se.death_type; + death_source = se.death_source; + mon_num = se.mon_num; + death_source_name = se.death_source_name; + auxkilldata = se.auxkilldata; + dlvl = se.dlvl; + level_type = se.level_type; + branch = se.branch; + final_hp = se.final_hp; + final_max_hp = se.final_max_hp; + final_max_max_hp = se.final_max_max_hp; + damage = se.damage; + str = se.str; + intel = se.intel; + dex = se.dex; + god = se.god; + piety = se.piety; + penance = se.penance; + wiz_mode = se.wiz_mode; + birth_time = se.birth_time; + death_time = se.death_time; + real_time = se.real_time; + num_turns = se.num_turns; + num_diff_runes = se.num_diff_runes; + num_runes = se.num_runes; +} + +bool scorefile_entry::parse(const std::string &line) +{ + // Scorefile formats down the ages: + // + // 1) old-style lines which were 80 character blocks + // 2) 4.0 pr1 through pr7 versions which were newline terminated + // 3) 4.0 pr8 and onwards which are colon-separated fields (and + // start with a colon), and may exceed 80 characters! + // 4) 0.2 and onwards, which are xlogfile format - no leading + // colon, fields separated by colons, each field specified as + // key=value. Colons are not allowed in key names, must be escaped to + // | in values. Literal | must be escaped as || in values. + // + // 0.2 only reads entries of type (3) and (4), and only writes entries of + // type (4). + + // Leading colon implies 4.0 style line: + if (line[0] == ':') + return (parse_obsolete_scoreline(line)); else - se.wiz_mode = 0; + return (parse_scoreline(line)); +} - // Skip comma - if (++inbuf - start >= inlen) - return; +// xlogfile escape: s/\\/\\\\/g, s/|/\\|/g, s/:/|/g, +std::string scorefile_entry::xlog_escape(const std::string &s) const +{ + return + replace_all_of( + replace_all_of( + replace_all_of(s, "\\", "\\\\"), + "|", "\\|" ), + ":", "|" ); +} - // 5. get death type - hs_search_death(inbuf, se); - - // 6. get branch, level - hs_search_where(inbuf, se); - - // set things that can't be picked out of old scorefile entries - se.uid = 0; - se.best_skill = 0; - se.best_skill_lvl = 0; - se.final_hp = 0; - se.final_max_hp = -1; - se.final_max_max_hp = -1; - se.damage = -1; - se.str = -1; - se.intel = -1; - se.dex = -1; - se.god = -1; - se.piety = -1; - se.penance = -1; - se.birth_time = 0; - se.death_time = 0; - se.real_time = -1; - se.num_turns = -1; - se.num_runes = 0; - se.num_diff_runes = 0; - se.auxkilldata[0] = 0; -} - -static void hs_parse_generic_1(char *&inbuf, char *outbuf, size_t outsz, const char *stopvalues) -{ - ASSERT(outsz > 0); - - char *p = outbuf; - - if (!*inbuf) +// xlogfile unescape: s/\\(.)/$1/g, s/|/:/g +std::string scorefile_entry::xlog_unescape(const std::string &s) const +{ + std::string unesc = s; + bool escaped = false; + for (int i = 0, size = unesc.size(); i < size; ++i) { - *p = 0; - return; + const char c = unesc[i]; + if (escaped) + { + escaped = false; + continue; + } + + if (c == '|') + unesc[i] = ':'; + else if (c == '\\') + { + escaped = true; + unesc.erase(i--, 1); + size--; + } } + return (unesc); +} - while (strchr(stopvalues, *inbuf) == NULL - && *inbuf != 0 - && (p - outbuf) < (int) outsz - 1) - *p++ = *inbuf++; +std::string scorefile_entry::raw_string() const +{ + set_score_fields(); + + if (!fields.get()) + return (""); + + std::string line; + for (int i = 0, size = fields->size(); i < size; ++i) + { + const std::pair<std::string, std::string> &f = (*fields)[i]; - while (strchr(stopvalues, *inbuf) == NULL - && *inbuf != 0) - inbuf++; + // Don't write empty fields. + if (f.second.empty()) + continue; + + if (!line.empty()) + line += ":"; - *p = 0; + line += f.first; + line += "="; + line += xlog_escape(f.second); + } + + return (line); } -static void hs_parse_generic_2( - char *&inbuf, char *outbuf, size_t outsz, const char *continuevalues) +bool scorefile_entry::parse_scoreline(const std::string &line) { - ASSERT(outsz > 0); - - char *p = outbuf; - - if (!*inbuf) + std::vector<std::string> rawfields = split_string(":", line); + fields.reset(new hs_fields); + for (int i = 0, size = rawfields.size(); i < size; ++i) { - *p = 0; - return; + const std::string field = rawfields[i]; + std::string::size_type st = field.find('='); + if (st == std::string::npos) + continue; + + fields->push_back( + std::pair<std::string, std::string>( + field.substr(0, st), + xlog_unescape(field.substr(st + 1)) ) ); } - while (strchr(continuevalues, *inbuf) != NULL - && *inbuf - && (p - outbuf) < (int) outsz - 1) - *p++ = *inbuf++; + init_with_fields(); + + return (true); +} - while (strchr(continuevalues, *inbuf) != NULL - && *inbuf) - inbuf++; +void scorefile_entry::add_field(const std::string &key, + const char *format, + ...) const +{ + char buf[400]; + va_list args; + va_start(args, format); + vsnprintf(buf, sizeof buf, format, args); + va_end(args); + + fields->push_back( + std::pair<std::string, std::string>( key, buf ) ); +} - *p = 0; +void scorefile_entry::add_auxkill_field() const +{ + const char *what = + death_type == KILLED_BY_MONSTER? "kweap" : + death_type == KILLED_BY_BEAM? "kbeam" : + "kaux"; + + add_field(what, "%s", auxkilldata.c_str()); } -static void hs_stripblanks(char *buf) +void scorefile_entry::read_auxkill_field() { - char *p = buf; - char *q = buf; + const char *what = + death_type == KILLED_BY_MONSTER? "kweap" : + death_type == KILLED_BY_BEAM? "kbeam" : + "kaux"; - // strip leading - while(*p == ' ') - p++; + auxkilldata = str_field(what); +} - while(*p != 0 && p != q) - *q++ = *p++; +std::string scorefile_entry::str_field(const std::string &s) const +{ + hs_map::const_iterator i = fieldmap->find(s); + if (i == fieldmap->end()) + return (""); - *q-- = 0; - // strip trailing - while (q >= buf && *q == ' ') - *q-- = 0; + return i->second; } -static void hs_search_death(char *inbuf, struct scorefile_entry &se) +int scorefile_entry::int_field(const std::string &s) const { - // assume killed by monster - se.death_type = KILLED_BY_MONSTER; + std::string field = str_field(s); + return atoi(field.c_str()); +} - // sigh.. - if (strstr(inbuf, "killed by a lethal dose of poison") != NULL) - se.death_type = KILLED_BY_POISON; - else if (strstr(inbuf, "killed by a cloud") != NULL) - se.death_type = KILLED_BY_CLOUD; - else if (strstr(inbuf, "killed from afar by") != NULL) - se.death_type = KILLED_BY_BEAM; - else if (strstr(inbuf, "took a swim in molten lava") != NULL) - se.death_type = KILLED_BY_LAVA; - else if (strstr(inbuf, "asphyxiated")) - se.death_type = KILLED_BY_CURARE; - else if (strstr(inbuf, "soaked and fell apart") != NULL) - { - se.death_type = KILLED_BY_WATER; - se.race = SP_MUMMY; - } - else if (strstr(inbuf, "drowned") != NULL) - se.death_type = KILLED_BY_WATER; - else if (strstr(inbuf, "died of stupidity") != NULL) - se.death_type = KILLED_BY_STUPIDITY; - else if (strstr(inbuf, "too weak to continue adventuring") != NULL) - se.death_type = KILLED_BY_WEAKNESS; - else if (strstr(inbuf, "slipped on a banana peel") != NULL) - se.death_type = KILLED_BY_CLUMSINESS; - else if (strstr(inbuf, "killed by a trap") != NULL) - se.death_type = KILLED_BY_TRAP; - else if (strstr(inbuf, "got out of the dungeon alive") != NULL) - se.death_type = KILLED_BY_LEAVING; - else if (strstr(inbuf, "escaped with the Orb") != NULL) - se.death_type = KILLED_BY_WINNING; - else if (strstr(inbuf, "quit") != NULL) - se.death_type = KILLED_BY_QUITTING; - else if (strstr(inbuf, "was drained of all life") != NULL) - se.death_type = KILLED_BY_DRAINING; - else if (strstr(inbuf, "starved to death") != NULL) - se.death_type = KILLED_BY_STARVATION; - else if (strstr(inbuf, "froze to death") != NULL) - se.death_type = KILLED_BY_FREEZING; - else if (strstr(inbuf, "burnt to a crisp") != NULL) - se.death_type = KILLED_BY_BURNING; - else if (strstr(inbuf, "killed by wild magic") != NULL) - se.death_type = KILLED_BY_WILD_MAGIC; - else if (strstr(inbuf, "killed by Xom") != NULL) - se.death_type = KILLED_BY_XOM; - else if (strstr(inbuf, "killed by a statue") != NULL) - se.death_type = KILLED_BY_STATUE; - else if (strstr(inbuf, "rotted away") != NULL) - se.death_type = KILLED_BY_ROTTING; - else if (strstr(inbuf, "killed by bad target") != NULL) - se.death_type = KILLED_BY_TARGETTING; - else if (strstr(inbuf, "killed by an exploding spore") != NULL) - se.death_type = KILLED_BY_SPORE; - else if (strstr(inbuf, "smote by The Shining One") != NULL) - se.death_type = KILLED_BY_TSO_SMITING; - else if (strstr(inbuf, "turned to stone") != NULL) - se.death_type = KILLED_BY_PETRIFICATION; - else if (strstr(inbuf, "melted into a puddle") != NULL) - se.death_type = KILLED_BY_MELTING; - else if (strstr(inbuf, "bled to death") != NULL) - se.death_type = KILLED_BY_BLEEDING; - - // whew! - - // now, if we're still KILLED_BY_MONSTER, make sure that there is - // a "killed by" somewhere, or else we're setting it to UNKNOWN. - if (se.death_type == KILLED_BY_MONSTER) +long scorefile_entry::long_field(const std::string &s) const +{ + std::string field = str_field(s); + return atol(field.c_str()); +} + +void scorefile_entry::map_fields() +{ + fieldmap.reset(new hs_map); + for (int i = 0, size = fields->size(); i < size; ++i) { - if (strstr(inbuf, "killed by") == NULL) - se.death_type = KILLED_BY_SOMETHING; + const std::pair<std::string, std::string> f = (*fields)[i]; + + (*fieldmap)[f.first] = f.second; } +} - // set some fields - se.death_source = 0; - se.mon_num = 0; - *se.death_source_name = 0; +static const char *short_branch_name(int branch) +{ + if (branch >= 0 && branch < NUM_BRANCHES) + return branches[branch].abbrevname; + return (""); +} - // now try to pull the monster out. - // [dshaligram] Holy brain damage, Batman. - if (se.death_type == KILLED_BY_MONSTER || se.death_type == KILLED_BY_BEAM) +static int str_to_branch(const std::string &branch) +{ + for (int i = 0; i < NUM_BRANCHES; ++i) { - char *p = strstr(inbuf, " by "); - if (p) - { - p += 4; - char *q = strstr(inbuf, " on "); - if (q == NULL) - q = strstr(inbuf, " in "); - - if (q && q > p) - { - char *d = se.death_source_name; - const int maxread = sizeof(se.death_source_name) - 1; + if (branches[i].abbrevname && branches[i].abbrevname == branch) + return (i); + } + return (BRANCH_MAIN_DUNGEON); +} - while (p < q && (d - se.death_source_name) < maxread) - *d++ = *p++; +static const char *level_type_names[] = +{ + "D", "Lab", "Abyss", "Pan" +}; - *d = 0; - } - } - } +static const char *level_area_type_name(int level_type) +{ + if (level_type >= 0 && level_type < NUM_LEVEL_AREA_TYPES) + return level_type_names[level_type]; + return (""); } -static void hs_search_where(char *inbuf, struct scorefile_entry &se) +static int str_to_level_area_type(const std::string &s) { - char scratch[6]; + for (int i = 0; i < NUM_LEVEL_AREA_TYPES; ++i) + if (s == level_type_names[i]) + return (i); + return (LEVEL_DUNGEON); +} - se.level_type = LEVEL_DUNGEON; - se.branch = BRANCH_MAIN_DUNGEON; - se.dlvl = 0; +static int str_to_god(const std::string &god) +{ + if (god.empty()) + return GOD_NO_GOD; + + for (int i = GOD_NO_GOD; i < NUM_GODS; ++i) + { + if (god_name(i) == god) + return (i); + } + return (GOD_NO_GOD); +} - // early out - if (se.death_type == KILLED_BY_LEAVING - || se.death_type == KILLED_BY_WINNING) - return; +void scorefile_entry::init_with_fields() +{ + map_fields(); + points = long_field("sc"); + name = str_field("name"); + uid = int_field("uid"); + + race = str_to_species(str_field("race")); + cls = get_class_index_by_name(str_field("cls").c_str()); + + lvl = int_field("xl"); + + best_skill = str_to_skill(str_field("sk")); + best_skill_lvl = int_field("sklev"); + death_type = str_to_kill_method(str_field("ktyp")); + death_source_name = str_field("killer"); + + read_auxkill_field(); + + branch = str_to_branch(str_field("br")); + dlvl = int_field("lvl"); + level_type = str_to_level_area_type(str_field("ltyp")); + + final_hp = int_field("hp"); + final_max_hp = int_field("mhp"); + final_max_max_hp = int_field("mmhp"); + damage = int_field("dam"); + str = int_field("str"); + intel = int_field("int"); + dex = int_field("dex"); + + god = str_to_god(str_field("god")); + piety = int_field("piety"); + penance = int_field("pen"); + wiz_mode = int_field("wiz"); + birth_time = parse_time(str_field("start")); + death_time = parse_time(str_field("end")); + real_time = long_field("dur"); + num_turns = long_field("turn"); + num_diff_runes = int_field("urune"); + num_runes = int_field("nrune"); +} - // here we go again. - if (strstr(inbuf, "in the Abyss") != NULL) - se.level_type = LEVEL_ABYSS; - else if (strstr(inbuf, "in Pandemonium") != NULL) - se.level_type = LEVEL_PANDEMONIUM; - else if (strstr(inbuf, "in a labyrinth") != NULL) - se.level_type = LEVEL_LABYRINTH; +void scorefile_entry::set_score_fields() const +{ + fields.reset(new hs_fields()); - // early out for special level types - if (se.level_type != LEVEL_DUNGEON) + if (!fields.get()) return; - // check for vestibule - if (strstr(inbuf, "in the Vestibule") != NULL) - { - se.branch = BRANCH_VESTIBULE_OF_HELL; - return; - } + add_field("v", VER_NUM); + add_field("lv", SCORE_VERSION); + add_field("sc", "%ld", points); + add_field("name", "%s", name.c_str()); + add_field("uid", "%d", uid); + add_field("race", "%s", species_name(race, lvl)); + add_field("cls", "%s", get_class_name(cls)); + add_field("xl", "%d", lvl); + add_field("sk", "%s", skill_name(best_skill)); + add_field("sklev", "%d", best_skill_lvl); + add_field("title", "%s", skill_title( best_skill, best_skill_lvl, + race, str, dex, god ) ); + add_field("ktyp", ::kill_method_name(kill_method_type(death_type))); + add_field("killer", death_source_desc()); + + add_auxkill_field(); + + add_field("place", "%s", + place_name(get_packed_place(branch, dlvl, level_type), + false, true).c_str()); + add_field("br", "%s", short_branch_name(branch)); + add_field("lvl", "%d", dlvl); + add_field("ltyp", "%s", level_area_type_name(level_type)); + + add_field("hp", "%d", final_hp); + add_field("mhp", "%d", final_max_hp); + add_field("mmhp", "%d", final_max_max_hp); + add_field("dam", "%d", damage); + add_field("str", "%d", str); + add_field("int", "%d", intel); + add_field("dex", "%d", dex); + + // Don't write No God to save some space. + if (god != -1) + add_field("god", "%s", god == GOD_NO_GOD? "" : god_name(god)); + if (piety > 0) + add_field("piety", "%d", piety); + if (penance > 0) + add_field("pen", "%d", penance); + if (wiz_mode) + add_field("wiz", "%d", wiz_mode); + + add_field("start", "%s", make_date_string(birth_time).c_str()); + add_field("end", "%s", make_date_string(death_time).c_str()); + add_field("dur", "%ld", real_time); + add_field("turn", "%ld", num_turns); + + if (num_diff_runes) + add_field("urune", "%d", num_diff_runes); + + if (num_runes) + add_field("nrune", "%d", num_runes); + +#ifdef DGL_EXTENDED_LOGFILES + const std::string short_msg = short_kill_message(); + add_field("tmsg", "%s", short_msg.c_str()); + const std::string long_msg = long_kill_message(); + if (long_msg != short_msg) + add_field("vmsg", "%s", long_msg.c_str()); +#endif +} - // from here, we have branch and level. - char *p = strstr(inbuf, "on L"); - if (p != NULL) +std::string scorefile_entry::make_oneline(const std::string &ml) const +{ + std::vector<std::string> lines = split_string(EOL, ml); + for (int i = 0, size = lines.size(); i < size; ++i) { - p += 4; - hs_parse_generic_2(p, scratch, sizeof scratch, "0123456789"); - se.dlvl = atoi( scratch ); + std::string &s = lines[i]; + if (s.find("...") == 0) + { + s = s.substr(3); + trim_string(s); + } } + return comma_separated_line(lines.begin(), lines.end(), " ", " "); +} - // get branch. - if (strstr(inbuf, "of Dis") != NULL) - se.branch = BRANCH_DIS; - else if (strstr(inbuf, "of Gehenna") != NULL) - se.branch = BRANCH_GEHENNA; - else if (strstr(inbuf, "of Cocytus") != NULL) - se.branch = BRANCH_COCYTUS; - else if (strstr(inbuf, "of Tartarus") != NULL) - se.branch = BRANCH_TARTARUS; - else if (strstr(inbuf, "of the Mines") != NULL) - se.branch = BRANCH_ORCISH_MINES; - else if (strstr(inbuf, "of the Hive") != NULL) - se.branch = BRANCH_HIVE; - else if (strstr(inbuf, "of the Lair") != NULL) - se.branch = BRANCH_LAIR; - else if (strstr(inbuf, "of the Slime Pits") != NULL) - se.branch = BRANCH_SLIME_PITS; - else if (strstr(inbuf, "of the Vaults") != NULL) - se.branch = BRANCH_VAULTS; - else if (strstr(inbuf, "of the Crypt") != NULL) - se.branch = BRANCH_CRYPT; - else if (strstr(inbuf, "of the Hall") != NULL) - se.branch = BRANCH_HALL_OF_BLADES; - else if (strstr(inbuf, "of Zot's Hall") != NULL) - se.branch = BRANCH_HALL_OF_ZOT; - else if (strstr(inbuf, "of the Temple") != NULL) - se.branch = BRANCH_ECUMENICAL_TEMPLE; - else if (strstr(inbuf, "of the Snake Pit") != NULL) - se.branch = BRANCH_SNAKE_PIT; - else if (strstr(inbuf, "of the Elf Hall") != NULL) - se.branch = BRANCH_ELVEN_HALLS; - else if (strstr(inbuf, "of the Tomb") != NULL) - se.branch = BRANCH_TOMB; - else if (strstr(inbuf, "of the Swamp") != NULL) - se.branch = BRANCH_SWAMP; +std::string scorefile_entry::long_kill_message() const +{ + std::string msg = death_description(DDV_LOGVERBOSE); + msg = make_oneline(msg); + msg[0] = tolower(msg[0]); + trim_string(msg); + return (msg); } -////////////////////////////////////////////////////////////////////////// -// scorefile_entry +std::string scorefile_entry::short_kill_message() const +{ + std::string msg = death_description(DDV_ONELINE); + msg = make_oneline(msg); + msg[0] = tolower(msg[0]); + trim_string(msg); + return (msg); +} -scorefile_entry::scorefile_entry(int dam, int dsource, int dtype, - const char *aux, bool death_cause_only) +// Maps a 0.1.x branch id to a 0.2 branch id. Ugh. Fortunately we need this +// only to read old logfiles/scorefiles. +int scorefile_entry::kludge_branch(int branch_01) const { - reset(); + static int branch_map[] = { + BRANCH_MAIN_DUNGEON, BRANCH_DIS, BRANCH_GEHENNA, + BRANCH_VESTIBULE_OF_HELL, BRANCH_COCYTUS, BRANCH_TARTARUS, + BRANCH_INFERNO, BRANCH_THE_PIT, BRANCH_MAIN_DUNGEON, + BRANCH_MAIN_DUNGEON, BRANCH_ORCISH_MINES, BRANCH_HIVE, + BRANCH_LAIR, BRANCH_SLIME_PITS, BRANCH_VAULTS, BRANCH_CRYPT, + BRANCH_HALL_OF_BLADES, BRANCH_HALL_OF_ZOT, BRANCH_ECUMENICAL_TEMPLE, + BRANCH_SNAKE_PIT, BRANCH_ELVEN_HALLS, BRANCH_TOMB, BRANCH_SWAMP, + BRANCH_CAVERNS + }; - init_death_cause(dam, dsource, dtype, aux); - if (!death_cause_only) - init(); + if (branch_01 < 0 + || branch_01 > (int) (sizeof(branch_map) / sizeof(*branch_map))) + { + return (BRANCH_MAIN_DUNGEON); + } + return branch_map[branch_01]; } -scorefile_entry::scorefile_entry() +// [ds] This is the 4.0 b26 logfile parser. Old-style logs are now deprecated; +// support for reading them may be discontinued in the next version. +bool scorefile_entry::parse_obsolete_scoreline(const std::string &line) { - // Completely uninitialized, caveat user. - reset(); + const char *inbuf = line.c_str(); + + version = hs_nextint(inbuf); + release = hs_nextint(inbuf); + + // this would be a good point to check for version numbers and branch + // appropriately + + // acceptable versions are 0 (converted from old hiscore format) and 4 + if (version != 4 || release < 2) + return (false); + + points = hs_nextlong(inbuf); + + name = hs_nextstring(inbuf); + + uid = hs_nextlong(inbuf); + race = hs_nextint(inbuf); + cls = hs_nextint(inbuf); + + race_class_name = hs_nextstring(inbuf, 6); + + lvl = hs_nextint(inbuf); + best_skill = hs_nextint(inbuf); + best_skill_lvl = hs_nextint(inbuf); + death_type = hs_nextint(inbuf); + death_source = hs_nextint(inbuf); + mon_num = hs_nextint(inbuf); + + death_source_name = hs_nextstring(inbuf); + + // To try and keep the scorefile backwards compatible, + // we'll branch on version > 4.0 to read the auxkilldata + // text field. + if (version == 4 && release >= 1) + auxkilldata = hs_nextstring( inbuf, ITEMNAME_SIZE ); + else + auxkilldata[0] = 0; + + dlvl = hs_nextint(inbuf); + level_type = hs_nextint(inbuf); + branch = kludge_branch( hs_nextint(inbuf) ); + + final_hp = hs_nextint(inbuf); + final_max_hp = hs_nextint(inbuf); + final_max_max_hp = hs_nextint(inbuf); + damage = hs_nextint(inbuf); + str = hs_nextint(inbuf); + intel = hs_nextint(inbuf); + dex = hs_nextint(inbuf); + god = hs_nextint(inbuf); + piety = hs_nextint(inbuf); + penance = hs_nextint(inbuf); + + wiz_mode = hs_nextint(inbuf); + + birth_time = hs_nextdate(inbuf); + death_time = hs_nextdate(inbuf); + + real_time = hs_nextint(inbuf); + num_turns = hs_nextint(inbuf); + + num_diff_runes = hs_nextint(inbuf); + num_runes = hs_nextint(inbuf); + + return (true); } void scorefile_entry::init_death_cause(int dam, int dsrc, @@ -1116,15 +1204,12 @@ void scorefile_entry::init_death_cause(int dam, int dsrc, // Set the default aux data value... // If aux is passed in (ie for a trap), we'll default to that. if (aux == NULL) - auxkilldata[0] = 0; + auxkilldata.clear(); else - { - strncpy( auxkilldata, aux, ITEMNAME_SIZE ); - auxkilldata[ ITEMNAME_SIZE - 1 ] = 0; - } + auxkilldata = aux; // for death by monster - if ((death_type == KILLED_BY_MONSTER || death_type == KILLED_BY_BEAM) + if ((death_type == KILLED_BY_MONSTER || death_type == KILLED_BY_BEAM) && death_source >= 0 && death_source < MAX_MONSTERS) { const monsters *monster = &menv[death_source]; @@ -1167,21 +1252,13 @@ void scorefile_entry::init_death_cause(int dam, int dsrc, // Setting this is redundant for dancing weapons, however // we do care about the above indentification. -- bwr if (monster->type != MONS_DANCING_WEAPON) - { - it_name( monster->inv[MSLOT_WEAPON], - DESC_NOCAP_A, - info ); - strncpy( auxkilldata, info, ITEMNAME_SIZE ); - auxkilldata[ ITEMNAME_SIZE - 1 ] = 0; - } + auxkilldata = it_name( monster->inv[MSLOT_WEAPON], + DESC_NOCAP_A ); } - - strcpy( info, - monam( monster->number, monster->type, true, DESC_NOCAP_A, - monster->inv[MSLOT_WEAPON] ) ); - strncpy( death_source_name, info, 40 ); - death_source_name[39] = 0; + death_source_name = + monam( monster->number, monster->type, true, DESC_NOCAP_A, + monster->inv[MSLOT_WEAPON] ); } } else @@ -1244,8 +1321,7 @@ void scorefile_entry::init() version = 4; release = 2; - strncpy( name, you.your_name, sizeof name ); - name[ sizeof(name) - 1 ] = 0; + name = you.your_name; #ifdef MULTIUSER uid = (int) getuid(); @@ -1416,9 +1492,9 @@ const char *scorefile_entry::death_source_desc() const if (death_type != KILLED_BY_MONSTER && death_type != KILLED_BY_BEAM) return (""); - return (*death_source_name? - death_source_name - : monam( mon_num, death_source, true, DESC_PLAIN ) ); + return (!death_source_name.empty()? + death_source_name.c_str() + : monam( mon_num, death_source, true, DESC_NOCAP_A ) ); } std::string scorefile_entry::damage_string(bool terse) const @@ -1438,26 +1514,15 @@ std::string scorefile_entry::strip_article_a(const std::string &s) const return (s); } -std::string scorefile_entry::terse_missile_cause() const +std::string scorefile_entry::terse_missile_name() const { - std::string cause; - std::string aux = auxkilldata; - - std::string monster_prefix = " by "; - // We're looking for Shot with a%s %s by %s/ Hit by a%s %s thrown by %s - std::string::size_type by = aux.rfind(monster_prefix); - if (by == std::string::npos) - return ("???"); - - std::string mcause = aux.substr(by + monster_prefix.length()); - mcause = strip_article_a(mcause); - - std::string missile; const std::string pre_post[][2] = { { "Shot with a", " by " }, { "Hit by a", " thrown by " } }; - + const std::string &aux = auxkilldata; + std::string missile; + for (unsigned i = 0; i < sizeof(pre_post) / sizeof(*pre_post); ++i) { if (aux.find(pre_post[i][0]) != 0) @@ -1475,6 +1540,24 @@ std::string scorefile_entry::terse_missile_cause() const if (missile.find("n ") == 0) missile = missile.substr(2); } + return (missile); +} + +std::string scorefile_entry::terse_missile_cause() const +{ + std::string cause; + const std::string &aux = auxkilldata; + + std::string monster_prefix = " by "; + // We're looking for Shot with a%s %s by %s/ Hit by a%s %s thrown by %s + std::string::size_type by = aux.rfind(monster_prefix); + if (by == std::string::npos) + return ("???"); + + std::string mcause = aux.substr(by + monster_prefix.length()); + mcause = strip_article_a(mcause); + + std::string missile = terse_missile_name(); if (missile.length()) mcause += "/" + missile; @@ -1497,7 +1580,7 @@ std::string scorefile_entry::terse_wild_magic() const std::string scorefile_entry::terse_trap() const { - std::string trap = *auxkilldata? std::string(auxkilldata) + " trap" + std::string trap = !auxkilldata.empty()? auxkilldata + " trap" : "trap"; if (trap.find("n ") == 0) trap = trap.substr(2); @@ -1508,29 +1591,27 @@ std::string scorefile_entry::terse_trap() const std::string scorefile_entry::single_cdesc() const { - char scratch[INFO_SIZE]; - char buf[INFO_SIZE]; + std::string char_desc; - if (!*race_class_name) + if (race_class_name.empty()) { - snprintf( scratch, sizeof(scratch), "%s%s", - get_species_abbrev( race ), get_class_abbrev( cls ) ); + char_desc = + make_stringf("%s%s", + get_species_abbrev( race ), + get_class_abbrev( cls ) ); } else { - strncpy( scratch, race_class_name, sizeof scratch ); - scratch[ sizeof(scratch) - 1 ] = 0; + char_desc = race_class_name; } std::string scname = name; if (scname.length() > 10) scname = scname.substr(0, 10); - snprintf( buf, sizeof buf, + return make_stringf( "%8ld %-10s %s-%02d%s", points, scname.c_str(), - scratch, lvl, (wiz_mode == 1) ? "W" : "" ); - - return (buf); + char_desc.c_str(), lvl, (wiz_mode == 1) ? "W" : "" ); } std::string @@ -1554,14 +1635,14 @@ scorefile_entry::character_description(death_desc_verbosity verbosity) const INFO_SIZE ); snprintf( buf, HIGHSCORE_SIZE, "%8ld %s the %s (level %d", - points, name, scratch, lvl ); + points, name.c_str(), scratch, lvl ); desc = buf; } else { snprintf( buf, HIGHSCORE_SIZE, "%8ld %s the %s %s (level %d", - points, name, species_name(race, lvl), + points, name.c_str(), species_name(race, lvl), get_class_name(cls), lvl ); desc = buf; } @@ -1690,9 +1771,10 @@ scorefile_entry::death_description(death_desc_verbosity verbosity) const bool needs_called_by_monster_line = false; bool needs_damage = false; - bool terse = verbosity == DDV_TERSE; - bool verbose = verbosity == DDV_VERBOSE; - bool oneline = verbosity == DDV_ONELINE; + const bool terse = verbosity == DDV_TERSE; + const bool semiverbose = verbosity == DDV_LOGVERBOSE; + const bool verbose = verbosity == DDV_VERBOSE || semiverbose; + const bool oneline = verbosity == DDV_ONELINE; char scratch[INFO_SIZE]; @@ -1733,20 +1815,29 @@ scorefile_entry::death_description(death_desc_verbosity verbosity) const { snprintf( scratch, sizeof(scratch), "%scloud of %s", terse? "" : "Engulfed by a ", - auxkilldata ); + auxkilldata.c_str() ); desc += scratch; } needs_damage = true; break; case KILLED_BY_BEAM: - if (oneline) + if (oneline || semiverbose) { // keeping this short to leave room for the deep elf spellcasters: snprintf( scratch, sizeof(scratch), "%s by ", - range_type_verb( auxkilldata ) ); + range_type_verb( auxkilldata.c_str() ) ); desc += scratch; desc += death_source_desc(); + + if (semiverbose) + { + std::string beam = terse_missile_name(); + if (beam.empty()) + beam = terse_beam_cause(); + if (!beam.empty()) + desc += make_stringf(" (%s)", beam.c_str()); + } } else if (isupper( auxkilldata[0] )) // already made (ie shot arrows) { @@ -1755,24 +1846,27 @@ scorefile_entry::death_description(death_desc_verbosity verbosity) const desc += terse? terse_missile_cause() : auxkilldata; needs_damage = true; } - else if (verbose && strncmp( auxkilldata, "by ", 3 ) == 0) + else if (verbose && auxkilldata.find("by ") == 0) { // "by" is used for priest attacks where the effect is indirect // in verbose format we have another line for the monster needs_called_by_monster_line = true; - snprintf( scratch, sizeof(scratch), "Killed %s", auxkilldata ); + snprintf( scratch, sizeof(scratch), "Killed %s", + auxkilldata.c_str() ); desc += scratch; } else { // Note: This is also used for the "by" cases in non-verbose // mode since listing the monster is more imporatant. - if (!terse) + if (semiverbose) + desc += "Killed by "; + else if (!terse) desc += "Killed from afar by "; desc += death_source_desc(); - if (*auxkilldata) + if (!auxkilldata.empty()) needs_beam_cause_line = true; needs_damage = true; @@ -1822,8 +1916,9 @@ scorefile_entry::death_description(death_desc_verbosity verbosity) const desc += terse_trap(); else { - snprintf( scratch, sizeof(scratch), "Killed by triggering a%s trap", - (*auxkilldata) ? auxkilldata : "" ); + snprintf( scratch, sizeof(scratch), + "Killed by triggering a%s trap", + auxkilldata.c_str() ); desc += scratch; } needs_damage = true; @@ -1870,7 +1965,7 @@ scorefile_entry::death_description(death_desc_verbosity verbosity) const break; case KILLED_BY_WILD_MAGIC: - if (!*auxkilldata) + if (auxkilldata.empty()) desc += terse? "wild magic" : "Killed by wild magic"; else { @@ -1880,8 +1975,8 @@ scorefile_entry::death_description(death_desc_verbosity verbosity) const { // A lot of sources for this case... some have "by" already. snprintf( scratch, sizeof(scratch), "Killed %s%s", - (strncmp( auxkilldata, "by ", 3 ) != 0) ? "by " : "", - auxkilldata ); + (auxkilldata.find("by ") != 0) ? "by " : "", + auxkilldata.c_str() ); desc += scratch; } } @@ -1925,7 +2020,7 @@ scorefile_entry::death_description(death_desc_verbosity verbosity) const /* case 26 */ case KILLED_BY_SOMETHING: - if (auxkilldata) + if (!auxkilldata.empty()) desc += auxkilldata; else desc += terse? "died" : "Died"; @@ -1965,7 +2060,7 @@ scorefile_entry::death_description(death_desc_verbosity verbosity) const // TODO: Eventually, get rid of "..." for cases where the text fits. if (terse) { - if (death_type == KILLED_BY_MONSTER && *auxkilldata) + if (death_type == KILLED_BY_MONSTER && !auxkilldata.empty()) { desc += "/"; desc += strip_article_a(auxkilldata); @@ -1983,7 +2078,7 @@ scorefile_entry::death_description(death_desc_verbosity verbosity) const { bool done_damage = false; // paranoia - if (needs_damage && damage > 0) + if (!semiverbose && needs_damage && damage > 0) { desc += " " + damage_string(); needs_damage = false; @@ -2001,14 +2096,15 @@ scorefile_entry::death_description(death_desc_verbosity verbosity) const num_runes, (num_runes > 1) ? "s" : "" ); desc += scratch; - if (num_diff_runes > 1) + if (!semiverbose && num_diff_runes > 1) { snprintf( scratch, INFO_SIZE, " (of %d types)", num_diff_runes ); desc += scratch; } - if (death_time > 0 + if (!semiverbose + && death_time > 0 && !hiscore_same_day( birth_time, death_time )) { desc += " on "; @@ -2025,30 +2121,40 @@ scorefile_entry::death_description(death_desc_verbosity verbosity) const if (death_type == KILLED_BY_MONSTER && auxkilldata[0]) { - snprintf(scratch, INFO_SIZE, "... wielding %s", auxkilldata); - desc += scratch; - needs_damage = true; + if (!semiverbose) + { + snprintf(scratch, INFO_SIZE, "... wielding %s", + auxkilldata.c_str()); + desc += scratch; + needs_damage = true; + } } else if (needs_beam_cause_line) { - desc += (is_vowel( auxkilldata[0] )) ? "... with an " - : "... with a "; - desc += auxkilldata; - needs_damage = true; + if (!semiverbose) + { + desc += (is_vowel( auxkilldata[0] )) ? "... with an " + : "... with a "; + desc += auxkilldata; + needs_damage = true; + } } else if (needs_called_by_monster_line) { - snprintf( scratch, sizeof(scratch), "... called down by %s", - death_source_name ); + snprintf( scratch, sizeof(scratch), "... invoked by %s", + death_source_name.c_str() ); desc += scratch; needs_damage = true; } - if (needs_damage && !done_damage && damage > 0) - desc += " " + damage_string(); + if (!semiverbose) + { + if (needs_damage && !done_damage && damage > 0) + desc += " " + damage_string(); - if (needs_damage) - desc += hiscore_newline_string(); + if (needs_damage) + desc += hiscore_newline_string(); + } } } diff --git a/crawl-ref/source/initfile.cc b/crawl-ref/source/initfile.cc index f73c7bd1d5..01764ac3ba 100644 --- a/crawl-ref/source/initfile.cc +++ b/crawl-ref/source/initfile.cc @@ -290,7 +290,7 @@ static char str_to_race( const std::string &str ) return ((index != -1) ? index_to_letter( index - 1 ) : 0); } -static char str_to_class( const std::string &str ) +static int str_to_class( const std::string &str ) { int index = -1; @@ -702,7 +702,7 @@ void game_options::reset_options() // These are only used internally, and only from the commandline: // XXX: These need a better place. sc_entries = 0; - sc_format = SCORE_REGULAR; + sc_format = -1; friend_brand = CHATTR_NORMAL; heap_brand = CHATTR_NORMAL; @@ -2406,6 +2406,8 @@ bool parse_args( int argc, char **argv, bool rc_only ) Options.sc_format = SCORE_TERSE; else if (o == CLO_VSCORES) Options.sc_format = SCORE_VERBOSE; + else if (o == CLO_SCORES) + Options.sc_format = SCORE_REGULAR; } break; diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc index 2aa2d363cb..94542fff02 100644 --- a/crawl-ref/source/misc.cc +++ b/crawl-ref/source/misc.cc @@ -1574,7 +1574,8 @@ bool fall_into_a_pool( int entry_x, int entry_y, bool allow_shift, { // back out the way we came in, if possible if (grid_distance( you.x_pos, you.y_pos, entry_x, entry_y ) == 1 - && (entry_x != empty[0] || entry_y != empty[1])) + && (entry_x != empty[0] || entry_y != empty[1]) + && mgrd[entry_x][entry_y] == NON_MONSTER) { escape = true; empty[0] = entry_x; @@ -1991,13 +1992,13 @@ unsigned short get_packed_place() bool single_level_branch( int branch ) { return - branch == BRANCH_VESTIBULE_OF_HELL || - branch == BRANCH_HALL_OF_BLADES || - branch == BRANCH_ECUMENICAL_TEMPLE; + branch >= 0 && branch < NUM_BRANCHES + && branches[branch].depth == 1; } std::string place_name( unsigned short place, bool long_name, - bool include_number ) { + bool include_number ) +{ unsigned char branch = (unsigned char) ((place >> 8) & 0xFF); int lev = place & 0xFF; diff --git a/crawl-ref/source/misc.h b/crawl-ref/source/misc.h index 20efd35732..b4fbfeb902 100644 --- a/crawl-ref/source/misc.h +++ b/crawl-ref/source/misc.h @@ -172,7 +172,6 @@ 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, bool include_number = true ); -std::string branch_name( int branch, bool terse ); // Prepositional form of branch level name. For example, "in the // Abyss" or "on level 3 of the Main Dungeon". diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h index 714d38abf0..c52a17913c 100644 --- a/crawl-ref/source/mon-data.h +++ b/crawl-ref/source/mon-data.h @@ -3395,7 +3395,7 @@ M_NO_SKELETON | M_SEE_INVIS, MR_RES_POISON | MR_RES_ASPHYX, 0, 5, MONS_JELLY, MONS_OOZE, MH_NATURAL, -6, - { {AT_HIT, AF_ACID, 5}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} }, + { {AT_HIT, AF_PLAIN, 5}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} }, { 3, 3, 5, 0 }, 1, 3, 8, 7, MST_NO_SPELLS, CE_POISONOUS, Z_NOZOMBIE, S_SILENT, I_PLANT, MONUSE_NOTHING, SIZE_LITTLE diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc index b0f932e8f5..c5882d783a 100644 --- a/crawl-ref/source/monstuff.cc +++ b/crawl-ref/source/monstuff.cc @@ -1167,18 +1167,23 @@ static bool habitat_okay( struct monsters *monster, int targ ) // swap pets to their death, we can let that go). -- bwr bool swap_places(struct monsters *monster) { - bool swap; - int loc_x = you.x_pos; int loc_y = you.y_pos; const int mgrid = grd[monster->x][monster->y]; - swap = habitat_okay( monster, grd[loc_x][loc_y] ) - && !is_grid_dangerous(mgrid); + const bool mon_dest_okay = habitat_okay( monster, grd[loc_x][loc_y] ); + const bool you_dest_okay = + !is_grid_dangerous(mgrid) + || yesno("Do you really want to step there?", false, 'n'); + + if (!you_dest_okay) + return (false); + + bool swap = mon_dest_okay; // chose an appropiate habitat square at random around the target. - if (!swap && !is_grid_dangerous(mgrid)) + if (!swap) { int num_found = 0; int temp_x, temp_y; @@ -4534,9 +4539,8 @@ void mons_check_pool(monsters *mons, int killer) return; const int grid = grd(mons->pos()); - const int native_habitat = monster_habitat(mons->type); if ((grid == DNGN_LAVA || grid == DNGN_DEEP_WATER) - && grid != native_habitat) + && !monster_habitable_grid(mons, grid)) { const bool message = mons_near(mons); @@ -4549,8 +4553,7 @@ void mons_check_pool(monsters *mons, int killer) // Even fire resistant monsters perish in lava, but undead can survive // deep water. - if (!monster_habitable_grid(mons, grid) - && (grid == DNGN_LAVA || mons->holiness() != MH_UNDEAD)) + if (grid == DNGN_LAVA || mons->holiness() != MH_UNDEAD) { if (message) { diff --git a/crawl-ref/source/mstuff2.cc b/crawl-ref/source/mstuff2.cc index 93ac6f69f8..ec44e6c55b 100644 --- a/crawl-ref/source/mstuff2.cc +++ b/crawl-ref/source/mstuff2.cc @@ -1424,7 +1424,7 @@ bolt mons_spells( int spell_cast, int power ) beam.type = SYM_MISSILE; beam.thrower = KILL_MON; beam.flavour = BEAM_POISON_ARROW; - beam.hit = 14 + power / 25; + beam.hit = 12 + power / 25; beam.range = beam.rangeMax = 8; break; diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index 983b8403f1..bb4cb683dc 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -327,7 +327,7 @@ bool is_grid_dangerous(int grid) { return (!player_is_levitating() && (grid == DNGN_LAVA - || grid == DNGN_DEEP_WATER && !player_can_swim())); + || (grid == DNGN_DEEP_WATER && !player_can_swim()))); } bool player_in_mappable_area( void ) @@ -3247,6 +3247,27 @@ void redraw_skill(const char your_name[kNameLen], const char class_name[80]) cprintf( "%s", print_it ); } // end redraw_skill() +// Does a case-sensitive lookup of the species name supplied. +int str_to_species(const std::string &species) +{ + if (species.empty()) + return SP_HUMAN; + + for (int i = SP_HUMAN; i < NUM_SPECIES; ++i) + { + if (species == species_name(i, 10)) + return (i); + } + + for (int i = SP_HUMAN; i < NUM_SPECIES; ++i) + { + if (species == species_name(i, 1)) + return (i); + } + + return (SP_HUMAN); +} + // Note that this function only has the one static buffer, so if you // want to use the results, you'll want to make a copy. char *species_name( int speci, int level, bool genus, bool adj, bool cap ) @@ -4770,11 +4791,7 @@ int player::damage_type(int) { return (DVORP_SLICING); } - else if (equip[EQ_GLOVES] == -1 && - (attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON - || mutation[MUT_CLAWS] - || species == SP_TROLL - || species == SP_GHOUL)) + else if (has_usable_claws()) { return (DVORP_CLAWING); } @@ -5162,3 +5179,12 @@ void player::slow_down(int str) { ::slow_player( str ); } + +bool player::has_usable_claws() const +{ + return (equip[EQ_GLOVES] == -1 && + (attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON + || mutation[MUT_CLAWS] + || species == SP_TROLL + || species == SP_GHOUL)); +} diff --git a/crawl-ref/source/player.h b/crawl-ref/source/player.h index 8cd5c23ff3..4dd31f58f2 100644 --- a/crawl-ref/source/player.h +++ b/crawl-ref/source/player.h @@ -79,7 +79,7 @@ bool wearing_amulet(char which_am, bool calc_unid = true); * called from: acr - chardump - describe - newgame - view * *********************************************************************** */ char *species_name( int speci, int level, bool genus = false, bool adj = false, bool cap = true ); - +int str_to_species(const std::string &species); /* *********************************************************************** * called from: beam @@ -439,7 +439,6 @@ int get_class_index_by_name( const char *name ); const char *get_class_abbrev( int which_job ); const char *get_class_name( int which_job ); - // last updated 19apr2001 {gdl} /* *********************************************************************** * called from: diff --git a/crawl-ref/source/skills2.cc b/crawl-ref/source/skills2.cc index 0bbc35dace..b325b3a64a 100644 --- a/crawl-ref/source/skills2.cc +++ b/crawl-ref/source/skills2.cc @@ -1994,6 +1994,15 @@ const char *skill_name(int which_skill) return (skills[which_skill][0]); } // end skill_name() +int str_to_skill(const std::string &skill) +{ + for (int i = 0; i < NUM_SKILLS; ++i) + { + if (skills[i][0] && skill == skills[i][0]) + return (i); + } + return (SK_FIGHTING); +} const char *skill_title( unsigned char best_skill, unsigned char skill_lev, int species, int str, int dex, int god ) diff --git a/crawl-ref/source/skills2.h b/crawl-ref/source/skills2.h index 73658f31af..31d0cc8690 100644 --- a/crawl-ref/source/skills2.h +++ b/crawl-ref/source/skills2.h @@ -24,7 +24,7 @@ * called from: chardump - it_use3 - itemname - skills * *********************************************************************** */ const char *skill_name(int which_skill); - +int str_to_skill(const std::string &skill); // last_updated 24may2000 {dlb} /* *********************************************************************** diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc index 6655fec60b..a95aad09d4 100644 --- a/crawl-ref/source/tags.cc +++ b/crawl-ref/source/tags.cc @@ -350,22 +350,25 @@ bool unmarshallBoolean(struct tagHeader &th) } // Saving the date as a string so we're not reliant on a particular epoch. -void make_date_string( time_t in_date, char buff[20] ) +std::string make_date_string( time_t in_date ) { + char buff[20]; + if (in_date <= 0) { buff[0] = 0; - return; + return (buff); } - struct tm *date = localtime( &in_date ); - snprintf( buff, 20, + snprintf( buff, sizeof buff, "%4d%02d%02d%02d%02d%02d%s", date->tm_year + 1900, date->tm_mon, date->tm_mday, date->tm_hour, date->tm_min, date->tm_sec, ((date->tm_isdst > 0) ? "D" : "S") ); + + return (buff); } static int get_val_from_string( const char *ptr, int len ) @@ -613,7 +616,6 @@ void tag_set_expected(char tags[], int fileType) // --------------------------- player tags (foo.sav) -------------------- // static void tag_construct_you(struct tagHeader &th) { - char buff[20]; // used for date string int i,j; marshallString(th, you.your_name, 30); @@ -776,8 +778,7 @@ static void tag_construct_you(struct tagHeader &th) marshallByte(th, you.wizard); // time of game start - make_date_string( you.birth_time, buff ); - marshallString(th, buff, 20); + marshallString(th, make_date_string( you.birth_time ).c_str(), 20); // real_time == -1 means game was started before this feature if (you.real_time != -1) diff --git a/crawl-ref/source/tags.h b/crawl-ref/source/tags.h index 6b1701699e..553cdaee6d 100644 --- a/crawl-ref/source/tags.h +++ b/crawl-ref/source/tags.h @@ -52,7 +52,7 @@ float unmarshallFloat(struct tagHeader &th); bool unmarshallBoolean(struct tagHeader &th); void unmarshallString(struct tagHeader &th, char *data, int maxSize); -void make_date_string( time_t in_date, char buff[20] ); +std::string make_date_string( time_t in_date ); time_t parse_date_string( char[20] ); // last updated 22jan2001 {gdl} diff --git a/crawl-ref/source/travel.h b/crawl-ref/source/travel.h index 28d980baef..c8bca8e0c6 100644 --- a/crawl-ref/source/travel.h +++ b/crawl-ref/source/travel.h @@ -143,6 +143,13 @@ public: int level_type; public: + // Returns the level_id of the current level. + static level_id current(); + + // Returns the level_id of the level that the stair/portal/whatever at + // 'pos' on the current level leads to. + static level_id get_next_level_id(const coord_def &pos); + level_id() : branch(0), depth(-1), level_type(LEVEL_DUNGEON) { } level_id(int br, int dep, int ltype = LEVEL_DUNGEON) : branch(br), depth(dep), level_type(ltype) @@ -160,13 +167,6 @@ public: return (branch != -1 && depth != -1) || level_type != LEVEL_DUNGEON; } - // Returns the level_id of the current level. - static level_id current(); - - // Returns the level_id of the level that the stair/portal/whatever at - // 'pos' on the current level leads to. - static level_id get_next_level_id(const coord_def &pos); - bool operator == ( const level_id &id ) const { return branch == id.branch && depth == id.depth diff --git a/crawl-ref/source/version.h b/crawl-ref/source/version.h index f523ff50d8..6677b4304f 100644 --- a/crawl-ref/source/version.h +++ b/crawl-ref/source/version.h @@ -37,11 +37,14 @@ #define CRAWL "Dungeon Crawl Stone Soup" +#define VER_NUM "0.2" +#define VER_QUAL "-svn" + // last updated 07august2001 {mv} /* *********************************************************************** * called from: chardump - command - newgame * *********************************************************************** */ -#define VERSION "0.2-svn (crawl-ref)" +#define VERSION VER_NUM VER_QUAL " (crawl-ref)" // last updated 20feb2001 {GDL} /* *********************************************************************** |