summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2006-10-29 11:33:25 +0000
committerdshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2006-10-29 11:33:25 +0000
commit59ea3af23c9fda27662c4ffd8069ce1237bddcba (patch)
treeaaaf9b6bdf7882f544a9371d354f7532816c3df8
parent28f97347df22169b839f0aa9a1fcc7c0136a0654 (diff)
downloadcrawl-ref-59ea3af23c9fda27662c4ffd8069ce1237bddcba.tar.gz
crawl-ref-59ea3af23c9fda27662c4ffd8069ce1237bddcba.zip
[1581221] When taking a low-hp note, also mention what caused the hp loss.
Fixed torment so it uses ouch() instead of directly doing dec_hp(). Cleaned up the highscore entry code; hopefully nothing's broken. Changed debug.cc so that wizmoded monsters arrive singly instead of in bands. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/branches/stone_soup@304 c06c8d41-db1a-0410-9941-cceddc491573
-rw-r--r--crawl-ref/source/abl-show.cc4
-rw-r--r--crawl-ref/source/debug.cc7
-rw-r--r--crawl-ref/source/decks.cc2
-rw-r--r--crawl-ref/source/effects.cc46
-rw-r--r--crawl-ref/source/effects.h3
-rw-r--r--crawl-ref/source/enum.h8
-rw-r--r--crawl-ref/source/externs.h38
-rw-r--r--crawl-ref/source/fight.cc2
-rw-r--r--crawl-ref/source/hiscores.cc1682
-rw-r--r--crawl-ref/source/hiscores.h4
-rw-r--r--crawl-ref/source/initfile.cc2
-rw-r--r--crawl-ref/source/it_use3.cc4
-rw-r--r--crawl-ref/source/item_use.cc2
-rw-r--r--crawl-ref/source/libutil.cc16
-rw-r--r--crawl-ref/source/libutil.h2
-rw-r--r--crawl-ref/source/mstuff2.cc5
-rw-r--r--crawl-ref/source/notes.cc57
-rw-r--r--crawl-ref/source/ouch.cc244
-rw-r--r--crawl-ref/source/player.cc25
-rw-r--r--crawl-ref/source/player.h4
-rw-r--r--crawl-ref/source/spells2.cc2
-rw-r--r--crawl-ref/source/spl-cast.cc5
22 files changed, 1175 insertions, 989 deletions
diff --git a/crawl-ref/source/abl-show.cc b/crawl-ref/source/abl-show.cc
index ca75c7bd23..4ff7e0c7dc 100644
--- a/crawl-ref/source/abl-show.cc
+++ b/crawl-ref/source/abl-show.cc
@@ -717,7 +717,7 @@ bool activate_ability(void)
return (false);
}
- torment(you.x_pos, you.y_pos);
+ torment(TORMENT_GENERIC, you.x_pos, you.y_pos);
break;
case ABIL_RAISE_DEAD:
@@ -1145,7 +1145,7 @@ bool activate_ability(void)
return (false);
}
- torment(you.x_pos, you.y_pos);
+ torment(TORMENT_GENERIC, you.x_pos, you.y_pos);
exercise(SK_INVOCATIONS, 2 + random2(4));
break;
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index 5249398507..ee41c84964 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -378,11 +378,6 @@ void TRACE(const char *format, ...)
}
#endif // DEBUG
-//---------------------------------------------------------------
-//
-// debug_prompt_for_monster
-//
-//---------------------------------------------------------------
#ifdef WIZARD
static int get_monnum(const char *name)
@@ -651,7 +646,7 @@ void create_spec_monster_name(void)
else
{
create_monster(mon, 0, BEH_SLEEP,
- you.x_pos, you.y_pos, MHITNOT, 250, true);
+ you.x_pos, you.y_pos, MHITNOT, 250, false);
}
} // end create_spec_monster_name()
#endif
diff --git a/crawl-ref/source/decks.cc b/crawl-ref/source/decks.cc
index 789d34a131..8755413256 100644
--- a/crawl-ref/source/decks.cc
+++ b/crawl-ref/source/decks.cc
@@ -776,7 +776,7 @@ static void cards(unsigned char which_card)
case CARD_TORMENT:
mpr("You have drawn the Symbol of Torment.");
- torment( you.x_pos, you.y_pos );
+ torment( TORMENT_CARDS, you.x_pos, you.y_pos );
break;
// what about checking whether there are items there, too? {dlb}
diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc
index 2206044bba..c8f3326279 100644
--- a/crawl-ref/source/effects.cc
+++ b/crawl-ref/source/effects.cc
@@ -46,20 +46,54 @@
// resistance! Even if we used maximum power of 1000, high
// level monsters and characters would save too often. (GDL)
-static int torment_monsters(int x, int y, int pow, int garbage)
+int torment_monsters(int x, int y, int pow, int caster)
{
UNUSED( pow );
- UNUSED( garbage );
// is player?
if (x == you.x_pos && y == you.y_pos)
{
- if (you.is_undead || you.mutation[MUT_TORMENT_RESISTANCE])
+ // [dshaligram] Switched to using ouch() instead of dec_hp so that
+ // notes can also track torment and activities can be interrupted
+ // correctly.
+ int hploss = 0;
+ if (!player_res_torment())
+ {
+ hploss = you.hp / 2 - 1;
+ if (hploss >= you.hp)
+ hploss = you.hp - 1;
+ }
+
+ if (!hploss)
mpr("You feel a surge of unholy energy.");
else
{
mpr("Your body is wracked with pain!");
- dec_hp((you.hp / 2) - 1, false);
+
+ const char *aux = "torment";
+ if (caster < 0)
+ {
+ switch (caster)
+ {
+ case TORMENT_CARDS:
+ case TORMENT_SPELL:
+ aux = "Symbol of Torment";
+ break;
+ case TORMENT_SPWLD:
+ // FIXME: If we ever make any other weapon / randart
+ // eligible to torment, this will be incorrect.
+ aux = "Sceptre of Torment";
+ break;
+ case TORMENT_SCROLL:
+ aux = "scroll of torment";
+ break;
+ }
+ caster = TORMENT_GENERIC;
+ }
+ ouch(hploss, caster,
+ caster != TORMENT_GENERIC? KILLED_BY_MONSTER
+ : KILLED_BY_SOMETHING,
+ aux);
}
return 1;
@@ -85,9 +119,9 @@ static int torment_monsters(int x, int y, int pow, int garbage)
return 1;
}
-void torment(int tx, int ty)
+void torment(int caster, int tx, int ty)
{
- apply_area_within_radius(torment_monsters, tx, ty, 0, 8, 0);
+ apply_area_within_radius(torment_monsters, tx, ty, 0, 8, caster);
} // end torment()
void banished(unsigned char gate_type)
diff --git a/crawl-ref/source/effects.h b/crawl-ref/source/effects.h
index f731d86bf5..44d1ba56a5 100644
--- a/crawl-ref/source/effects.h
+++ b/crawl-ref/source/effects.h
@@ -88,7 +88,8 @@ void yell(void);
* called from: ability - decks - fight - it_use3 - item_use - mstuff2 -
* spell
* *********************************************************************** */
-void torment( int tx, int ty );
+void torment( int caster, int tx, int ty );
+int torment_monsters(int x, int y, int pow, int caster);
#endif
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index eb4823f8a1..e721bb9600 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -3482,6 +3482,14 @@ enum tag_file_type // file types supported by tag system
TAGTYPE_PLAYER_NAME // Used only to read the player name
};
+enum torment_source_type
+{
+ TORMENT_GENERIC = -1,
+ TORMENT_CARDS = -2, // Symbol of torment
+ TORMENT_SPWLD = -3, // Special wield torment
+ TORMENT_SCROLL = -4,
+ TORMENT_SPELL = -5 // SPELL_SYMBOL_OF_TORMENT
+};
enum transformation_type
{
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index 623ea959fa..2f7e8c473b 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -856,6 +856,7 @@ struct tagHeader
struct scorefile_entry
{
+public:
char version;
char release;
long points;
@@ -892,6 +893,43 @@ struct scorefile_entry
long num_turns; // number of turns taken
int num_diff_runes; // number of rune types in inventory
int num_runes; // total number of runes in inventory
+
+public:
+ scorefile_entry();
+ scorefile_entry(int damage, int death_source, int death_type,
+ const char *aux, bool death_cause_only = false);
+
+ void init_death_cause(int damage, int death_source, int death_type,
+ const char *aux);
+ void init();
+
+ enum death_desc_verbosity {
+ DDV_TERSE,
+ DDV_ONELINE,
+ DDV_NORMAL,
+ DDV_VERBOSE
+ };
+
+ 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:
+ std::string single_cdesc() const;
+ std::string strip_article_a(const std::string &s) const;
+ std::string terse_missile_cause() 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;
};
extern bool autoprayer_on; // defined in acr.cc
diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc
index 1cf2a7c8ea..5afad64954 100644
--- a/crawl-ref/source/fight.cc
+++ b/crawl-ref/source/fight.cc
@@ -1107,7 +1107,7 @@ bool you_attack(int monster_attacked, bool unarmed_attacks)
if (you.special_wield == SPWLD_TORMENT)
{
- torment(you.x_pos, you.y_pos);
+ torment(TORMENT_SPWLD, you.x_pos, you.y_pos);
did_god_conduct(DID_UNHOLY, 5);
}
diff --git a/crawl-ref/source/hiscores.cc b/crawl-ref/source/hiscores.cc
index 842678dff6..5f06f925e3 100644
--- a/crawl-ref/source/hiscores.cc
+++ b/crawl-ref/source/hiscores.cc
@@ -37,10 +37,14 @@
#include "hiscores.h"
#include "itemname.h"
+#include "itemprop.h"
+#include "items.h"
+#include "libutil.h"
#include "misc.h"
#include "mon-util.h"
#include "player.h"
#include "religion.h"
+#include "shopping.h"
#include "stuff.h"
#include "tags.h"
#include "view.h"
@@ -246,237 +250,11 @@ static const char *const range_type_verb( const char *const aux )
return ("blasted"); // spells, wands
}
-void hiscores_format_single(char *buf, struct scorefile_entry &se)
+void hiscores_format_single(char *buf, const scorefile_entry &se)
{
- char scratch[100];
-
- // Now that we have a long format, I'm starting to make this
- // more terse, in hopes that it will better fit. -- bwr
-
- // race_class_name overrides race & class
- if (se.race_class_name[0] == 0)
- {
- snprintf( scratch, sizeof(scratch), "%s%s",
- get_species_abbrev( se.race ), get_class_abbrev( se.cls ) );
- }
- else
- {
- strcpy( scratch, se.race_class_name );
- }
-
- se.name[10] = 0;
- sprintf( buf, "%8ld %-10s %s-%02d%s", se.points, se.name,
- scratch, se.lvl, (se.wiz_mode == 1) ? "W" : "" );
-
- // get monster type & number, if applicable
- int mon_type = se.death_source;
- int mon_number = se.mon_num;
-
- // remember -- we have 36 characters (not including initial space):
- switch (se.death_type)
- {
- case KILLED_BY_MONSTER:
- strcat( buf, " slain by " );
-
- // if death_source_name is non-null, override lookup (names might have
- // changed!)
- if (se.death_source_name[0] != 0)
- strcat( buf, se.death_source_name );
- else
- strcat( buf, monam( mon_number, mon_type, true, DESC_PLAIN ) );
-
- break;
-
- case KILLED_BY_POISON:
- //if (dam == -9999) strcat(buf, "an overload of ");
- strcat( buf, " succumbed to poison" );
- break;
-
- case KILLED_BY_CLOUD:
- if (se.auxkilldata[0] == 0)
- strcat( buf, " engulfed by a cloud" );
- else
- {
- const int len = strlen( se.auxkilldata );
-
- // Squeeze out "a cloud of" if required. -- bwr
- snprintf( scratch, sizeof(scratch), " engulfed by %s%s",
- (len < 15) ? "a cloud of " : "",
- se.auxkilldata );
-
- strcat( buf, scratch );
- }
- break;
-
- case KILLED_BY_BEAM:
- // keeping this short to leave room for the deep elf spellcasters:
- snprintf( scratch, sizeof(scratch), " %s by ",
- range_type_verb( se.auxkilldata ) );
- strcat( buf, scratch );
-
- // if death_source_name is non-null, override this
- if (se.death_source_name[0] != 0)
- strcat( buf, se.death_source_name );
- else
- strcat( buf, monam( mon_number, mon_type, true, DESC_PLAIN ) );
- break;
-
-/*
- case KILLED_BY_DEATHS_DOOR:
- // death's door running out - NOTE: This is no longer fatal
- strcat(buf, " ran out of time");
- break;
-*/
-
- case KILLED_BY_CURARE:
- strcat( buf, " asphyxiated");
- break;
-
- case KILLED_BY_LAVA:
- if (se.race == SP_MUMMY)
- strcat( buf, " turned to ash by lava" );
- else
- strcat( buf, " took a swim in lava" );
- break;
-
- case KILLED_BY_WATER:
- if (se.race == SP_MUMMY)
- strcat( buf, " soaked and fell apart" );
- else
- strcat( buf, " drowned" );
- break;
-
- // these three are probably only possible if you wear a ring
- // of >= +3 ability, get drained to 3, then take it off, or have a very
- // low abil and wear a -ve ring. or, as of 2.7x, mutations can cause this
- // Don't forget decks of cards (they have some nasty code for this) -- bwr
- case KILLED_BY_STUPIDITY:
- strcat( buf, " died of stupidity" );
- break;
-
- case KILLED_BY_WEAKNESS:
- strcat( buf, " became too weak to continue" );
- break;
-
- case KILLED_BY_CLUMSINESS:
- strcat( buf, " slipped on a banana peel" );
- break;
-
- case KILLED_BY_TRAP:
- snprintf( scratch, sizeof(scratch), " triggered a%s trap",
- (se.auxkilldata[0] != 0) ? se.auxkilldata : "" );
- strcat( buf, scratch );
- break;
-
- case KILLED_BY_LEAVING:
- strcat( buf, " got out of the dungeon alive" );
- break;
-
- case KILLED_BY_WINNING:
- strcat( buf, " escaped with the Orb!" );
- break;
-
- case KILLED_BY_QUITTING:
- strcat( buf, " quit the game" );
- break;
-
- case KILLED_BY_DRAINING:
- strcat( buf, " drained of all life" );
- break;
-
- case KILLED_BY_STARVATION:
- strcat( buf, " starved to death" );
- break;
-
- case KILLED_BY_FREEZING:
- strcat( buf, " froze to death" );
- break;
-
- case KILLED_BY_BURNING: // only sticky flame
- strcat( buf, " burnt to a crisp" );
- break;
-
- case KILLED_BY_WILD_MAGIC:
- if (se.auxkilldata[0] == 0)
- strcat( buf, " killed by wild magic" );
- else
- {
- const bool need_by = (strncmp( se.auxkilldata, "by ", 3 ) == 0);
- const int len = strlen( se.auxkilldata );
-
- // Squeeze out "killed" if we're getting a bit long. -- bwr
- snprintf( scratch, sizeof(scratch), " %s%s%s",
- (len + 3 * (need_by) < 30) ? "killed" : "",
- (need_by) ? "by " : "",
- se.auxkilldata );
-
- strcat( buf, scratch );
- }
- break;
-
- case KILLED_BY_XOM: // only used for old Xom kills
- strcat( buf, " killed for Xom's enjoyment" );
- break;
-
- case KILLED_BY_STATUE:
- strcat( buf, " killed by a statue" );
- break;
-
- case KILLED_BY_ROTTING:
- strcat( buf, " rotted away" );
- break;
-
- case KILLED_BY_TARGETTING:
- strcat( buf, " killed by bad targeting" );
- break;
-
- case KILLED_BY_SPORE:
- strcat( buf, " killed by an exploding spore" );
- break;
-
- case KILLED_BY_TSO_SMITING:
- strcat( buf, " smote by The Shining One" );
- break;
-
- case KILLED_BY_PETRIFICATION:
- strcat( buf, " turned to stone" );
- break;
-
- case KILLED_BY_MELTING:
- strcat( buf, " melted into a puddle" );
- break;
-
- case KILLED_BY_BLEEDING:
- strcat( buf, " bled to death" );
- break;
-
- case KILLED_BY_SOMETHING:
- strcat( buf, " died" );
- break;
-
- case KILLED_BY_FALLING_DOWN_STAIRS:
- strcat( buf, " fell down a flight of stairs" );
- break;
-
- case KILLED_BY_ACID:
- strcat( buf, " splashed by acid" );
- break;
-
- default:
- strcat( buf, " nibbled to death by software bugs" );
- break;
- } // end switch
-
- if (se.death_type != KILLED_BY_LEAVING && se.death_type != KILLED_BY_WINNING)
- {
- snprintf( scratch, sizeof scratch, " (%s)",
- place_name(get_packed_place(se.branch,se.dlvl,se.level_type),
- false, true).c_str());
-
- strcat( buf, scratch );
- } // endif - killed by winning
-
- return;
+ std::string line = se.hiscore_line(scorefile_entry::DDV_ONELINE);
+ strncpy(buf, line.c_str(), HIGHSCORE_SIZE);
+ buf[HIGHSCORE_SIZE - 1] = 0;
}
static bool hiscore_same_day( time_t t1, time_t t2 )
@@ -502,480 +280,21 @@ static void hiscore_date_string( time_t time, char buff[INFO_SIZE] )
date->tm_mday, date->tm_year + 1900 );
}
-static void hiscore_newline( char *buf, int &line_count )
+static std::string hiscore_newline_string()
{
- strncat( buf, EOL " ", HIGHSCORE_SIZE );
- line_count++;
+ return (EOL " ");
}
-int hiscores_format_single_long( char *buf, struct scorefile_entry &se,
+void hiscores_format_single_long( char *buf, const scorefile_entry &se,
bool verbose )
{
- char scratch[INFO_SIZE];
- int line_count = 1;
-
- // race_class_name could/used to override race & class
- // strcpy(scratch, se.race_class_name);
-
- // Please excuse the following bit of mess in the name of flavour ;)
- if (verbose)
- {
- strncpy( scratch, skill_title( se.best_skill, se.best_skill_lvl,
- se.race, se.str, se.dex, se.god ),
- INFO_SIZE );
-
- snprintf( buf, HIGHSCORE_SIZE, "%8ld %s the %s (level %d",
- se.points, se.name, scratch, se.lvl );
-
- }
- else
- {
- snprintf( buf, HIGHSCORE_SIZE, "%8ld %s the %s %s (level %d",
- se.points, se.name, species_name(se.race, se.lvl),
- get_class_name(se.cls), se.lvl );
- }
-
- if (se.final_max_max_hp > 0) // as the other two may be negative
- {
- snprintf( scratch, INFO_SIZE, ", %d/%d", se.final_hp, se.final_max_hp );
- strncat( buf, scratch, HIGHSCORE_SIZE );
-
- if (se.final_max_hp < se.final_max_max_hp)
- {
- snprintf( scratch, INFO_SIZE, " (%d)", se.final_max_max_hp );
- strncat( buf, scratch, HIGHSCORE_SIZE );
- }
-
- strncat( buf, " HPs", HIGHSCORE_SIZE );
- }
-
- strncat( buf, ((se.wiz_mode) ? ") *WIZ*" : ")"), HIGHSCORE_SIZE );
- hiscore_newline( buf, line_count );
-
- if (verbose)
- {
- const char *const race = species_name( se.race, se.lvl );
-
- snprintf( scratch, INFO_SIZE, "Began as a%s %s %s",
- is_vowel(race[0]) ? "n" : "", race, get_class_name(se.cls) );
- strncat( buf, scratch, HIGHSCORE_SIZE );
-
- if (se.birth_time > 0)
- {
- strncat( buf, " on ", HIGHSCORE_SIZE );
- hiscore_date_string( se.birth_time, scratch );
- strncat( buf, scratch, HIGHSCORE_SIZE );
- }
-
- strncat( buf, "." , HIGHSCORE_SIZE );
- hiscore_newline( buf, line_count );
-
- if (se.race != SP_DEMIGOD && se.god != -1)
- {
- if (se.god == GOD_XOM)
- {
- snprintf( scratch, INFO_SIZE, "Was a %sPlaything of Xom.",
- (se.lvl >= 20) ? "Favourite " : "" );
-
- strncat( buf, scratch, HIGHSCORE_SIZE );
- hiscore_newline( buf, line_count );
- }
- else if (se.god != GOD_NO_GOD)
- {
- // Not exactly the same as the religon screen, but
- // good enough to fill this slot for now.
- snprintf( scratch, INFO_SIZE, "Was %s of %s%s",
- (se.piety > 160) ? "the Champion" :
- (se.piety >= 120) ? "a High Priest" :
- (se.piety >= 100) ? "an Elder" :
- (se.piety >= 75) ? "a Priest" :
- (se.piety >= 50) ? "a Believer" :
- (se.piety >= 30) ? "a Follower"
- : "an Initiate",
- god_name( se.god ),
- (se.penance > 0) ? " (penitent)." : "." );
-
- strncat( buf, scratch, HIGHSCORE_SIZE );
- hiscore_newline( buf, line_count );
- }
- }
- }
-
- // get monster type & number, if applicable
- int mon_type = se.death_source;
- int mon_number = se.mon_num;
-
- bool needs_beam_cause_line = false;
- bool needs_called_by_monster_line = false;
- bool needs_damage = false;
-
- switch (se.death_type)
- {
- case KILLED_BY_MONSTER:
- // GDL: here's an example of using final_hp. Verbiage could be better.
- // bwr: changed "blasted" since this is for melee
- snprintf( scratch, INFO_SIZE, "%s %s",
- (se.final_hp > -6) ? "Slain by" :
- (se.final_hp > -14) ? "Mangled by" :
- (se.final_hp > -22) ? "Demolished by"
- : "Annihilated by",
-
- (se.death_source_name[0] != 0)
- ? se.death_source_name
- : monam( mon_number, mon_type, true, DESC_PLAIN ) );
-
- strncat( buf, scratch, HIGHSCORE_SIZE );
-
- // put the damage on the weapon line if there is one
- if (se.auxkilldata[0] == 0)
- needs_damage = true;
- break;
-
- case KILLED_BY_POISON:
- //if (dam == -9999) strcat(buf, "an overload of ");
- strcat( buf, "Succumbed to poison" );
- break;
-
- case KILLED_BY_CLOUD:
- if (se.auxkilldata[0] == 0)
- strcat( buf, "Engulfed by a cloud" );
- else
- {
- snprintf( scratch, sizeof(scratch), "Engulfed by a cloud of %s",
- se.auxkilldata );
- strcat( buf, scratch );
- }
- needs_damage = true;
- break;
-
- case KILLED_BY_BEAM:
- if (isupper( se.auxkilldata[0] )) // already made (ie shot arrows)
- {
- strcat( buf, se.auxkilldata );
- needs_damage = true;
- }
- else if (verbose && strncmp( se.auxkilldata, "by ", 3 ) == 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", se.auxkilldata );
- strncat( buf, scratch, HIGHSCORE_SIZE );
- }
- else
- {
- // Note: This is also used for the "by" cases in non-verbose
- // mode since listing the monster is more imporatant.
- strcat( buf, "Killed from afar by " );
-
- // if death_source_name is non-null, override this
- if (se.death_source_name[0] != 0)
- strcat(buf, se.death_source_name);
- else
- strcat(buf, monam( mon_number, mon_type, true, DESC_PLAIN ));
-
- if (se.auxkilldata[0] != 0)
- needs_beam_cause_line = true;
- }
- break;
-
- case KILLED_BY_CURARE:
- strcat(buf, "Asphyxiated");
- break;
-
- case KILLED_BY_LAVA:
- if (se.race == SP_MUMMY)
- strcat( buf, "Turned to ash by lava" );
- else
- strcat( buf, "Took a swim in molten lava" );
- break;
-
- case KILLED_BY_WATER:
- if (se.race == SP_MUMMY)
- strcat( buf, "Soaked and fell apart" );
- else
- strcat( buf, "Drowned" );
- break;
-
- case KILLED_BY_STUPIDITY:
- strcat( buf, "Forgot to breathe" );
- break;
-
- case KILLED_BY_WEAKNESS:
- strcat( buf, "Collapsed under their own weight" );
- break;
-
- case KILLED_BY_CLUMSINESS:
- strcat( buf, "Slipped on a banana peel" );
- break;
-
- case KILLED_BY_TRAP:
- snprintf( scratch, sizeof(scratch), "Killed by triggering a%s trap",
- (se.auxkilldata[0] != 0) ? se.auxkilldata : "" );
- strcat( buf, scratch );
- needs_damage = true;
- break;
-
- case KILLED_BY_LEAVING:
- if (se.num_runes > 0)
- strcat( buf, "Got out of the dungeon" );
- else
- strcat( buf, "Got out of the dungeon alive!" );
- break;
-
- case KILLED_BY_WINNING:
- strcat( buf, "Escaped with the Orb" );
- if (se.num_runes < 1)
- strcat( buf, "!" );
- break;
-
- case KILLED_BY_QUITTING:
- strcat( buf, "Quit the game" );
- break;
-
- case KILLED_BY_DRAINING:
- strcat( buf, "Was drained of all life" );
- break;
-
- case KILLED_BY_STARVATION:
- strcat( buf, "Starved to death" );
- break;
-
- case KILLED_BY_FREEZING: // refrigeration spell
- strcat( buf, "Froze to death" );
- needs_damage = true;
- break;
-
- case KILLED_BY_BURNING: // sticky flame
- strcat( buf, "Burnt to a crisp" );
- needs_damage = true;
- break;
-
- case KILLED_BY_WILD_MAGIC:
- if (se.auxkilldata[0] == 0)
- strcat( buf, "Killed by wild magic" );
- else
- {
- // A lot of sources for this case... some have "by" already.
- snprintf( scratch, sizeof(scratch), "Killed %s%s",
- (strncmp( se.auxkilldata, "by ", 3 ) != 0) ? "by " : "",
- se.auxkilldata );
-
- strcat( buf, scratch );
- }
-
- needs_damage = true;
- break;
-
- case KILLED_BY_XOM: // only used for old Xom kills
- strcat( buf, "It was good that Xom killed them" );
- needs_damage = true;
- break;
-
- case KILLED_BY_STATUE:
- strcat( buf, "Killed by a statue" );
- needs_damage = true;
- break;
-
- case KILLED_BY_ROTTING:
- strcat( buf, "Rotted away" );
- break;
-
- case KILLED_BY_TARGETTING:
- strcat( buf, "Killed themselves with bad targeting" );
- needs_damage = true;
- break;
-
- case KILLED_BY_SPORE:
- strcat( buf, "Killed by an exploding spore" );
- needs_damage = true;
- break;
-
- case KILLED_BY_TSO_SMITING:
- strcat( buf, "Smote by The Shining One" );
- needs_damage = true;
- break;
-
- case KILLED_BY_PETRIFICATION:
- strcat( buf, "Turned to stone" );
- break;
-
- case KILLED_BY_MELTING:
- strcat( buf, " melted into a puddle" );
- break;
-
- case KILLED_BY_BLEEDING:
- strcat( buf, " bled to death" );
- break;
-
- case KILLED_BY_SOMETHING:
- strcat( buf, "Died" );
- break;
-
- case KILLED_BY_FALLING_DOWN_STAIRS:
- strcat( buf, "Fell down a flight of stairs" );
- needs_damage = true;
- break;
-
- case KILLED_BY_ACID:
- strcat( buf, "Splashed by acid" );
- needs_damage = true;
- break;
-
- default:
- strcat( buf, "Nibbled to death by software bugs" );
- break;
- } // end switch
-
- // TODO: Eventually, get rid of "..." for cases where the text fits.
- if (verbose)
- {
- bool done_damage = false; // paranoia
-
- if (needs_damage && se.damage > 0)
- {
- snprintf( scratch, INFO_SIZE, " (%d damage)", se.damage );
- strncat( buf, scratch, HIGHSCORE_SIZE );
- needs_damage = false;
- done_damage = true;
- }
-
- if ((se.death_type == KILLED_BY_LEAVING
- || se.death_type == KILLED_BY_WINNING)
- && se.num_runes > 0)
- {
- hiscore_newline( buf, line_count );
-
- snprintf( scratch, INFO_SIZE, "... %s %d rune%s",
- (se.death_type == KILLED_BY_WINNING) ? "and" : "with",
- se.num_runes, (se.num_runes > 1) ? "s" : "" );
- strncat( buf, scratch, HIGHSCORE_SIZE );
-
- if (se.num_diff_runes > 1)
- {
- snprintf( scratch, INFO_SIZE, " (of %d types)",
- se.num_diff_runes );
- strncat( buf, scratch, HIGHSCORE_SIZE );
- }
-
- if (se.death_time > 0
- && !hiscore_same_day( se.birth_time, se.death_time ))
- {
- strcat( buf, " on " );
- hiscore_date_string( se.death_time, scratch );
- strcat( buf, scratch );
- }
-
- strcat( buf, "!" );
- hiscore_newline( buf, line_count );
- }
- else if (se.death_type != KILLED_BY_QUITTING)
- {
- hiscore_newline( buf, line_count );
-
- if (se.death_type == KILLED_BY_MONSTER && se.auxkilldata[0])
- {
- snprintf(scratch, INFO_SIZE, "... wielding %s", se.auxkilldata);
- strncat(buf, scratch, HIGHSCORE_SIZE);
- needs_damage = true;
- }
- else if (needs_beam_cause_line)
- {
- strcat( buf, (is_vowel( se.auxkilldata[0] )) ? "... with an "
- : "... with a " );
- strcat( buf, se.auxkilldata );
- needs_damage = true;
- }
- else if (needs_called_by_monster_line)
- {
- snprintf( scratch, sizeof(scratch), "... called down by %s",
- se.death_source_name );
- strncat( buf, scratch, HIGHSCORE_SIZE );
- needs_damage = true;
- }
-
- if (needs_damage && !done_damage)
- {
- if (se.damage > 0)
- {
- snprintf( scratch, INFO_SIZE, " (%d damage)", se.damage );
- strncat( buf, scratch, HIGHSCORE_SIZE );
- hiscore_newline( buf, line_count );
- }
- }
- }
- }
-
- if (se.death_type == KILLED_BY_LEAVING
- || se.death_type == KILLED_BY_WINNING)
- {
- // TODO: strcat "after reaching level %d"; for LEAVING
- if (!verbose)
- {
- if (se.num_runes > 0)
- strcat( buf, "!" );
-
- hiscore_newline( buf, line_count );
- }
- }
- else
- {
- if (verbose && se.death_type != KILLED_BY_QUITTING)
- strcat( buf, "..." );
-
- // where did we die?
- std::string placename =
- place_name(get_packed_place(se.branch, se.dlvl, se.level_type),
- true, true);
-
- // add appropriate prefix
- if (placename.find("Level") == 0)
- strcat( buf, " on ");
- else
- strcat( buf, " in ");
-
- strcat(buf, placename.c_str() );
-
- if (verbose && se.death_time
- && !hiscore_same_day( se.birth_time, se.death_time ))
- {
- strcat( buf, " on " );
- hiscore_date_string( se.death_time, scratch );
- strcat( buf, scratch );
- }
-
- strcat( buf, "." );
- hiscore_newline( buf, line_count );
- } // endif - killed by winning
-
- if (verbose)
- {
- if (se.real_time > 0)
- {
- char username[80] = "The";
- char tmp[80];
-
-#ifdef MULTIUSER
- if (se.uid > 0)
- {
- struct passwd *pw_entry = getpwuid( se.uid );
- strncpy( username, pw_entry->pw_name, sizeof(username) );
- strncat( username, "'s", sizeof(username) );
- username[0] = toupper( username[0] );
- }
-#endif
-
- make_time_string( se.real_time, tmp, sizeof(tmp) );
-
- snprintf( scratch, INFO_SIZE, "%s game lasted %s (%ld turns).",
- username, tmp, se.num_turns );
-
- strncat( buf, scratch, HIGHSCORE_SIZE );
- hiscore_newline( buf, line_count );
- }
- }
-
- return (line_count);
+ std::string line =
+ se.hiscore_line(
+ verbose?
+ scorefile_entry::DDV_VERBOSE
+ : scorefile_entry::DDV_NORMAL );
+ strncpy(buf, line.c_str(), HIGHSCORE_SIZE);
+ buf[HIGHSCORE_SIZE - 1] = 0;
}
// --------------------------------------------------------------------------
@@ -1736,3 +1055,968 @@ static void hs_search_where(char *inbuf, struct scorefile_entry &se)
else if (strstr(inbuf, "of the Swamp") != NULL)
se.branch = BRANCH_SWAMP;
}
+
+//////////////////////////////////////////////////////////////////////////
+// scorefile_entry
+
+scorefile_entry::scorefile_entry(int dam, int dsource, int dtype,
+ const char *aux, bool death_cause_only)
+{
+ init_death_cause(dam, dsource, dtype, aux);
+ if (!death_cause_only)
+ init();
+}
+
+scorefile_entry::scorefile_entry()
+{
+ // Completely uninitialized, caveat user.
+}
+
+void scorefile_entry::init_death_cause(int dam, int dsrc,
+ int dtype, const char *aux)
+{
+ death_source = dsrc;
+ death_type = dtype;
+ damage = dam;
+
+ // 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;
+ else
+ {
+ strncpy( auxkilldata, aux, ITEMNAME_SIZE );
+ auxkilldata[ ITEMNAME_SIZE - 1 ] = 0;
+ }
+
+ // for death by monster
+ if ((death_type == KILLED_BY_MONSTER || death_type == KILLED_BY_BEAM)
+ && death_source >= 0 && death_source < MAX_MONSTERS)
+ {
+ const monsters *monster = &menv[death_source];
+
+ if (monster->type > 0 || monster->type <= NUM_MONSTERS)
+ {
+ death_source = monster->type;
+ mon_num = monster->number;
+
+ // Previously the weapon was only used for dancing weapons,
+ // but now we pass it in as a string through the scorefile
+ // entry to be appended in hiscores_format_single in long or
+ // medium scorefile formats.
+ // It still isn't used in monam for anything but flying weapons
+ // though
+ if (death_type == KILLED_BY_MONSTER
+ && monster->inv[MSLOT_WEAPON] != NON_ITEM)
+ {
+ // [ds] The highscore entry may be constructed while the player
+ // is alive (for notes), so make sure we don't reveal info we
+ // shouldn't.
+ if (you.hp <= 0)
+ {
+#if HISCORE_WEAPON_DETAIL
+ set_ident_flags( mitm[monster->inv[MSLOT_WEAPON]],
+ ISFLAG_IDENT_MASK );
+#else
+ // changing this to ignore the pluses to keep it short
+ unset_ident_flags( mitm[monster->inv[MSLOT_WEAPON]],
+ ISFLAG_IDENT_MASK );
+
+ set_ident_flags( mitm[monster->inv[MSLOT_WEAPON]],
+ ISFLAG_KNOW_TYPE );
+
+ // clear "runed" description text to make shorter yet
+ set_equip_desc( mitm[monster->inv[MSLOT_WEAPON]], 0 );
+#endif
+ }
+
+ // 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;
+ }
+ }
+
+ 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;
+ }
+ }
+ else
+ {
+ death_source = death_source;
+ mon_num = 0;
+ death_source_name[0] = 0;
+ }
+}
+
+void scorefile_entry::init()
+{
+ // Score file entry version:
+ //
+ // 4.0 - original versioned entry
+ // 4.1 - added real_time and num_turn fields
+ // 4.2 - stats and god info
+
+ version = 4;
+ release = 2;
+ strncpy( name, you.your_name, kNameLen );
+
+ name[ kNameLen - 1 ] = 0;
+#ifdef MULTIUSER
+ uid = (int) getuid();
+#else
+ uid = 0;
+#endif
+
+ // do points first.
+ points = you.gold;
+ points += (you.experience * 7) / 10;
+
+ //if (death_type == KILLED_BY_WINNING) points += points / 2;
+ //if (death_type == KILLED_BY_LEAVING) points += points / 10;
+ // these now handled by giving player the value of their inventory
+ char temp_id[4][50];
+
+ for (int d = 0; d < 4; d++)
+ {
+ for (int e = 0; e < 50; e++)
+ temp_id[d][e] = 1;
+ }
+
+ FixedVector< int, NUM_RUNE_TYPES > rune_array;
+
+ num_runes = 0;
+ num_diff_runes = 0;
+
+ for (int i = 0; i < NUM_RUNE_TYPES; i++)
+ rune_array[i] = 0;
+
+ // Calculate value of pack and runes when character leaves dungeon
+ if (death_type == KILLED_BY_LEAVING || death_type == KILLED_BY_WINNING)
+ {
+ for (int d = 0; d < ENDOFPACK; d++)
+ {
+ if (is_valid_item( you.inv[d] ))
+ {
+ points += item_value( you.inv[d], temp_id, true );
+
+ if (you.inv[d].base_type == OBJ_MISCELLANY
+ && you.inv[d].sub_type == MISC_RUNE_OF_ZOT)
+ {
+ if (rune_array[ you.inv[d].plus ] == 0)
+ num_diff_runes++;
+
+ num_runes += you.inv[d].quantity;
+ rune_array[ you.inv[d].plus ] += you.inv[d].quantity;
+ }
+ }
+ }
+
+ // Bonus for exploring different areas, not for collecting a
+ // huge stack of demonic runes in Pandemonium (gold value
+ // is enough for those). -- bwr
+ if (num_diff_runes >= 3)
+ points += ((num_diff_runes + 2) * (num_diff_runes + 2) * 1000);
+ }
+
+ // Players will have a hard time getting 1/10 of this (see XP cap):
+ if (points > 99999999)
+ points = 99999999;
+
+ race = you.species;
+ cls = you.char_class;
+
+ race_class_name[0] = 0;
+
+ lvl = you.experience_level;
+ best_skill = ::best_skill( SK_FIGHTING, NUM_SKILLS - 1, 99 );
+ best_skill_lvl = you.skills[ best_skill ];
+
+ final_hp = you.hp;
+ final_max_hp = you.hp_max;
+ final_max_max_hp = you.hp_max + player_rotted();
+ str = you.strength;
+ intel = you.intel;
+ dex = you.dex;
+
+ god = you.religion;
+ if (you.religion != GOD_NO_GOD)
+ {
+ piety = you.piety;
+ penance = you.penance[you.religion];
+ }
+
+ // main dungeon: level is simply level
+ dlvl = you.your_level + 1;
+ switch (you.where_are_you)
+ {
+ case BRANCH_ORCISH_MINES:
+ case BRANCH_HIVE:
+ case BRANCH_LAIR:
+ case BRANCH_SLIME_PITS:
+ case BRANCH_VAULTS:
+ case BRANCH_CRYPT:
+ case BRANCH_HALL_OF_BLADES:
+ case BRANCH_HALL_OF_ZOT:
+ case BRANCH_ECUMENICAL_TEMPLE:
+ case BRANCH_SNAKE_PIT:
+ case BRANCH_ELVEN_HALLS:
+ case BRANCH_TOMB:
+ case BRANCH_SWAMP:
+ dlvl = you.your_level - you.branch_stairs[you.where_are_you - 10];
+ break;
+
+ case BRANCH_DIS:
+ case BRANCH_GEHENNA:
+ case BRANCH_VESTIBULE_OF_HELL:
+ case BRANCH_COCYTUS:
+ case BRANCH_TARTARUS:
+ case BRANCH_INFERNO:
+ case BRANCH_THE_PIT:
+ dlvl = you.your_level - 26;
+ break;
+ }
+
+ branch = you.where_are_you; // no adjustments necessary.
+ level_type = you.level_type; // pandemonium, labyrinth, dungeon..
+
+ birth_time = you.birth_time; // start time of game
+ death_time = time( NULL ); // end time of game
+
+ if (you.real_time != -1)
+ real_time = you.real_time + (death_time - you.start_time);
+ else
+ real_time = -1;
+
+ num_turns = you.num_turns;
+
+#ifdef WIZARD
+ wiz_mode = (you.wizard ? 1 : 0);
+#else
+ wiz_mode = 0;
+#endif
+}
+
+std::string scorefile_entry::hiscore_line(death_desc_verbosity verbosity) const
+{
+ std::string line = character_description(verbosity);
+ line += death_description(verbosity);
+ line += death_place(verbosity);
+ line += game_time(verbosity);
+
+ return (line);
+}
+
+std::string scorefile_entry::game_time(death_desc_verbosity verbosity) const
+{
+ std::string line;
+
+ if (verbosity == DDV_VERBOSE)
+ {
+ if (real_time > 0)
+ {
+ char username[80] = "The";
+ char scratch[INFO_SIZE];
+ char tmp[80];
+
+#ifdef MULTIUSER
+ if (uid > 0)
+ {
+ struct passwd *pw_entry = getpwuid( uid );
+ strncpy( username, pw_entry->pw_name, sizeof(username) );
+ strncat( username, "'s", sizeof(username) );
+ username[0] = toupper( username[0] );
+ }
+#endif
+
+ make_time_string( real_time, tmp, sizeof(tmp) );
+
+ snprintf( scratch, INFO_SIZE, "%s game lasted %s (%ld turns).",
+ username, tmp, num_turns );
+
+ line += scratch;
+ line += hiscore_newline_string();
+ }
+ }
+
+ return (line);
+}
+
+const char *scorefile_entry::damage_verb() const
+{
+ // GDL: here's an example of using final_hp. Verbiage could be better.
+ // bwr: changed "blasted" since this is for melee
+ return (final_hp > -6) ? "Slain" :
+ (final_hp > -14) ? "Mangled" :
+ (final_hp > -22) ? "Demolished" :
+ "Annihilated";
+}
+
+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 ) );
+}
+
+std::string scorefile_entry::damage_string(bool terse) const
+{
+ char scratch[50];
+ snprintf( scratch, sizeof scratch, "(%d%s)", damage,
+ terse? "" : " damage" );
+ return (scratch);
+}
+
+std::string scorefile_entry::strip_article_a(const std::string &s) const
+{
+ if (s.find("a ") == 0)
+ return (s.substr(2));
+ else if (s.find("an ") == 0)
+ return (s.substr(3));
+ return (s);
+}
+
+std::string scorefile_entry::terse_missile_cause() 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 " }
+ };
+
+ for (unsigned i = 0; i < sizeof(pre_post) / sizeof(*pre_post); ++i)
+ {
+ if (aux.find(pre_post[i][0]) != 0)
+ continue;
+
+ std::string::size_type end = aux.rfind(pre_post[i][1]);
+ if (end == std::string::npos)
+ continue;
+
+ int istart = pre_post[i][0].length();
+ int nchars = end - istart;
+ missile = aux.substr(istart, nchars);
+
+ // Was this prefixed by "an"?
+ if (missile.find("n ") == 0)
+ missile = missile.substr(2);
+ }
+
+ if (missile.length())
+ mcause += "/" + missile;
+
+ return (mcause);
+}
+
+std::string scorefile_entry::terse_beam_cause() const
+{
+ std::string cause = auxkilldata;
+ if (cause.find("by ") == 0 || cause.find("By ") == 0)
+ cause = cause.substr(3);
+ return (cause);
+}
+
+std::string scorefile_entry::terse_wild_magic() const
+{
+ return terse_beam_cause();
+}
+
+std::string scorefile_entry::terse_trap() const
+{
+ std::string trap = *auxkilldata? std::string(auxkilldata) + " trap"
+ : "trap";
+ if (trap.find("n ") == 0)
+ trap = trap.substr(2);
+ trim_string(trap);
+
+ return (trap);
+}
+
+std::string scorefile_entry::single_cdesc() const
+{
+ char scratch[INFO_SIZE];
+ char buf[INFO_SIZE];
+
+ if (!*race_class_name)
+ {
+ snprintf( scratch, sizeof(scratch), "%s%s",
+ get_species_abbrev( race ), get_class_abbrev( cls ) );
+ }
+ else
+ {
+ strncpy( scratch, race_class_name, sizeof scratch );
+ scratch[ sizeof(scratch) - 1 ] = 0;
+ }
+
+
+ std::string scname = name;
+ if (scname.length() > 10)
+ scname = scname.substr(0, 10);
+
+ snprintf( buf, sizeof buf,
+ "%8ld %-10s %s-%02d%s", points, scname.c_str(),
+ scratch, lvl, (wiz_mode == 1) ? "W" : "" );
+
+ return (buf);
+}
+
+std::string
+scorefile_entry::character_description(death_desc_verbosity verbosity) const
+{
+ bool single = verbosity == DDV_TERSE || verbosity == DDV_ONELINE;
+
+ if (single)
+ return single_cdesc();
+
+ bool verbose = verbosity == DDV_VERBOSE;
+ char scratch[INFO_SIZE];
+ char buf[INFO_SIZE];
+
+ std::string desc;
+ // Please excuse the following bit of mess in the name of flavour ;)
+ if (verbose)
+ {
+ strncpy( scratch, skill_title( best_skill, best_skill_lvl,
+ race, str, dex, god ),
+ INFO_SIZE );
+
+ snprintf( buf, HIGHSCORE_SIZE, "%8ld %s the %s (level %d",
+ points, name, scratch, lvl );
+
+ desc = buf;
+ }
+ else
+ {
+ snprintf( buf, HIGHSCORE_SIZE, "%8ld %s the %s %s (level %d",
+ points, name, species_name(race, lvl),
+ get_class_name(cls), lvl );
+ desc = buf;
+ }
+
+ if (final_max_max_hp > 0) // as the other two may be negative
+ {
+ snprintf( scratch, INFO_SIZE, ", %d/%d", final_hp, final_max_hp );
+ desc += scratch;
+
+ if (final_max_hp < final_max_max_hp)
+ {
+ snprintf( scratch, INFO_SIZE, " (%d)", final_max_max_hp );
+ desc += scratch;
+ }
+
+ desc += " HPs";
+ }
+
+ desc += wiz_mode? ") *WIZ*" : ")";
+ desc += hiscore_newline_string();
+
+ if (verbose)
+ {
+ const char *const srace = species_name( race, lvl );
+
+ snprintf( scratch, INFO_SIZE, "Began as a%s %s %s",
+ is_vowel(*srace) ? "n" : "", srace, get_class_name(cls) );
+ desc += scratch;
+
+ if (birth_time > 0)
+ {
+ desc += " on ";
+ hiscore_date_string( birth_time, scratch );
+ desc += scratch;
+ }
+
+ desc += ".";
+ desc += hiscore_newline_string();
+
+ if (race != SP_DEMIGOD && god != -1)
+ {
+ if (god == GOD_XOM)
+ {
+ snprintf( scratch, INFO_SIZE, "Was a %sPlaything of Xom.",
+ (lvl >= 20) ? "Favourite " : "" );
+
+ desc += scratch;
+ desc += hiscore_newline_string();
+ }
+ else if (god != GOD_NO_GOD)
+ {
+ // Not exactly the same as the religon screen, but
+ // good enough to fill this slot for now.
+ snprintf( scratch, INFO_SIZE, "Was %s of %s%s",
+ (piety > 160) ? "the Champion" :
+ (piety >= 120) ? "a High Priest" :
+ (piety >= 100) ? "an Elder" :
+ (piety >= 75) ? "a Priest" :
+ (piety >= 50) ? "a Believer" :
+ (piety >= 30) ? "a Follower"
+ : "an Initiate",
+ god_name( god ),
+ (penance > 0) ? " (penitent)." : "." );
+
+ desc += scratch;
+ desc += hiscore_newline_string();
+ }
+ }
+ }
+
+ return (desc);
+}
+
+std::string scorefile_entry::death_place(death_desc_verbosity verbosity) const
+{
+ bool verbose = verbosity == DDV_VERBOSE;
+ std::string place;
+
+ if (death_type == KILLED_BY_LEAVING || death_type == KILLED_BY_WINNING)
+ return ("");
+
+ char scratch[ INFO_SIZE ];
+
+ if (verbosity == DDV_ONELINE || verbosity == DDV_TERSE)
+ {
+ snprintf( scratch, sizeof scratch, " (%s)",
+ place_name(get_packed_place(branch, dlvl, level_type),
+ false, true).c_str());
+ return (scratch);
+ }
+
+ if (verbose && death_type != KILLED_BY_QUITTING)
+ place += "...";
+
+ // where did we die?
+ std::string placename =
+ place_name(get_packed_place(branch, dlvl, level_type),
+ true, true);
+
+ // add appropriate prefix
+ if (placename.find("Level") == 0)
+ place += " on ";
+ else
+ place += " in ";
+
+ place += placename;
+
+ if (verbose && death_time
+ && !hiscore_same_day( birth_time, death_time ))
+ {
+ place += " on ";
+ hiscore_date_string( death_time, scratch );
+ place += scratch;
+ }
+
+ place += ".";
+ place += hiscore_newline_string();
+
+ return (place);
+}
+
+std::string
+scorefile_entry::death_description(death_desc_verbosity verbosity) const
+{
+ bool needs_beam_cause_line = false;
+ 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;
+
+ char scratch[INFO_SIZE];
+
+ std::string desc;
+
+ if (oneline)
+ desc = " ";
+
+ switch (death_type)
+ {
+ case KILLED_BY_MONSTER:
+ if (terse)
+ desc += death_source_desc();
+ else if (oneline)
+ desc += std::string("slain by ") + death_source_desc();
+ else
+ {
+ snprintf( scratch, sizeof scratch, "%s by %s",
+ damage_verb(),
+ death_source_desc() );
+
+ desc += scratch;
+ }
+
+ // put the damage on the weapon line if there is one
+ if (auxkilldata[0] == 0)
+ needs_damage = true;
+ break;
+
+ case KILLED_BY_POISON:
+ desc += terse? "poison" : "Succumbed to poison";
+ break;
+
+ case KILLED_BY_CLOUD:
+ if (auxkilldata[0] == 0)
+ desc += terse? "cloud" : "Engulfed by a cloud";
+ else
+ {
+ snprintf( scratch, sizeof(scratch), "%scloud of %s",
+ terse? "" : "Engulfed by a ",
+ auxkilldata );
+ desc += scratch;
+ }
+ needs_damage = true;
+ break;
+
+ case KILLED_BY_BEAM:
+ if (oneline)
+ {
+ // keeping this short to leave room for the deep elf spellcasters:
+ snprintf( scratch, sizeof(scratch), "%s by ",
+ range_type_verb( auxkilldata ) );
+ desc += scratch;
+ desc += death_source_desc();
+ }
+ else if (isupper( auxkilldata[0] )) // already made (ie shot arrows)
+ {
+ // If terse we have to parse the information from the string.
+ // Darn it to heck.
+ desc += terse? terse_missile_cause() : auxkilldata;
+ needs_damage = true;
+ }
+ else if (verbose && strncmp( auxkilldata, "by ", 3 ) == 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 );
+ 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)
+ desc += "Killed from afar by ";
+
+ desc += death_source_desc();
+
+ if (*auxkilldata)
+ needs_beam_cause_line = true;
+
+ needs_damage = true;
+ }
+ break;
+
+ case KILLED_BY_CURARE:
+ desc += terse? "asphyx" : "Asphyxiated";
+ break;
+
+ case KILLED_BY_LAVA:
+ if (terse)
+ desc += "lava";
+ else
+ {
+ if (race == SP_MUMMY)
+ desc += "Turned to ash by lava";
+ else
+ desc += "Took a swim in molten lava";
+ }
+ break;
+
+ case KILLED_BY_WATER:
+ if (race == SP_MUMMY)
+ desc += terse? "fell apart" : "Soaked and fell apart";
+ else
+ desc += terse? "drowned" : "Drowned";
+ break;
+
+ case KILLED_BY_STUPIDITY:
+ desc += terse? "stupidity" : "Forgot to breathe";
+ break;
+
+ case KILLED_BY_WEAKNESS:
+ desc += terse? "collapsed " : "Collapsed under their own weight";
+ break;
+
+ case KILLED_BY_CLUMSINESS:
+ desc += terse? "clumsiness" : "Slipped on a banana peel";
+ break;
+
+ case KILLED_BY_TRAP:
+ if (terse)
+ desc += terse_trap();
+ else
+ {
+ snprintf( scratch, sizeof(scratch), "Killed by triggering a%s trap",
+ (*auxkilldata) ? auxkilldata : "" );
+ desc += scratch;
+ }
+ needs_damage = true;
+ break;
+
+ case KILLED_BY_LEAVING:
+ if (terse)
+ desc += "left";
+ else
+ {
+ if (num_runes > 0)
+ desc += "Got out of the dungeon";
+ else
+ desc += "Got out of the dungeon alive";
+ }
+ break;
+
+ case KILLED_BY_WINNING:
+ desc += terse? "escaped" : "Escaped with the Orb";
+ if (num_runes < 1)
+ desc += "!";
+ break;
+
+ case KILLED_BY_QUITTING:
+ desc += terse? "quit" : "Quit the game";
+ break;
+
+ case KILLED_BY_DRAINING:
+ desc += terse? "drained" : "Was drained of all life";
+ break;
+
+ case KILLED_BY_STARVATION:
+ desc += terse? "starvation" : "Starved to death";
+ break;
+
+ case KILLED_BY_FREEZING: // refrigeration spell
+ desc += terse? "frozen" : "Froze to death";
+ needs_damage = true;
+ break;
+
+ case KILLED_BY_BURNING: // sticky flame
+ desc += terse? "burnt" : "Burnt to a crisp";
+ needs_damage = true;
+ break;
+
+ case KILLED_BY_WILD_MAGIC:
+ if (!*auxkilldata)
+ desc += terse? "wild magic" : "Killed by wild magic";
+ else
+ {
+ if (terse)
+ desc += terse_wild_magic();
+ else
+ {
+ // 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 );
+ desc += scratch;
+ }
+ }
+
+ needs_damage = true;
+ break;
+
+ case KILLED_BY_XOM: // only used for old Xom kills
+ desc += terse? "xom" : "Killed for Xom's enjoyment";
+ needs_damage = true;
+ break;
+
+ case KILLED_BY_STATUE:
+ desc += terse? "statue" : "Killed by a statue";
+ needs_damage = true;
+ break;
+
+ case KILLED_BY_ROTTING:
+ desc += terse? "rotting" : "Rotted away";
+ break;
+
+ case KILLED_BY_TARGETTING:
+ desc += terse? "shot self" : "Killed themselves with bad targeting";
+ needs_damage = true;
+ break;
+
+ case KILLED_BY_SPORE:
+ desc += terse? "spore" : "Killed by an exploding spore";
+ needs_damage = true;
+ break;
+
+ case KILLED_BY_TSO_SMITING:
+ desc += terse? "smote by Shining One" : "Smote by The Shining One";
+ needs_damage = true;
+ break;
+
+ case KILLED_BY_PETRIFICATION:
+ desc += terse? "petrified" : "Turned to stone";
+ break;
+
+ case KILLED_BY_MELTING:
+ desc += terse? "melted" : " melted into a puddle";
+ break;
+
+ case KILLED_BY_BLEEDING:
+ desc += terse? "bleeding" : " bled to death";
+ break;
+
+ case KILLED_BY_SOMETHING:
+ if (auxkilldata)
+ desc += auxkilldata;
+ else
+ desc += terse? "died" : "Died";
+ needs_damage = true;
+ break;
+
+ case KILLED_BY_FALLING_DOWN_STAIRS:
+ desc += terse? "fell downstairs" : "Fell down a flight of stairs";
+ needs_damage = true;
+ break;
+
+ case KILLED_BY_ACID:
+ desc += terse? "acid" : "Splashed by acid";
+ needs_damage = true;
+ break;
+
+ default:
+ desc += terse? "program bug" : "Nibbled to death by software bugs";
+ break;
+ } // end switch
+
+ if (oneline && desc.length() > 2)
+ desc[1] = tolower(desc[1]);
+
+ // TODO: Eventually, get rid of "..." for cases where the text fits.
+ if (terse)
+ {
+ if (death_type == KILLED_BY_MONSTER && *auxkilldata)
+ {
+ desc += "/";
+ desc += strip_article_a(auxkilldata);
+ needs_damage = true;
+ }
+ else if (needs_beam_cause_line)
+ desc += "/" + terse_beam_cause();
+ else if (needs_called_by_monster_line)
+ desc += death_source_name;
+
+ if (needs_damage && damage > 0)
+ desc += " " + damage_string(true);
+ }
+ else if (verbose)
+ {
+ bool done_damage = false; // paranoia
+
+ if (needs_damage && damage > 0)
+ {
+ desc += " " + damage_string();
+ needs_damage = false;
+ done_damage = true;
+ }
+
+ if ((death_type == KILLED_BY_LEAVING
+ || death_type == KILLED_BY_WINNING)
+ && num_runes > 0)
+ {
+ desc += hiscore_newline_string();
+
+ snprintf( scratch, INFO_SIZE, "... %s %d rune%s",
+ (death_type == KILLED_BY_WINNING) ? "and" : "with",
+ num_runes, (num_runes > 1) ? "s" : "" );
+ desc += scratch;
+
+ if (num_diff_runes > 1)
+ {
+ snprintf( scratch, INFO_SIZE, " (of %d types)",
+ num_diff_runes );
+ desc += scratch;
+ }
+
+ if (death_time > 0
+ && !hiscore_same_day( birth_time, death_time ))
+ {
+ desc += " on ";
+ hiscore_date_string( death_time, scratch );
+ desc += scratch;
+ }
+
+ desc += "!";
+ desc += hiscore_newline_string();
+ }
+ else if (death_type != KILLED_BY_QUITTING)
+ {
+ desc += hiscore_newline_string();
+
+ if (death_type == KILLED_BY_MONSTER && auxkilldata[0])
+ {
+ snprintf(scratch, INFO_SIZE, "... wielding %s", auxkilldata);
+ 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;
+ }
+ else if (needs_called_by_monster_line)
+ {
+ snprintf( scratch, sizeof(scratch), "... called down by %s",
+ death_source_name );
+ desc += scratch;
+ needs_damage = true;
+ }
+
+ if (needs_damage && !done_damage)
+ {
+ if (damage > 0)
+ {
+ desc += " " + damage_string();
+ desc += hiscore_newline_string();
+ }
+ }
+ }
+ }
+
+ if (!oneline)
+ {
+ if (death_type == KILLED_BY_LEAVING
+ || death_type == KILLED_BY_WINNING)
+ {
+ // TODO: strcat "after reaching level %d"; for LEAVING
+ if (verbosity == DDV_NORMAL)
+ {
+ if (num_runes > 0)
+ desc += "!";
+ }
+ desc += hiscore_newline_string();
+ }
+ }
+
+ if (terse)
+ {
+ trim_string(desc);
+ desc = strip_article_a(desc);
+ }
+
+ return (desc);
+}
diff --git a/crawl-ref/source/hiscores.h b/crawl-ref/source/hiscores.h
index 2cb5f4786f..0d71a8c7c5 100644
--- a/crawl-ref/source/hiscores.h
+++ b/crawl-ref/source/hiscores.h
@@ -28,8 +28,8 @@ void hiscores_print_list( int display_count = -1, int format = SCORE_TERSE );
/* ***********************************************************************
* called from: ouch hiscores
* *********************************************************************** */
-void hiscores_format_single( char *buffer, struct scorefile_entry &se );
-int hiscores_format_single_long( char *buffer, struct scorefile_entry &se,
+void hiscores_format_single( char *buffer, const scorefile_entry &se );
+void hiscores_format_single_long( char *buffer, const scorefile_entry &se,
bool verbose = false );
#endif // HISCORES_H
diff --git a/crawl-ref/source/initfile.cc b/crawl-ref/source/initfile.cc
index 3bcd6a34a8..65597332f6 100644
--- a/crawl-ref/source/initfile.cc
+++ b/crawl-ref/source/initfile.cc
@@ -430,7 +430,7 @@ void reset_options(bool clear_name)
Options.delay_message_clear = false;
Options.pickup_dropped = true;
Options.travel_colour = true;
- Options.travel_delay = -1;
+ Options.travel_delay = 10;
Options.travel_stair_cost = 500;
Options.travel_exclude_radius2 = 68;
diff --git a/crawl-ref/source/it_use3.cc b/crawl-ref/source/it_use3.cc
index 8671b52614..f1288a3974 100644
--- a/crawl-ref/source/it_use3.cc
+++ b/crawl-ref/source/it_use3.cc
@@ -144,7 +144,7 @@ void special_wielded(void)
if (one_chance_in(200))
{
- torment( you.x_pos, you.y_pos );
+ torment( TORMENT_SPWLD, you.x_pos, you.y_pos );
did_god_conduct( DID_UNHOLY, 1 );
}
break;
@@ -374,7 +374,7 @@ bool evoke_wielded( void )
mpr("You feel the staff feeding on your energy!");
- dec_hp( 5 + random2avg(19, 2), false );
+ dec_hp( 5 + random2avg(19, 2), false, "Staff of Dispater" );
dec_mp( 2 + random2avg(5, 2) );
make_hungry( 100, false );
diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc
index 1bc6f2d003..affb4633f4 100644
--- a/crawl-ref/source/item_use.cc
+++ b/crawl-ref/source/item_use.cc
@@ -3216,7 +3216,7 @@ void read_scroll(void)
break;
case SCR_TORMENT:
- torment( you.x_pos, you.y_pos );
+ torment( TORMENT_SCROLL, you.x_pos, you.y_pos );
// is only naughty if you know you're doing it
if (get_ident_type( OBJ_SCROLLS, SCR_TORMENT ) == ID_KNOWN_TYPE)
diff --git a/crawl-ref/source/libutil.cc b/crawl-ref/source/libutil.cc
index 8627f403e7..d4bacf44c2 100644
--- a/crawl-ref/source/libutil.cc
+++ b/crawl-ref/source/libutil.cc
@@ -48,7 +48,7 @@
// the shell can do damage with.
bool shell_safe(const char *file)
{
- int match = strcspn(file, "`$*?|><");
+ int match = strcspn(file, "`$*?|><&\n");
return !(match >= 0 && file[match]);
}
@@ -70,6 +70,20 @@ void play_sound( const char *file )
#endif
}
+int count_occurrences(const std::string &text, const std::string &s)
+{
+ int nfound = 0;
+ std::string::size_type pos = 0;
+
+ while (text.find(s, pos) != std::string::npos)
+ {
+ ++nfound;
+ pos += s.length();
+ }
+
+ return (nfound);
+}
+
void get_input_line( char *const buff, int len )
{
buff[0] = 0; // just in case
diff --git a/crawl-ref/source/libutil.h b/crawl-ref/source/libutil.h
index e7deb46dc1..da036f01e1 100644
--- a/crawl-ref/source/libutil.h
+++ b/crawl-ref/source/libutil.h
@@ -17,6 +17,8 @@
#include <string>
#include <vector>
+int count_occurrences(const std::string &text, const std::string &searchfor);
+
// getch() that returns a consistent set of values for all platforms.
int c_getch();
diff --git a/crawl-ref/source/mstuff2.cc b/crawl-ref/source/mstuff2.cc
index d6f041257e..c72349c066 100644
--- a/crawl-ref/source/mstuff2.cc
+++ b/crawl-ref/source/mstuff2.cc
@@ -548,7 +548,7 @@ void mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cast)
simple_monster_message(monster, " calls on the powers of Hell!");
- torment(monster->x, monster->y);
+ torment(monster_index(monster), monster->x, monster->y);
return;
case MS_SUMMON_DEMON_GREATER:
@@ -1165,7 +1165,8 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
strcat(info, ".");
mpr(info);
-
+ // [dshaligram] When changing bolt names here, you must edit
+ // hiscores.cc (scorefile_entry::terse_missile_cause()) to match.
if (launched)
{
snprintf( throw_buff, sizeof(throw_buff), "Shot with a%s %s by %s",
diff --git a/crawl-ref/source/notes.cc b/crawl-ref/source/notes.cc
index 3be11388f2..0efe9686bc 100644
--- a/crawl-ref/source/notes.cc
+++ b/crawl-ref/source/notes.cc
@@ -203,8 +203,8 @@ static bool is_noteworthy( const Note& note ) {
/* not if we have a recent warning */
if ( (note.turn - rnote.turn < 5) &&
/* unless we've lost half our HP since then */
- (note.first * 2 >= rnote.first) )
- return false;
+ (note.first * 2 >= rnote.first) )
+ return false;
break;
default:
mpr("Buggy note passed: unknown note type");
@@ -231,87 +231,92 @@ std::string describe_note( const Note& note ) {
switch ( note.type ) {
case NOTE_HP_CHANGE:
- sprintf(buf, "Had %d/%d hit points", note.first, note.second);
+ // [ds] Shortened HP change note from "Had X hitpoints" to accommodate
+ // the cause for the loss of hitpoints.
+ snprintf(buf, sizeof buf, "HP: %d/%d [%s]",
+ note.first, note.second, note.name.c_str());
break;
case NOTE_MP_CHANGE:
- sprintf(buf, "Had %d/%d mana", note.first, note.second);
+ snprintf(buf, sizeof buf, "Mana: %d/%d", note.first, note.second);
break;
case NOTE_MAXHP_CHANGE:
- sprintf(buf, "Reached %d max hit points", note.first);
+ snprintf(buf, sizeof buf, "Reached %d max hit points", note.first);
break;
case NOTE_MAXMP_CHANGE:
- sprintf(buf, "Reached %d max mana", note.first);
+ snprintf(buf, sizeof buf, "Reached %d max mana", note.first);
break;
case NOTE_XP_LEVEL_CHANGE:
- sprintf(buf, "Reached XP level %d. %s", note.first, note.name.c_str());
+ snprintf(buf, sizeof buf, "Reached XP level %d. %s", note.first,
+ note.name.c_str());
break;
case NOTE_DUNGEON_LEVEL_CHANGE:
- sprintf(buf, "Entered %s",
+ snprintf(buf, sizeof buf, "Entered %s",
branch_level_name(note.packed_place).c_str());
break;
case NOTE_LEARN_SPELL:
- sprintf(buf, "Learned a level %d spell: %s",
+ snprintf(buf, sizeof buf, "Learned a level %d spell: %s",
spell_difficulty(note.first), spell_title(note.first));
break;
case NOTE_GET_GOD:
- sprintf(buf, "Became a worshipper of %s",
+ snprintf(buf, sizeof buf, "Became a worshipper of %s",
god_name(note.first, true));
break;
case NOTE_LOSE_GOD:
- sprintf(buf, "Fell from the grace of %s",
+ snprintf(buf, sizeof buf, "Fell from the grace of %s",
god_name(note.first));
break;
case NOTE_GOD_GIFT:
- sprintf(buf, "Received a gift from %s",
+ snprintf(buf, sizeof buf, "Received a gift from %s",
god_name(note.first));
break;
case NOTE_ID_ITEM:
if (note.desc.length() > 0)
- sprintf(buf, "Identified %s (%s)", note.name.c_str(),
+ snprintf(buf, sizeof buf, "Identified %s (%s)", note.name.c_str(),
note.desc.c_str());
else
- sprintf(buf, "Identified %s", note.name.c_str());
+ snprintf(buf, sizeof buf, "Identified %s", note.name.c_str());
break;
case NOTE_GET_ITEM:
- sprintf(buf, "Got %s", note.name.c_str());
+ snprintf(buf, sizeof buf, "Got %s", note.name.c_str());
break;
case NOTE_GAIN_SKILL:
- sprintf(buf, "Reached skill %d in %s",
+ snprintf(buf, sizeof buf, "Reached skill %d in %s",
note.second, skill_name(note.first));
break;
case NOTE_SEEN_MONSTER:
- sprintf(buf, "Noticed %s", note.name.c_str() );
+ snprintf(buf, sizeof buf, "Noticed %s", note.name.c_str() );
break;
case NOTE_KILL_MONSTER:
- sprintf(buf, "Defeated %s", note.name.c_str());
+ snprintf(buf, sizeof buf, "Defeated %s", note.name.c_str());
break;
case NOTE_POLY_MONSTER:
- sprintf(buf, "%s changed form", note.name.c_str() );
+ snprintf(buf, sizeof buf, "%s changed form", note.name.c_str() );
break;
case NOTE_GOD_POWER:
- sprintf(buf, "Acquired %s's %s power", god_name(note.first),
+ snprintf(buf, sizeof buf, "Acquired %s's %s power",
+ god_name(note.first),
number_to_ordinal(real_god_power(note.first, note.second)+1));
break;
case NOTE_GET_MUTATION:
- sprintf(buf, "Gained mutation: %s",
+ snprintf(buf, sizeof buf, "Gained mutation: %s",
mutation_name(note.first, note.second == 0 ? 1 : note.second));
break;
case NOTE_LOSE_MUTATION:
- sprintf(buf, "Lost mutation: %s",
+ snprintf(buf, sizeof buf, "Lost mutation: %s",
mutation_name(note.first,
note.second == 3 ? 3 : note.second+1));
break;
case NOTE_USER_NOTE:
- sprintf(buf, "%s", note.name.c_str());
+ snprintf(buf, sizeof buf, "%s", note.name.c_str());
break;
case NOTE_MESSAGE:
- sprintf(buf, "%s", note.name.c_str());
+ snprintf(buf, sizeof buf, "%s", note.name.c_str());
break;
default:
- sprintf(buf, "Buggy note description: unknown note type");
+ snprintf(buf, sizeof buf, "Buggy note description: unknown note type");
break;
}
- sprintf(buf2, "| %5ld | ", note.turn );
+ snprintf(buf2, sizeof buf2, "| %5ld | ", note.turn );
std::string placename = short_place_name(note.packed_place);
while ( placename.length() < 7 )
placename += ' ';
diff --git a/crawl-ref/source/ouch.cc b/crawl-ref/source/ouch.cc
index ea074b9043..7f318959e2 100644
--- a/crawl-ref/source/ouch.cc
+++ b/crawl-ref/source/ouch.cc
@@ -681,9 +681,6 @@ void drain_exp(void)
// death_source should be set to zero for non-monsters {dlb}
void ouch( int dam, int death_source, char death_type, const char *aux )
{
- int d = 0;
- int e = 0;
-
ait_hp_loss hpl(dam, death_type);
interrupt_activity( AI_HP_LOSS, &hpl );
@@ -746,7 +743,18 @@ void ouch( int dam, int death_source, char death_type, const char *aux )
{
mpr( "* * * LOW HITPOINT WARNING * * *", MSGCH_DANGER );
}
- take_note(Note(NOTE_HP_CHANGE, you.hp, you.hp_max));
+ take_note(
+ Note(
+ NOTE_HP_CHANGE,
+ you.hp,
+ you.hp_max,
+ scorefile_entry(
+ dam,
+ death_source,
+ death_type,
+ aux )
+ .death_description(scorefile_entry::DDV_TERSE)
+ .c_str()) );
if (you.hp > 0)
return;
@@ -787,228 +795,8 @@ void ouch( int dam, int death_source, char death_type, const char *aux )
// prevent bogus notes
activate_notes(false);
- // do points first.
- long points = you.gold;
- points += (you.experience * 7) / 10;
-
- //if (death_type == KILLED_BY_WINNING) points += points / 2;
- //if (death_type == KILLED_BY_LEAVING) points += points / 10;
- // these now handled by giving player the value of their inventory
- char temp_id[4][50];
-
- for (d = 0; d < 4; d++)
- {
- for (e = 0; e < 50; e++)
- temp_id[d][e] = 1;
- }
-
// CONSTRUCT SCOREFILE ENTRY
- struct scorefile_entry se;
-
- // Score file entry version:
- //
- // 4.0 - original versioned entry
- // 4.1 - added real_time and num_turn fields
- // 4.2 - stats and god info
-
- se.version = 4;
- se.release = 2;
-
- strncpy( se.name, you.your_name, kNameLen );
- se.name[ kNameLen - 1 ] = 0;
-#ifdef MULTIUSER
- se.uid = (int) getuid();
-#else
- se.uid = 0;
-#endif
-
- FixedVector< int, NUM_RUNE_TYPES > rune_array;
-
- se.num_runes = 0;
- se.num_diff_runes = 0;
-
- for (int i = 0; i < NUM_RUNE_TYPES; i++)
- rune_array[i] = 0;
-
- // Calculate value of pack and runes when character leaves dungeon
- if (death_type == KILLED_BY_LEAVING || death_type == KILLED_BY_WINNING)
- {
- for (d = 0; d < ENDOFPACK; d++)
- {
- if (is_valid_item( you.inv[d] ))
- {
- points += item_value( you.inv[d], temp_id, true );
-
- if (you.inv[d].base_type == OBJ_MISCELLANY
- && you.inv[d].sub_type == MISC_RUNE_OF_ZOT)
- {
- if (rune_array[ you.inv[d].plus ] == 0)
- se.num_diff_runes++;
-
- se.num_runes += you.inv[d].quantity;
- rune_array[ you.inv[d].plus ] += you.inv[d].quantity;
- }
- }
- }
-
- // Bonus for exploring different areas, not for collecting a
- // huge stack of demonic runes in Pandemonium (gold value
- // is enough for those). -- bwr
- if (se.num_diff_runes >= 3)
- points += ((se.num_diff_runes + 2) * (se.num_diff_runes + 2) * 1000);
- }
-
- // Players will have a hard time getting 1/10 of this (see XP cap):
- if (points > 99999999)
- points = 99999999;
-
- se.points = points;
- se.race = you.species;
- se.cls = you.char_class;
-
- // strcpy(se.race_class_name, "");
- se.race_class_name[0] = 0;
-
- se.lvl = you.experience_level;
- se.best_skill = best_skill( SK_FIGHTING, NUM_SKILLS - 1, 99 );
- se.best_skill_lvl = you.skills[ se.best_skill ];
- se.death_type = death_type;
-
- // for death by monster
-
- // Set the default aux data value...
- // If aux is passed in (ie for a trap), we'll default to that.
- if (aux == NULL)
- se.auxkilldata[0] = 0;
- else
- {
- strncpy( se.auxkilldata, aux, ITEMNAME_SIZE );
- se.auxkilldata[ ITEMNAME_SIZE - 1 ] = 0;
- }
-
- if ((death_type == KILLED_BY_MONSTER || death_type == KILLED_BY_BEAM)
- && death_source >= 0 && death_source < MAX_MONSTERS)
- {
- struct monsters *monster = &menv[death_source];
-
- if (monster->type > 0 || monster->type <= NUM_MONSTERS)
- {
- se.death_source = monster->type;
- se.mon_num = monster->number;
-
- // Previously the weapon was only used for dancing weapons,
- // but now we pass it in as a string through the scorefile
- // entry to be appended in hiscores_format_single in long or
- // medium scorefile formats.
- // It still isn't used in monam for anything but flying weapons
- // though
- if (death_type == KILLED_BY_MONSTER
- && monster->inv[MSLOT_WEAPON] != NON_ITEM)
- {
-#if HISCORE_WEAPON_DETAIL
- set_ident_flags( mitm[monster->inv[MSLOT_WEAPON]],
- ISFLAG_IDENT_MASK );
-#else
- // changing this to ignore the pluses to keep it short
- unset_ident_flags( mitm[monster->inv[MSLOT_WEAPON]],
- ISFLAG_IDENT_MASK );
-
- set_ident_flags( mitm[monster->inv[MSLOT_WEAPON]],
- ISFLAG_KNOW_TYPE );
-
- // clear "runed" description text to make shorter yet
- set_equip_desc( mitm[monster->inv[MSLOT_WEAPON]], 0 );
-#endif
-
- // 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( se.auxkilldata, info, ITEMNAME_SIZE );
- se.auxkilldata[ ITEMNAME_SIZE - 1 ] = 0;
- }
- }
-
- strcpy( info,
- monam( monster->number, monster->type, true, DESC_NOCAP_A,
- monster->inv[MSLOT_WEAPON] ) );
-
- strncpy( se.death_source_name, info, 40 );
- se.death_source_name[39] = 0;
- }
- }
- else
- {
- se.death_source = death_source;
- se.mon_num = 0;
- se.death_source_name[0] = 0;
- }
-
- se.damage = dam;
- se.final_hp = you.hp;
- se.final_max_hp = you.hp_max;
- se.final_max_max_hp = you.hp_max + player_rotted();
- se.str = you.strength;
- se.intel = you.intel;
- se.dex = you.dex;
-
- se.god = you.religion;
- if (you.religion != GOD_NO_GOD)
- {
- se.piety = you.piety;
- se.penance = you.penance[you.religion];
- }
-
- // main dungeon: level is simply level
- se.dlvl = you.your_level + 1;
- switch (you.where_are_you)
- {
- case BRANCH_ORCISH_MINES:
- case BRANCH_HIVE:
- case BRANCH_LAIR:
- case BRANCH_SLIME_PITS:
- case BRANCH_VAULTS:
- case BRANCH_CRYPT:
- case BRANCH_HALL_OF_BLADES:
- case BRANCH_HALL_OF_ZOT:
- case BRANCH_ECUMENICAL_TEMPLE:
- case BRANCH_SNAKE_PIT:
- case BRANCH_ELVEN_HALLS:
- case BRANCH_TOMB:
- case BRANCH_SWAMP:
- se.dlvl = you.your_level - you.branch_stairs[you.where_are_you - 10];
- break;
-
- case BRANCH_DIS:
- case BRANCH_GEHENNA:
- case BRANCH_VESTIBULE_OF_HELL:
- case BRANCH_COCYTUS:
- case BRANCH_TARTARUS:
- case BRANCH_INFERNO:
- case BRANCH_THE_PIT:
- se.dlvl = you.your_level - 26;
- break;
- }
-
- se.branch = you.where_are_you; // no adjustments necessary.
- se.level_type = you.level_type; // pandemonium, labyrinth, dungeon..
-
- se.birth_time = you.birth_time; // start time of game
- se.death_time = time( NULL ); // end time of game
-
- if (you.real_time != -1)
- se.real_time = you.real_time + (se.death_time - you.start_time);
- else
- se.real_time = -1;
-
- se.num_turns = you.num_turns;
-
-#ifdef WIZARD
- se.wiz_mode = (you.wizard ? 1 : 0);
-#else
- se.wiz_mode = 0;
-#endif
+ scorefile_entry se(dam, death_source, death_type, aux);
#ifdef SCORE_WIZARD_CHARACTERS
// add this highscore to the score file.
@@ -1123,10 +911,12 @@ void end_game( struct scorefile_entry &se )
char scorebuff[ HIGHSCORE_SIZE ];
- const int lines = hiscores_format_single_long( scorebuff, se, true );
-
+ hiscores_format_single_long( scorebuff, se, true );
// truncate
scorebuff[ HIGHSCORE_SIZE - 1 ] = 0;
+
+ const int lines = count_occurrences(scorebuff, EOL) + 1;
+
cprintf( scorebuff );
cprintf( EOL "Best Crawlers -" EOL );
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index e02e9acef4..d4928f13f8 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -1069,6 +1069,11 @@ bool player_control_teleport(bool calc_unid) {
you.mutation[MUT_TELEPORT_CONTROL] );
}
+int player_res_torment(bool)
+{
+ return (you.is_undead || you.mutation[MUT_TORMENT_RESISTANCE]);
+}
+
// funny that no races are susceptible to poisons {dlb}
int player_res_poison(bool calc_unid)
{
@@ -3659,17 +3664,25 @@ void modify_stat(unsigned char which_stat, char amount, bool suppress_msg)
return;
} // end modify_stat()
-void dec_hp(int hp_loss, bool fatal)
+void dec_hp(int hp_loss, bool fatal, const char *aux)
{
+ if (!fatal && you.hp < 1)
+ you.hp = 1;
+
+ if (!fatal && hp_loss >= you.hp)
+ hp_loss = you.hp - 1;
+
if (hp_loss < 1)
return;
- you.hp -= hp_loss;
-
- if (!fatal && you.hp < 1)
- you.hp = 1;
+ // If it's not fatal, use ouch() so that notes can be taken. If it IS
+ // fatal, somebody else is doing the bookkeeping, and we don't want to mess
+ // with that.
+ if (!fatal && aux)
+ ouch(hp_loss, -1, KILLED_BY_SOMETHING, aux);
+ else
+ you.hp -= hp_loss;
- // take_note(Note(NOTE_HP_CHANGE, you.hp, you.hp_max));
you.redraw_hit_points = 1;
return;
diff --git a/crawl-ref/source/player.h b/crawl-ref/source/player.h
index 8b322d151d..a685a103e2 100644
--- a/crawl-ref/source/player.h
+++ b/crawl-ref/source/player.h
@@ -150,6 +150,8 @@ int player_regen(void);
int player_res_cold(bool calc_unid = true);
int player_res_acid(void);
+int player_res_torment(bool calc_unid = true);
+
bool player_res_corrosion(bool calc_unid = true);
bool player_item_conserve(bool calc_unid = true);
int player_mental_clarity(bool calc_unid = true);
@@ -350,7 +352,7 @@ bool player_genus( unsigned char which_genus,
* called from: ability - effects - fight - it_use3 - ouch - spell -
* spells - spells2 - spells3 - spells4
* *********************************************************************** */
-void dec_hp(int hp_loss, bool fatal);
+void dec_hp(int hp_loss, bool fatal, const char *aux = NULL);
/* ***********************************************************************
diff --git a/crawl-ref/source/spells2.cc b/crawl-ref/source/spells2.cc
index 319b039991..44fc92536b 100644
--- a/crawl-ref/source/spells2.cc
+++ b/crawl-ref/source/spells2.cc
@@ -1028,7 +1028,7 @@ int vampiric_drain(int pow)
if (holy == MH_UNDEAD || holy == MH_DEMONIC)
{
mpr("Aaaarggghhhhh!");
- dec_hp(random2avg(39, 2) + 10, false);
+ dec_hp(random2avg(39, 2) + 10, false, "vampiric drain backlash");
return -1;
}
diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc
index ad6af55fab..cce05b918e 100644
--- a/crawl-ref/source/spl-cast.cc
+++ b/crawl-ref/source/spl-cast.cc
@@ -1722,7 +1722,7 @@ bool your_spells( int spc2, int powc, bool allow_fail )
mpr("To torment others, one must first know what torment means. ");
return (false);
}
- torment(you.x_pos, you.y_pos);
+ torment(TORMENT_SPELL, you.x_pos, you.y_pos);
break;
case SPELL_DEFLECT_MISSILES:
@@ -2859,9 +2859,8 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail,
mpr("Something just walked over your grave. No, really!");
break;
}
- mpr("Your body is wracked with pain!");
- dec_hp((you.hp / 2) - 1, false);
+ torment_monsters(you.x_pos, you.y_pos, 0, TORMENT_GENERIC);
break;
case 1: