summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/hiscores.cc
diff options
context:
space:
mode:
authordshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2007-03-08 20:40:24 +0000
committerdshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2007-03-08 20:40:24 +0000
commitb31338b60aefb79b5d31e7bd1e3573c5965047e3 (patch)
tree2f406ecb6e20d62bcac6682636f15f9fd3744ecb /crawl-ref/source/hiscores.cc
parenta1b5f79cc172bddee8b225cd5a6726f6a5451666 (diff)
downloadcrawl-ref-b31338b60aefb79b5d31e7bd1e3573c5965047e3.tar.gz
crawl-ref-b31338b60aefb79b5d31e7bd1e3573c5965047e3.zip
New key=value logfile format as proposed by Shawn Moore. This is more verbose
than the old format by about 2x, but is more maintainable and comprehensible. Removed support for parsing scorefiles/logfiles older than 4.0 beta 26. Added shim to make 0.1.7 logfiles compatible with 0.2 Using the -scorefile option alone (no -scores, -tscores, etc.) causes Crawl to read in the existing scorefile/logfile and write it out to stdout in the new format. Ghouls get claw damage messages in unarmed combat. Plain oozes lose acid damage attacks (added inadvertently). Prompt the user when trying to displace a friendly over water (the old fix was to simply say "The foo resists"). git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@994 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source/hiscores.cc')
-rw-r--r--crawl-ref/source/hiscores.cc1208
1 files changed, 657 insertions, 551 deletions
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();
+ }
}
}