diff options
Diffstat (limited to 'stone_soup/crawl-ref/source/hiscores.cc')
-rw-r--r-- | stone_soup/crawl-ref/source/hiscores.cc | 1846 |
1 files changed, 0 insertions, 1846 deletions
diff --git a/stone_soup/crawl-ref/source/hiscores.cc b/stone_soup/crawl-ref/source/hiscores.cc deleted file mode 100644 index f3aba604d1..0000000000 --- a/stone_soup/crawl-ref/source/hiscores.cc +++ /dev/null @@ -1,1846 +0,0 @@ -/* - * File: highscore.cc - * Summary: deal with reading and writing of highscore file - * Written by: Gordon Lipford - * - * Change History (most recent first): - * - * <1> 16feb2001 gdl Created - */ - -/* - * ----------- MODIFYING THE PRINTED SCORE FORMAT --------------------- - * Do this at your leisure. Change hiscores_format_single() as much - * as you like. - * - * - * ----------- IF YOU MODIFY THE INTERNAL SCOREFILE FORMAT ------------ - * .. as defined by the struct 'scorefile_entry' .. - * You MUST change hs_copy(), hs_parse_numeric(), hs_parse_string(), - * and hs_write(). It's also a really good idea to change the - * version numbers assigned in ouch() so that Crawl can tell the - * difference between your new entry and previous versions. - * - * - * - */ - - -#include <stdlib.h> -#include <stdio.h> -#include <ctype.h> - -#include "AppHdr.h" -#include "externs.h" - -#include "hiscores.h" -#include "itemname.h" -#include "mon-util.h" -#include "player.h" -#include "religion.h" -#include "stuff.h" -#include "tags.h" -#include "view.h" - -#include "skills2.h" - -#ifdef MULTIUSER - // includes to get passwd file access: - #include <pwd.h> - #include <sys/types.h> -#endif - -// enough memory allocated to snarf in the scorefile entries -static struct scorefile_entry hs_list[SCORE_FILE_ENTRIES]; - -// hackish: scorefile position of newest entry. Will be highlit during -// highscore printing (always -1 when run from command line). -static int newest_entry = -1; - -static FILE *hs_open(const char *mode); -static void hs_close(FILE *handle, const char *mode); -static bool hs_read(FILE *scores, struct scorefile_entry &dest); -static void hs_parse_numeric(char *inbuf, struct scorefile_entry &dest); -static void hs_parse_string(char *inbuf, struct scorefile_entry &dest); -static void hs_copy(struct scorefile_entry &dest, struct scorefile_entry &src); -static void hs_write(FILE *scores, struct scorefile_entry &entry); -static void hs_nextstring(char *&inbuf, char *dest); -static int hs_nextint(char *&inbuf); -static long hs_nextlong(char *&inbuf); - -// functions dealing with old scorefile entries -static void hs_parse_generic_1(char *&inbuf, char *outbuf, const char *stopvalues); -static void hs_parse_generic_2(char *&inbuf, char *outbuf, const char *continuevalues); -static void hs_stripblanks(char *buf); -static void hs_search_death(char *inbuf, struct scorefile_entry &se); -static void hs_search_where(char *inbuf, struct scorefile_entry &se); - -// file locking stuff -#ifdef USE_FILE_LOCKING -static bool lock_file_handle( FILE *handle, int type ); -static bool unlock_file_handle( FILE *handle ); -#endif // USE_FILE_LOCKING - -void hiscores_new_entry( struct scorefile_entry &ne ) -{ - FILE *scores; - int i, total_entries; - bool inserted = false; - - // open highscore file (reading) -- note that NULL is not fatal! - scores = hs_open("r"); - - // read highscore file, inserting new entry at appropriate point, - for (i = 0; i < SCORE_FILE_ENTRIES; i++) - { - if (hs_read(scores, hs_list[i]) == false) - break; - - // compare points.. - if (ne.points >= hs_list[i].points && inserted == false) - { - newest_entry = i; // for later printing - inserted = true; - // copy read entry to i+1th position - // Fixed a nasty overflow bug here -- Sharp - if (i+1 < SCORE_FILE_ENTRIES) - { - hs_copy(hs_list[i+1], hs_list[i]); - hs_copy(hs_list[i], ne); - i++; - } else { - // copy new entry to current position - hs_copy(hs_list[i], ne); - } - } - } - - // special case: lowest score, with room - if (!inserted && i < SCORE_FILE_ENTRIES) - { - newest_entry = i; - inserted = true; - // copy new entry - hs_copy(hs_list[i], ne); - i++; - } - - total_entries = i; - - // close so we can re-open for writing - hs_close(scores,"r"); - - // open highscore file (writing) -- NULL *is* fatal here. - scores = hs_open("w"); - if (scores == NULL) - { - perror("Entry not added - failure opening score file for writing."); - return; - } - - // write scorefile entries. - for (i = 0; i < total_entries; i++) - { - hs_write(scores, hs_list[i]); - } - - // close scorefile. - hs_close(scores, "w"); -} - -void hiscores_print_list( int display_count, int format ) -{ - FILE *scores; - int i, total_entries; - bool use_printf = (Options.sc_entries > 0); - - if (display_count <= 0) - display_count = SCORE_FILE_ENTRIES; - - // open highscore file (reading) - scores = hs_open("r"); - if (scores == NULL) - { - // will only happen from command line - puts( "No high scores." ); - return; - } - - // read highscore file - for (i = 0; i < SCORE_FILE_ENTRIES; i++) - { - if (hs_read( scores, hs_list[i] ) == false) - break; - } - total_entries = i; - - // close off - hs_close( scores, "r" ); - - if (!use_printf) - textcolor(LIGHTGREY); - - int start = (newest_entry > 10) ? newest_entry - 10: 0; - - if (start + display_count > total_entries) - start = total_entries - display_count; - - if (start < 0) - start = 0; - - const int finish = start + display_count; - - for (i = start; i < finish && i < total_entries; i++) - { - // check for recently added entry - if (i == newest_entry && !use_printf) - textcolor(YELLOW); - - // print position (tracked implicitly by order score file) - snprintf( info, INFO_SIZE, "%3d.", i + 1 ); - if (use_printf) - printf(info); - else - cprintf(info); - - // format the entry - if (format == SCORE_TERSE) - { - hiscores_format_single( info, hs_list[i] ); - // truncate if we want short format - info[75] = '\0'; - } - else - { - hiscores_format_single_long( info, hs_list[i], - (format == SCORE_VERBOSE) ); - } - - // print entry - strcat(info, EOL); - if(use_printf) - printf(info); - else - cprintf(info); - - if (i == newest_entry && !use_printf) - textcolor(LIGHTGREY); - } -} - -// Trying to supply an appropriate verb for the attack type. -- bwr -static const char *const range_type_verb( const char *const aux ) -{ - if (strncmp( aux, "Shot ", 5 ) == 0) // launched - return ("shot"); - else if (aux[0] == '\0' // unknown - || strncmp( aux, "Hit ", 4 ) == 0 // thrown - || strncmp( aux, "volley ", 7 ) == 0) // manticore spikes - { - return ("hit from afar"); - } - - return ("blasted"); // spells, wands -} - -void hiscores_format_single(char *buf, struct 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) - { - if (se.level_type == LEVEL_ABYSS) - { - strcat(buf, " (Abyss)"); - return; - } - else if (se.level_type == LEVEL_PANDEMONIUM) - { - strcat(buf, " (Pan)"); - return; - } - else if (se.level_type == LEVEL_LABYRINTH) - { - strcat(buf, " (Lab)"); - return; - } - else if (se.branch == BRANCH_VESTIBULE_OF_HELL) - { - strcat(buf, " (Hell)"); // Gate? Vest? - return; - } - else if (se.branch == BRANCH_HALL_OF_BLADES) - { - strcat(buf, " (Blade)"); - return; - } - else if (se.branch == BRANCH_ECUMENICAL_TEMPLE) - { - strcat(buf, " (Temple)"); - return; - } - - snprintf( scratch, sizeof(scratch), " (%s%d)", - (se.branch == BRANCH_DIS) ? "Dis " : - (se.branch == BRANCH_GEHENNA) ? "Geh " : - (se.branch == BRANCH_COCYTUS) ? "Coc " : - (se.branch == BRANCH_TARTARUS) ? "Tar " : - (se.branch == BRANCH_ORCISH_MINES) ? "Orc " : - (se.branch == BRANCH_HIVE) ? "Hive " : - (se.branch == BRANCH_LAIR) ? "Lair " : - (se.branch == BRANCH_SLIME_PITS) ? "Slime " : - (se.branch == BRANCH_VAULTS) ? "Vault " : - (se.branch == BRANCH_CRYPT) ? "Crypt " : - (se.branch == BRANCH_HALL_OF_ZOT) ? "Zot " : - (se.branch == BRANCH_SNAKE_PIT) ? "Snake " : - (se.branch == BRANCH_ELVEN_HALLS) ? "Elf " : - (se.branch == BRANCH_TOMB) ? "Tomb " : - (se.branch == BRANCH_SWAMP) ? "Swamp " : "DLv ", - se.dlvl ); - - strcat( buf, scratch ); - } // endif - killed by winning - - return; -} - -static bool hiscore_same_day( time_t t1, time_t t2 ) -{ - struct tm *d1 = localtime( &t1 ); - const int year = d1->tm_year; - const int mon = d1->tm_mon; - const int day = d1->tm_mday; - - struct tm *d2 = localtime( &t2 ); - - return (d2->tm_mday == day && d2->tm_mon == mon && d2->tm_year == year); -} - -static void hiscore_date_string( time_t time, char buff[INFO_SIZE] ) -{ - struct tm *date = localtime( &time ); - - const char *mons[12] = { "Jan", "Feb", "Mar", "Apr", "May", "June", - "July", "Aug", "Sept", "Oct", "Nov", "Dec" }; - - snprintf( buff, INFO_SIZE, "%s %d, %d", mons[date->tm_mon], - date->tm_mday, date->tm_year + 1900 ); -} - -static void hiscore_newline( char *buf, int &line_count ) -{ - strncat( buf, EOL " ", HIGHSCORE_SIZE ); - line_count++; -} - -int hiscores_format_single_long( char *buf, struct 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, "..." ); - - if (se.level_type == LEVEL_ABYSS) - strcat( buf, " in the Abyss" ); - else if (se.level_type == LEVEL_PANDEMONIUM) - strcat( buf, " in Pandemonium" ); - else if (se.level_type == LEVEL_LABYRINTH) - strcat( buf, " in a labyrinth" ); - else - { - switch (se.branch) - { - case BRANCH_ECUMENICAL_TEMPLE: - strcat( buf, " in the Ecumenical Temple" ); - break; - case BRANCH_HALL_OF_BLADES: - strcat( buf, " in the Hall of Blades" ); - break; - case BRANCH_VESTIBULE_OF_HELL: - strcat( buf, " in the Vestibule" ); - break; - - case BRANCH_DIS: - strcat( buf, " on Dis" ); - break; - case BRANCH_GEHENNA: - strcat( buf, " on Gehenna" ); - break; - case BRANCH_COCYTUS: - strcat( buf, " on Cocytus" ); - break; - case BRANCH_TARTARUS: - strcat( buf, " on Tartarus" ); - break; - case BRANCH_ORCISH_MINES: - strcat( buf, " on Orcish Mines" ); - break; - case BRANCH_HIVE: - strcat( buf, " on Hive" ); - break; - case BRANCH_LAIR: - strcat( buf, " on Lair" ); - break; - case BRANCH_SLIME_PITS: - strcat( buf, " on Slime Pits" ); - break; - case BRANCH_VAULTS: - strcat( buf, " on Vault" ); - break; - case BRANCH_CRYPT: - strcat( buf, " on Crypt" ); - break; - case BRANCH_HALL_OF_ZOT: - strcat( buf, " on Hall of Zot" ); - break; - case BRANCH_SNAKE_PIT: - strcat( buf, " on Snake Pit" ); - break; - case BRANCH_ELVEN_HALLS: - strcat( buf, " on Elven Halls" ); - break; - case BRANCH_TOMB: - strcat( buf, " on Tomb" ); - break; - case BRANCH_SWAMP: - strcat( buf, " on Swamp" ); - break; - case BRANCH_MAIN_DUNGEON: - strcat( buf, " on Dungeon" ); - break; - } - - if (se.branch != BRANCH_VESTIBULE_OF_HELL - && se.branch != BRANCH_ECUMENICAL_TEMPLE - && se.branch != BRANCH_HALL_OF_BLADES) - { - snprintf( scratch, sizeof(scratch), " Level %d", se.dlvl ); - strcat( buf, scratch ); - } - } - - 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); -} - -// -------------------------------------------------------------------------- -// BEGIN private functions -// -------------------------------------------------------------------------- - -// first, some file locking stuff for multiuser crawl -#ifdef USE_FILE_LOCKING - -static bool lock_file_handle( FILE *handle, int type ) -{ - struct flock lock; - int status; - - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; - lock.l_type = type; - -#ifdef USE_BLOCKING_LOCK - - status = fcntl( fileno( handle ), F_SETLKW, &lock ); - -#else - - for (int i = 0; i < 30; i++) - { - status = fcntl( fileno( handle ), F_SETLK, &lock ); - - // success - if (status == 0) - break; - - // known failure - if (status == -1 && (errno != EACCES && errno != EAGAIN)) - break; - - perror( "Problems locking file... retrying..." ); - delay( 1000 ); - } - -#endif - - return (status == 0); -} - -static bool unlock_file_handle( FILE *handle ) -{ - struct flock lock; - int status; - - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; - lock.l_type = F_UNLCK; - -#ifdef USE_BLOCKING_LOCK - - status = fcntl( fileno( handle ), F_SETLKW, &lock ); - -#else - - for (int i = 0; i < 30; i++) - { - status = fcntl( fileno( handle ), F_SETLK, &lock ); - - // success - if (status == 0) - break; - - // known failure - if (status == -1 && (errno != EACCES && errno != EAGAIN)) - break; - - perror( "Problems unlocking file... retrying..." ); - delay( 1000 ); - } - -#endif - - return (status == 0); -} - -#endif - - - -FILE *hs_open( const char *mode ) -{ -#ifdef SAVE_DIR_PATH - FILE *handle = fopen(SAVE_DIR_PATH "scores", mode); -#ifdef SHARED_FILES_CHMOD_PUBLIC - chmod(SAVE_DIR_PATH "scores", SHARED_FILES_CHMOD_PUBLIC); -#endif -#else - FILE *handle = fopen("scores", mode); -#endif - -#ifdef USE_FILE_LOCKING - int locktype = F_RDLCK; - if (stricmp(mode, "w") == 0) - locktype = F_WRLCK; - - if (handle && !lock_file_handle( handle, locktype )) - { - perror( "Could not lock scorefile... " ); - fclose( handle ); - handle = NULL; - } -#endif - return handle; -} - -void hs_close( FILE *handle, const char *mode ) -{ - UNUSED( mode ); - - if (handle == NULL) - return; - -#ifdef USE_FILE_LOCKING - unlock_file_handle( handle ); -#endif - - // actually close - fclose(handle); - -#ifdef SHARED_FILES_CHMOD_PUBLIC - if (stricmp(mode, "w") == 0) - { - #ifdef SAVE_DIR_PATH - chmod(SAVE_DIR_PATH "scores", SHARED_FILES_CHMOD_PUBLIC); - #else - chmod("scores", SHARED_FILES_CHMOD_PUBLIC); - #endif - } -#endif -} - -static void hs_init( struct scorefile_entry &dest ) -{ - // simple init - dest.version = 0; - dest.release = 0; - dest.points = -1; - dest.name[0] = '\0'; - dest.uid = 0; - dest.race = 0; - dest.cls = 0; - dest.lvl = 0; - dest.race_class_name[0] = '\0'; - dest.best_skill = 0; - dest.best_skill_lvl = 0; - dest.death_type = KILLED_BY_SOMETHING; - dest.death_source = 0; - dest.mon_num = 0; - dest.death_source_name[0] = '\0'; - dest.auxkilldata[0] = '\0'; - dest.dlvl = 0; - dest.level_type = 0; - dest.branch = 0; - dest.final_hp = -1; - dest.final_max_hp = -1; - dest.final_max_max_hp = -1; - dest.str = -1; - dest.intel = -1; - dest.dex = -1; - dest.damage = -1; - dest.god = -1; - dest.piety = -1; - dest.penance = -1; - dest.wiz_mode = 0; - dest.birth_time = 0; - dest.death_time = 0; - dest.real_time = -1; - dest.num_turns = -1; - dest.num_diff_runes = 0; - dest.num_runes = 0; -} - -void hs_copy(struct scorefile_entry &dest, struct scorefile_entry &src) -{ - // simple field copy -- assume src is well constructed. - - dest.version = src.version; - dest.release = src.release; - dest.points = src.points; - strcpy(dest.name, src.name); - dest.uid = src.uid; - dest.race = src.race; - dest.cls = src.cls; - dest.lvl = src.lvl; - strcpy(dest.race_class_name, src.race_class_name); - dest.best_skill = src.best_skill; - dest.best_skill_lvl = src.best_skill_lvl; - dest.death_type = src.death_type; - dest.death_source = src.death_source; - dest.mon_num = src.mon_num; - strcpy( dest.death_source_name, src.death_source_name ); - strcpy( dest.auxkilldata, src.auxkilldata ); - dest.dlvl = src.dlvl; - dest.level_type = src.level_type; - dest.branch = src.branch; - dest.final_hp = src.final_hp; - dest.final_max_hp = src.final_max_hp; - dest.final_max_max_hp = src.final_max_max_hp; - dest.str = src.str; - dest.intel = src.intel; - dest.dex = src.dex; - dest.damage = src.damage; - dest.god = src.god; - dest.piety = src.piety; - dest.penance = src.penance; - dest.wiz_mode = src.wiz_mode; - dest.birth_time = src.birth_time; - dest.death_time = src.death_time; - dest.real_time = src.real_time; - dest.num_turns = src.num_turns; - dest.num_diff_runes = src.num_diff_runes; - dest.num_runes = src.num_runes; -} - -bool hs_read( FILE *scores, struct scorefile_entry &dest ) -{ - char inbuf[200]; - int c = EOF; - - hs_init( dest ); - - // get a character.. - if (scores != NULL) - c = fgetc(scores); - - // check for NULL scores file or EOF - if (scores == NULL || c == EOF) - return false; - - // get a line - this is tricky. "Lines" come in three flavors: - // 1) old-style lines which were 80 character blocks - // 2) 4.0 pr1 through pr7 versions which were newline terminated - // 3) 4.0 pr8 and onwards which are 'current' ASCII format, and - // may exceed 80 characters! - - // put 'c' in first spot - inbuf[0] = c; - - if (fgets(&inbuf[1], (c==':') ? (sizeof(inbuf) - 2) : 81, scores) == NULL) - return false; - - // check type; lines starting with a colon are new-style scores. - if (c == ':') - hs_parse_numeric(inbuf, dest); - else - hs_parse_string(inbuf, dest); - - return true; -} - -static void hs_nextstring(char *&inbuf, char *dest) -{ - char *p = dest; - - if (*inbuf == '\0') - { - *p = '\0'; - return; - } - - // assume we're on a ':' - inbuf ++; - while(*inbuf != ':' && *inbuf != '\0') - *p++ = *inbuf++; - - *p = '\0'; -} - -static int hs_nextint(char *&inbuf) -{ - char num[20]; - hs_nextstring(inbuf, num); - - return (num[0] == '\0' ? 0 : atoi(num)); -} - -static long hs_nextlong(char *&inbuf) -{ - char num[20]; - hs_nextstring(inbuf, num); - - return (num[0] == '\0' ? 0 : atol(num)); -} - -static int val_char( char digit ) -{ - return (digit - '0'); -} - -static time_t hs_nextdate(char *&inbuf) -{ - char buff[20]; - struct tm date; - - hs_nextstring( inbuf, buff ); - - if (strlen( buff ) < 15) - return (static_cast<time_t>(0)); - - date.tm_year = val_char( buff[0] ) * 1000 + val_char( buff[1] ) * 100 - + val_char( buff[2] ) * 10 + val_char( buff[3] ) - 1900; - - date.tm_mon = val_char( buff[4] ) * 10 + val_char( buff[5] ); - date.tm_mday = val_char( buff[6] ) * 10 + val_char( buff[7] ); - date.tm_hour = val_char( buff[8] ) * 10 + val_char( buff[9] ); - date.tm_min = val_char( buff[10] ) * 10 + val_char( buff[11] ); - date.tm_sec = val_char( buff[12] ) * 10 + val_char( buff[13] ); - date.tm_isdst = (buff[14] == 'D'); - - return (mktime( &date )); -} - -static void hs_parse_numeric(char *inbuf, struct scorefile_entry &se) -{ - se.version = hs_nextint(inbuf); - se.release = hs_nextint(inbuf); - - // this would be a good point to check for version numbers and branch - // appropriately - - // acceptable versions are 0 (converted from old hiscore format) and 4 - if (se.version != 0 && se.version != 4) - return; - - se.points = hs_nextlong(inbuf); - - hs_nextstring(inbuf, se.name); - - se.uid = hs_nextlong(inbuf); - se.race = hs_nextint(inbuf); - se.cls = hs_nextint(inbuf); - - hs_nextstring(inbuf, se.race_class_name); - - se.lvl = hs_nextint(inbuf); - se.best_skill = hs_nextint(inbuf); - se.best_skill_lvl = hs_nextint(inbuf); - se.death_type = hs_nextint(inbuf); - se.death_source = hs_nextint(inbuf); - se.mon_num = hs_nextint(inbuf); - - hs_nextstring(inbuf, se.death_source_name); - - // To try and keep the scorefile backwards compatible, - // we'll branch on version > 4.0 to read the auxkilldata - // text field. - if (se.version == 4 && se.release >= 1) - hs_nextstring( inbuf, se.auxkilldata ); - else - se.auxkilldata[0] = '\0'; - - se.dlvl = hs_nextint(inbuf); - se.level_type = hs_nextint(inbuf); - se.branch = hs_nextint(inbuf); - - // Trying to fix some bugs that have been around since at - // least pr19, if not longer. From now on, dlvl should - // be calculated on death and need no further modification. - if (se.version < 4 || se.release < 2) - { - if (se.level_type == LEVEL_DUNGEON) - { - if (se.branch == BRANCH_MAIN_DUNGEON) - se.dlvl += 1; - else if (se.branch < BRANCH_ORCISH_MINES) // ie the hells - se.dlvl -= 1; - } - } - - se.final_hp = hs_nextint(inbuf); - if (se.version == 4 && se.release >= 2) - { - se.final_max_hp = hs_nextint(inbuf); - se.final_max_max_hp = hs_nextint(inbuf); - se.damage = hs_nextint(inbuf); - se.str = hs_nextint(inbuf); - se.intel = hs_nextint(inbuf); - se.dex = hs_nextint(inbuf); - se.god = hs_nextint(inbuf); - se.piety = hs_nextint(inbuf); - se.penance = hs_nextint(inbuf); - } - else - { - se.final_max_hp = -1; - se.final_max_max_hp = -1; - se.damage = -1; - se.str = -1; - se.intel = -1; - se.dex = -1; - se.god = -1; - se.piety = -1; - se.penance = -1; - } - - se.wiz_mode = hs_nextint(inbuf); - - se.birth_time = hs_nextdate(inbuf); - se.death_time = hs_nextdate(inbuf); - - if (se.version == 4 && se.release >= 2) - { - se.real_time = hs_nextint(inbuf); - se.num_turns = hs_nextint(inbuf); - } - else - { - se.real_time = -1; - se.num_turns = -1; - } - - se.num_diff_runes = hs_nextint(inbuf); - se.num_runes = hs_nextint(inbuf); -} - -static void hs_write( FILE *scores, struct scorefile_entry &se ) -{ - char buff[80]; // should be more than enough for date stamps - - se.version = 4; - se.release = 2; - - fprintf( scores, ":%d:%d:%ld:%s:%ld:%d:%d:%s:%d:%d:%d", - se.version, se.release, se.points, se.name, - se.uid, se.race, se.cls, se.race_class_name, se.lvl, - se.best_skill, se.best_skill_lvl ); - - // XXX: need damage - fprintf( scores, ":%d:%d:%d:%s:%s:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", - se.death_type, se.death_source, se.mon_num, - se.death_source_name, se.auxkilldata, - se.dlvl, se.level_type, se.branch, - se.final_hp, se.final_max_hp, se.final_max_max_hp, se.damage, - se.str, se.intel, se.dex, - se.god, se.piety, se.penance, se.wiz_mode ); - - make_date_string( se.birth_time, buff ); - fprintf( scores, ":%s", buff ); - - make_date_string( se.death_time, buff ); - fprintf( scores, ":%s", buff ); - - fprintf( scores, ":%ld:%ld:%d:%d:\n", - se.real_time, se.num_turns, se.num_diff_runes, se.num_runes ); -} -// ------------------------------------------------------------------------- -// functions dealing with old-style scorefile entries. -// ------------------------------------------------------------------------- - -static void hs_parse_string(char *inbuf, struct scorefile_entry &se) -{ - /* old entries are of the following format (Brent introduced some - spacing at one point, we have to take this into account): - - // Actually, I believe it might have been Brian who added the spaces, - // I was quite happy with the condensed version, given the 80 column - // restriction. -- bwr - -6263 BoBo - DSD10 Wiz, killed by an acid blob on L1 of the Slime Pits. -5877 Aldus-DGM10, killed by a lethal dose of poison on L10. -5419 Yarf - Koa10, killed by a warg on L1 of the Mines. - - 1. All numerics up to the first non-numeric are the score - 2. All non '-' characters are the name. Strip spaces. - 3. All alphabetics up to the first numeric are race/class - 4. All numerics up to the comma are the clevel - 5. From the comma, search for known fixed substrings and - translate to death_type. Leave death source = 0 for old - scores, and just copy in the monster name. - 6. Look for the branch type (again, substring search for - fixed strings) and level. - - Very ugly and time consuming. - - */ - - char scratch[80]; - - // 1. get score - hs_parse_generic_2(inbuf, scratch, "0123456789"); - - se.version = 0; // version # of converted score - se.release = 0; - se.points = atoi(scratch); - - // 2. get name - hs_parse_generic_1(inbuf, scratch, "-"); - hs_stripblanks(scratch); - strcpy(se.name, scratch); - - // 3. get race, class - inbuf++; // skip '-' - hs_parse_generic_1(inbuf, scratch, "0123456789"); - hs_stripblanks(scratch); - strcpy(se.race_class_name, scratch); - se.race = 0; - se.cls = 0; - - // 4. get clevel - hs_parse_generic_2(inbuf, scratch, "0123456789"); - se.lvl = atoi(scratch); - - // 4a. get wizard mode - hs_parse_generic_1(inbuf, scratch, ","); - if (strstr(scratch, "Wiz") != NULL) - se.wiz_mode = 1; - else - se.wiz_mode = 0; - - // 5. get death type - inbuf++; // skip comma - hs_search_death(inbuf, se); - - // 6. get branch, level - hs_search_where(inbuf, se); - - // set things that can't be picked out of old scorefile entries - se.uid = 0; - se.best_skill = 0; - se.best_skill_lvl = 0; - se.final_hp = 0; - se.final_max_hp = -1; - se.final_max_max_hp = -1; - se.damage = -1; - se.str = -1; - se.intel = -1; - se.dex = -1; - se.god = -1; - se.piety = -1; - se.penance = -1; - se.birth_time = 0; - se.death_time = 0; - se.real_time = -1; - se.num_turns = -1; - se.num_runes = 0; - se.num_diff_runes = 0; - se.auxkilldata[0] = '\0'; -} - -static void hs_parse_generic_1(char *&inbuf, char *outbuf, const char *stopvalues) -{ - char *p = outbuf; - - while(strchr(stopvalues, *inbuf) == NULL && *inbuf != '\0') - *p++ = *inbuf++; - - *p = '\0'; -} - -static void hs_parse_generic_2(char *&inbuf, char *outbuf, const char *continuevalues) -{ - char *p = outbuf; - - while(strchr(continuevalues, *inbuf) != NULL && *inbuf != '\0') - *p++ = *inbuf++; - - *p = '\0'; -} - -static void hs_stripblanks(char *buf) -{ - char *p = buf; - char *q = buf; - - // strip leading - while(*p == ' ') - p++; - while(*p != '\0') - *q++ = *p++; - - *q-- = '\0'; - // strip trailing - while(*q == ' ') - { - *q = '\0'; - q--; - } -} - -static void hs_search_death(char *inbuf, struct scorefile_entry &se) -{ - // assume killed by monster - se.death_type = KILLED_BY_MONSTER; - - // sigh.. - if (strstr(inbuf, "killed by a lethal dose of poison") != NULL) - se.death_type = KILLED_BY_POISON; - else if (strstr(inbuf, "killed by a cloud") != NULL) - se.death_type = KILLED_BY_CLOUD; - else if (strstr(inbuf, "killed from afar by") != NULL) - se.death_type = KILLED_BY_BEAM; - else if (strstr(inbuf, "took a swim in molten lava") != NULL) - se.death_type = KILLED_BY_LAVA; - else if (strstr(inbuf, "asphyxiated")) - se.death_type = KILLED_BY_CURARE; - else if (strstr(inbuf, "soaked and fell apart") != NULL) - { - se.death_type = KILLED_BY_WATER; - se.race = SP_MUMMY; - } - else if (strstr(inbuf, "drowned") != NULL) - se.death_type = KILLED_BY_WATER; - else if (strstr(inbuf, "died of stupidity") != NULL) - se.death_type = KILLED_BY_STUPIDITY; - else if (strstr(inbuf, "too weak to continue adventuring") != NULL) - se.death_type = KILLED_BY_WEAKNESS; - else if (strstr(inbuf, "slipped on a banana peel") != NULL) - se.death_type = KILLED_BY_CLUMSINESS; - else if (strstr(inbuf, "killed by a trap") != NULL) - se.death_type = KILLED_BY_TRAP; - else if (strstr(inbuf, "got out of the dungeon alive") != NULL) - se.death_type = KILLED_BY_LEAVING; - else if (strstr(inbuf, "escaped with the Orb") != NULL) - se.death_type = KILLED_BY_WINNING; - else if (strstr(inbuf, "quit") != NULL) - se.death_type = KILLED_BY_QUITTING; - else if (strstr(inbuf, "was drained of all life") != NULL) - se.death_type = KILLED_BY_DRAINING; - else if (strstr(inbuf, "starved to death") != NULL) - se.death_type = KILLED_BY_STARVATION; - else if (strstr(inbuf, "froze to death") != NULL) - se.death_type = KILLED_BY_FREEZING; - else if (strstr(inbuf, "burnt to a crisp") != NULL) - se.death_type = KILLED_BY_BURNING; - else if (strstr(inbuf, "killed by wild magic") != NULL) - se.death_type = KILLED_BY_WILD_MAGIC; - else if (strstr(inbuf, "killed by Xom") != NULL) - se.death_type = KILLED_BY_XOM; - else if (strstr(inbuf, "killed by a statue") != NULL) - se.death_type = KILLED_BY_STATUE; - else if (strstr(inbuf, "rotted away") != NULL) - se.death_type = KILLED_BY_ROTTING; - else if (strstr(inbuf, "killed by bad target") != NULL) - se.death_type = KILLED_BY_TARGETTING; - else if (strstr(inbuf, "killed by an exploding spore") != NULL) - se.death_type = KILLED_BY_SPORE; - else if (strstr(inbuf, "smote by The Shining One") != NULL) - se.death_type = KILLED_BY_TSO_SMITING; - else if (strstr(inbuf, "turned to stone") != NULL) - se.death_type = KILLED_BY_PETRIFICATION; - else if (strstr(inbuf, "melted into a puddle") != NULL) - se.death_type = KILLED_BY_MELTING; - else if (strstr(inbuf, "bled to death") != NULL) - se.death_type = KILLED_BY_BLEEDING; - - // whew! - - // now, if we're still KILLED_BY_MONSTER, make sure that there is - // a "killed by" somewhere, or else we're setting it to UNKNOWN. - if (se.death_type == KILLED_BY_MONSTER) - { - if (strstr(inbuf, "killed by") == NULL) - se.death_type = KILLED_BY_SOMETHING; - } - - // set some fields - se.death_source = 0; - se.mon_num = 0; - strcpy(se.death_source_name, ""); - - // now try to pull the monster out. - if (se.death_type == KILLED_BY_MONSTER || se.death_type == KILLED_BY_BEAM) - { - char *p = strstr(inbuf, " by "); - p += 4; - char *q = strstr(inbuf, " on "); - if (q == NULL) - q = strstr(inbuf, " in "); - char *d = se.death_source_name; - while(p != q) - *d++ = *p++; - - *d = '\0'; - } -} - -static void hs_search_where(char *inbuf, struct scorefile_entry &se) -{ - char scratch[6]; - - se.level_type = LEVEL_DUNGEON; - se.branch = BRANCH_MAIN_DUNGEON; - se.dlvl = 0; - - // early out - if (se.death_type == KILLED_BY_LEAVING || se.death_type == KILLED_BY_WINNING) - return; - - // here we go again. - if (strstr(inbuf, "in the Abyss") != NULL) - se.level_type = LEVEL_ABYSS; - else if (strstr(inbuf, "in Pandemonium") != NULL) - se.level_type = LEVEL_PANDEMONIUM; - else if (strstr(inbuf, "in a labyrinth") != NULL) - se.level_type = LEVEL_LABYRINTH; - - // early out for special level types - if (se.level_type != LEVEL_DUNGEON) - return; - - // check for vestible - if (strstr(inbuf, "in the Vestibule") != NULL) - { - se.branch = BRANCH_VESTIBULE_OF_HELL; - return; - } - - // from here, we have branch and level. - char *p = strstr(inbuf, "on L"); - if (p != NULL) - { - p += 4; - hs_parse_generic_2(p, scratch, "0123456789"); - se.dlvl = atoi( scratch ); - } - - // get branch. - if (strstr(inbuf, "of Dis") != NULL) - se.branch = BRANCH_DIS; - else if (strstr(inbuf, "of Gehenna") != NULL) - se.branch = BRANCH_GEHENNA; - else if (strstr(inbuf, "of Cocytus") != NULL) - se.branch = BRANCH_COCYTUS; - else if (strstr(inbuf, "of Tartarus") != NULL) - se.branch = BRANCH_TARTARUS; - else if (strstr(inbuf, "of the Mines") != NULL) - se.branch = BRANCH_ORCISH_MINES; - else if (strstr(inbuf, "of the Hive") != NULL) - se.branch = BRANCH_HIVE; - else if (strstr(inbuf, "of the Lair") != NULL) - se.branch = BRANCH_LAIR; - else if (strstr(inbuf, "of the Slime Pits") != NULL) - se.branch = BRANCH_SLIME_PITS; - else if (strstr(inbuf, "of the Vaults") != NULL) - se.branch = BRANCH_VAULTS; - else if (strstr(inbuf, "of the Crypt") != NULL) - se.branch = BRANCH_CRYPT; - else if (strstr(inbuf, "of the Hall") != NULL) - se.branch = BRANCH_HALL_OF_BLADES; - else if (strstr(inbuf, "of Zot's Hall") != NULL) - se.branch = BRANCH_HALL_OF_ZOT; - else if (strstr(inbuf, "of the Temple") != NULL) - se.branch = BRANCH_ECUMENICAL_TEMPLE; - else if (strstr(inbuf, "of the Snake Pit") != NULL) - se.branch = BRANCH_SNAKE_PIT; - else if (strstr(inbuf, "of the Elf Hall") != NULL) - se.branch = BRANCH_ELVEN_HALLS; - else if (strstr(inbuf, "of the Tomb") != NULL) - se.branch = BRANCH_TOMB; - else if (strstr(inbuf, "of the Swamp") != NULL) - se.branch = BRANCH_SWAMP; -} |