summaryrefslogtreecommitdiffstats
path: root/crawl-ref
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
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')
-rw-r--r--crawl-ref/source/AppHdr.h39
-rw-r--r--crawl-ref/source/acr.cc2
-rw-r--r--crawl-ref/source/enum.h4
-rw-r--r--crawl-ref/source/externs.h54
-rw-r--r--crawl-ref/source/fight.cc9
-rw-r--r--crawl-ref/source/hiscores.cc1208
-rw-r--r--crawl-ref/source/initfile.cc6
-rw-r--r--crawl-ref/source/misc.cc11
-rw-r--r--crawl-ref/source/misc.h1
-rw-r--r--crawl-ref/source/mon-data.h2
-rw-r--r--crawl-ref/source/monstuff.cc21
-rw-r--r--crawl-ref/source/mstuff2.cc2
-rw-r--r--crawl-ref/source/player.cc38
-rw-r--r--crawl-ref/source/player.h3
-rw-r--r--crawl-ref/source/skills2.cc9
-rw-r--r--crawl-ref/source/skills2.h2
-rw-r--r--crawl-ref/source/tags.cc15
-rw-r--r--crawl-ref/source/tags.h2
-rw-r--r--crawl-ref/source/travel.h14
-rw-r--r--crawl-ref/source/version.h5
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}
/* ***********************************************************************