diff options
author | dshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573> | 2006-10-29 11:33:25 +0000 |
---|---|---|
committer | dshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573> | 2006-10-29 11:33:25 +0000 |
commit | 59ea3af23c9fda27662c4ffd8069ce1237bddcba (patch) | |
tree | aaaf9b6bdf7882f544a9371d354f7532816c3df8 | |
parent | 28f97347df22169b839f0aa9a1fcc7c0136a0654 (diff) | |
download | crawl-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.cc | 4 | ||||
-rw-r--r-- | crawl-ref/source/debug.cc | 7 | ||||
-rw-r--r-- | crawl-ref/source/decks.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/effects.cc | 46 | ||||
-rw-r--r-- | crawl-ref/source/effects.h | 3 | ||||
-rw-r--r-- | crawl-ref/source/enum.h | 8 | ||||
-rw-r--r-- | crawl-ref/source/externs.h | 38 | ||||
-rw-r--r-- | crawl-ref/source/fight.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/hiscores.cc | 1682 | ||||
-rw-r--r-- | crawl-ref/source/hiscores.h | 4 | ||||
-rw-r--r-- | crawl-ref/source/initfile.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/it_use3.cc | 4 | ||||
-rw-r--r-- | crawl-ref/source/item_use.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/libutil.cc | 16 | ||||
-rw-r--r-- | crawl-ref/source/libutil.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/mstuff2.cc | 5 | ||||
-rw-r--r-- | crawl-ref/source/notes.cc | 57 | ||||
-rw-r--r-- | crawl-ref/source/ouch.cc | 244 | ||||
-rw-r--r-- | crawl-ref/source/player.cc | 25 | ||||
-rw-r--r-- | crawl-ref/source/player.h | 4 | ||||
-rw-r--r-- | crawl-ref/source/spells2.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/spl-cast.cc | 5 |
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: |