/*
* File: chardump.cc
* Summary: Dumps character info out to the morgue file.
* Written by: Linley Henzell
*/
#include "AppHdr.h"
#include "chardump.h"
#include "clua.h"
#include <string>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#if !defined(__IBMCPP__) && !defined(TARGET_COMPILER_VC)
#include <unistd.h>
#endif
#include <ctype.h>
#include "externs.h"
#include "options.h"
#include "artefact.h"
#include "debug.h"
#include "describe.h"
#include "dungeon.h"
#include "hiscores.h"
#include "initfile.h"
#include "itemprop.h"
#include "itemname.h"
#include "items.h"
#include "kills.h"
#include "macro.h"
#include "message.h"
#include "menu.h"
#include "mutation.h"
#include "notes.h"
#include "output.h"
#include "overmap.h"
#include "place.h"
#include "player.h"
#include "religion.h"
#include "shopping.h"
#include "showsymb.h"
#include "skills2.h"
#include "spl-book.h"
#include "spl-cast.h"
#include "spl-util.h"
#include "stash.h"
#include "stuff.h"
#include "env.h"
#include "transform.h"
#include "travel.h"
#include "view.h"
#include "viewchar.h"
#include "xom.h"
struct dump_params;
static void _sdump_header(dump_params &);
static void _sdump_stats(dump_params &);
static void _sdump_location(dump_params &);
static void _sdump_religion(dump_params &);
static void _sdump_burden(dump_params &);
static void _sdump_hunger(dump_params &);
static void _sdump_transform(dump_params &);
static void _sdump_visits(dump_params &);
static void _sdump_gold(dump_params &);
static void _sdump_misc(dump_params &);
static void _sdump_turns_by_place(dump_params &);
static void _sdump_notes(dump_params &);
static void _sdump_inventory(dump_params &);
static void _sdump_skills(dump_params &);
static void _sdump_spells(dump_params &);
static void _sdump_mutations(dump_params &);
static void _sdump_messages(dump_params &);
static void _sdump_screenshot(dump_params &);
static void _sdump_kills_by_place(dump_params &);
static void _sdump_kills(dump_params &);
static void _sdump_newline(dump_params &);
static void _sdump_overview(dump_params &);
static void _sdump_hiscore(dump_params &);
static void _sdump_monster_list(dump_params &);
static void _sdump_vault_list(dump_params &);
static void _sdump_separator(dump_params &);
#ifdef CLUA_BINDINGS
static void _sdump_lua(dump_params &);
#endif
static bool write_dump(const std::string &fname, dump_params &);
struct dump_section_handler
{
const char *name;
void (*handler)(dump_params &);
};
struct dump_params
{
std::string &text;
std::string section;
bool show_prices;
bool full_id;
const scorefile_entry *se;
dump_params(std::string &_text, const std::string &sec = "",
bool prices = false, bool id = false,
const scorefile_entry *s = NULL)
: text(_text), section(sec), show_prices(prices), full_id(id),
se(s)
{
}
};
static dump_section_handler dump_handlers[] = {
{ "header", _sdump_header },
{ "stats", _sdump_stats },
{ "location", _sdump_location },
{ "religion", _sdump_religion },
{ "burden", _sdump_burden },
{ "hunger", _sdump_hunger },
{ "transform", _sdump_transform },
{ "visits", _sdump_visits },
{ "gold", _sdump_gold },
{ "misc", _sdump_misc },
{ "turns_by_place", _sdump_turns_by_place},
{ "notes", _sdump_notes },
{ "inventory", _sdump_inventory },
{ "skills", _sdump_skills },
{ "spells", _sdump_spells },
{ "mutations", _sdump_mutations },
{ "messages", _sdump_messages },
{ "screenshot", _sdump_screenshot },
{ "kills_by_place", _sdump_kills_by_place},
{ "kills", _sdump_kills },
{ "overview", _sdump_overview },
{ "hiscore", _sdump_hiscore },
{ "monlist", _sdump_monster_list },
{ "vaults", _sdump_vault_list },
// Conveniences for the .crawlrc artist.
{ "", _sdump_newline },
{ "-", _sdump_separator },
#ifdef CLUA_BINDINGS
{ NULL, _sdump_lua }
#else
{ NULL, NULL }
#endif
};
static void dump_section(dump_params &par)
{
for (int i = 0; ; ++i)
{
if (!dump_handlers[i].name || par.section == dump_handlers[i].name)
{
if (dump_handlers[i].handler)
(*dump_handlers[i].handler)(par);
break;
}
}
}
// #define DEBUG_DUMP_COMMANDS
bool dump_char(const std::string &fname, bool show_prices, bool full_id,
const scorefile_entry *se)
{
// Start with enough room for 100 80 character lines.
std::string text;
text.reserve(100 * 80);
dump_params par(text, "", show_prices, full_id, se);
for (int i = 0, size = Options.dump_order.size(); i < size; ++i)
{
par.section = Options.dump_order[i];
dump_section(par);
}
#ifdef DEBUG_DUMP_COMMANDS
list_all_commands(par.text);
#endif
return write_dump(fname, par);
}
static void _sdump_header(dump_params &par)
{
par.text += " " CRAWL " version " + Version::Long();
par.text += " character file.\n\n";
}
static void _sdump_stats(dump_params &par)
{
par.text += dump_overview_screen(par.full_id);
par.text += "\n\n";
}
static void _sdump_burden(dump_params &par)
{
std::string verb = par.se? "were" : "are";
switch (you.burden_state)
{
case BS_OVERLOADED:
par.text += "You " + verb + " overloaded with stuff.\n";
break;
case BS_ENCUMBERED:
par.text += "You " + verb + " encumbered.\n";
break;
default:
break;
}
}
static void _sdump_hunger(dump_params &par)
{
if (par.se)
par.text += "You were ";
else
par.text += "You are ";
par.text += hunger_level();
par.text += ".\n\n";
}
static void _sdump_transform(dump_params &par)
{
std::string &text(par.text);
if (you.attribute[ATTR_TRANSFORMATION])
{
std::string verb = par.se? "were" : "are";
switch (you.attribute[ATTR_TRANSFORMATION])
{
case TRAN_SPIDER:
text += "You " + verb + " in spider-form.";
break;
case TRAN_BAT:
text += "You " + verb + " in ";
if (you.species == SP_VAMPIRE)
text += "vampire ";
text += "bat-form.";
break;
case TRAN_BLADE_HANDS:
text += "Your hands " + verb + " blades.";
break;
case TRAN_STATUE:
text += "You " + verb + " a stone statue.";
break;
case TRAN_ICE_BEAST:
text += "You " + verb + " a creature of crystalline ice.";
break;
case TRAN_DRAGON:
text += "You " + verb + " a fearsome dragon!";
break;
case TRAN_LICH:
text += "You " + verb + " in lich-form.";
break;
case TRAN_PIG:
text += "You " + verb + " a filthy swine.";
break;
}
text += "\n\n";
}
}
static void _sdump_visits(dump_params &par)
{
std::string &text(par.text);
std::string have = "have ";
std::string seen = "seen";
if (par.se) // you died -> past tense
{
have = "";
seen = "saw";
}
std::vector<PlaceInfo> branches_visited =
you.get_all_place_info(true, true);
PlaceInfo branches_total;
for (unsigned int i = 0; i < branches_visited.size(); i++)
branches_total += branches_visited[i];
text += make_stringf("You %svisited %ld branch",
have.c_str(), branches_visited.size());
if (branches_visited.size() != 1)
text += "es";
text += make_stringf(" of the dungeon, and %s %ld of its levels.\n",
seen.c_str(), branches_total.levels_seen);
PlaceInfo place_info = you.get_place_info(LEVEL_PANDEMONIUM);
if (place_info.num_visits > 0)
{
text += make_stringf("You %svisited Pandemonium %ld time",
have.c_str(), place_info.num_visits);
if (place_info.num_visits > 1)
text += "s";
text += make_stringf(", and %s %ld of its levels.\n",
seen.c_str(), place_info.levels_seen);
}
place_info = you.get_place_info(LEVEL_ABYSS);
if (place_info.num_visits > 0)
{
text += make_stringf("You %svisited the Abyss %ld time",
have.c_str(), place_info.num_visits);
if (place_info.num_visits > 1)
text += "s";
text += ".\n";
}
place_info = you.get_place_info(LEVEL_LABYRINTH);
if (place_info.num_visits > 0)
{
text += make_stringf("You %svisited %ld Labyrinth",
have.c_str(), place_info.num_visits);
if (place_info.num_visits > 1)
text += "s";
text += ".\n";
}
place_info = you.get_place_info(LEVEL_PORTAL_VAULT);
if (place_info.num_visits > 0)
{
CrawlVector &vaults =
you.props[YOU_PORTAL_VAULT_NAMES_KEY].get_vector();
int num_bazaars = 0;
int num_zigs = 0;
int zig_levels = 0;
std::vector<std::string> misc_portals;
for (unsigned int i = 0; i < vaults.size(); i++)
{
std::string name = vaults[i].get_string();
if (name.find("Ziggurat") != std::string::npos)
{
zig_levels++;
if (name == "Ziggurat:1")
num_zigs++;
}
else if (name == "bazaar")
num_bazaars++;
else
misc_portals.push_back(name);
}
if (num_bazaars > 0)
{
text += make_stringf("You %svisited %d bazaar",
have.c_str(), num_bazaars);
if (num_bazaars > 1)
text += "s";
text += ".\n";
}
if (num_zigs > 0)
{
text += make_stringf("You %svisited %ld Ziggurat",
have.c_str(), num_zigs);
if (num_zigs > 1)
text += "s";
text += make_stringf(", and %s %ld of %s levels.\n",
seen.c_str(), zig_levels,
num_zigs > 1 ? "their" : "its");
}
if (!misc_portals.empty())
{
text += make_stringf("You %svisited %ld portal chamber",
have.c_str(), misc_portals.size());
if (misc_portals.size() > 1)
text += "s";
text += ": ";
text += comma_separated_line(misc_portals.begin(),
misc_portals.end(),
", ");
text += ".\n";
}
}
text += "\n";
}
static void _sdump_gold(dump_params &par)
{
std::string &text(par.text);
int lines = 0;
const char* have = "have ";
if (par.se) // you died -> past tense
have = "";
if (you.attribute[ATTR_GOLD_FOUND] > 0)
{
lines++;
text += make_stringf("You %scollected %d gold pieces.\n", have,
you.attribute[ATTR_GOLD_FOUND]);
}
if (you.attribute[ATTR_PURCHASES] > 0)
{
lines++;
text += make_stringf("You %sspent %d gold pieces at shops.\n", have,
you.attribute[ATTR_PURCHASES]);
}
if (you.attribute[ATTR_DONATIONS] > 0)
{
lines++;
text += make_stringf("You %sdonated %d gold pieces.\n", have,
you.attribute[ATTR_DONATIONS]);
}
if (you.attribute[ATTR_MISC_SPENDING] > 0)
{
lines++;
text += make_stringf("You %sused %d gold pieces for miscellaneous "
"purposes.\n", have,
you.attribute[ATTR_MISC_SPENDING]);
}
if (lines > 0)
text += "\n";
}
static void _sdump_misc(dump_params &par)
{
_sdump_location(par);
_sdump_religion(par);
_sdump_burden(par);
_sdump_hunger(par);
_sdump_transform(par);
_sdump_visits(par);
_sdump_gold(par);
}
#define TO_PERCENT(x, y) (100.0f * ((float) (x)) / ((float) (y)))
static std::string _sdump_turns_place_info(PlaceInfo place_info,
std::string name = "")
{
PlaceInfo gi = you.global_info;
std::string out;
if (name.empty())
name = place_info.short_name();
float a, b, c, d, e, f;
unsigned int non_interlevel =
place_info.turns_total - place_info.turns_interlevel;
unsigned int global_non_interlevel =
gi.turns_total - gi.turns_interlevel;
a = TO_PERCENT(place_info.turns_total, gi.turns_total);
b = TO_PERCENT(non_interlevel, global_non_interlevel);
c = TO_PERCENT(place_info.turns_interlevel, place_info.turns_total);
d = TO_PERCENT(place_info.turns_resting, non_interlevel);
e = TO_PERCENT(place_info.turns_explore, non_interlevel);
f = (float) non_interlevel / (float) place_info.levels_seen;
out =
make_stringf("%14s | %5.1f | %5.1f | %5.1f | %5.1f | %5.1f | %13.1f\n",
name.c_str(), a, b, c , d, e, f);
out = replace_all(out, " nan ", " N/A ");
return out;
}
static void _sdump_turns_by_place(dump_params &par)
{
std::string &text(par.text);
std::vector<PlaceInfo> all_visited =
you.get_all_place_info(true);
text +=
"Table legend:\n"
" A = Turns spent in this place as a percentage of turns spent in the\n"
" entire game.\n"
" B = Non-inter-level travel turns spent in this place as a percentage of\n"
" non-inter-level travel turns spent in the entire game.\n"
" C = Inter-level travel turns spent in this place as a percentage of\n"
" turns spent in this place.\n"
" D = Turns resting spent in this place as a percentage of non-inter-level\n"
" travel turns spent in this place.\n"
" E = Turns spent auto-exploring this place as a percentage of\n"
" non-inter-level travel turns spent in this place.\n"
" F = Non-inter-level travel turns spent in this place divided by the\n"
" number of levels of this place that you've seen.\n\n";
text += " ";
text += " A B C D E F\n";
text += " ";
text += "+-------+-------+-------+-------+-------+----------------------\n";
text += _sdump_turns_place_info(you.global_info, "Total");
for (unsigned int i = 0; i < all_visited.size(); i++)
{
PlaceInfo pi = all_visited[i];
text += _sdump_turns_place_info(pi);
}
text += " ";
text += "+-------+-------+-------+-------+-------+----------------------\n";
text += "\n";
}
static void _sdump_newline(dump_params &par)
{
par.text += "\n";
}
static void _sdump_separator(dump_params &par)
{
par.text += std::string(79, '-') + "\n";
}
#ifdef CLUA_BINDINGS
// Assume this is an arbitrary Lua function name, call the function and
// dump whatever it returns.
static void _sdump_lua(dump_params &par)
{
std::string luatext;
if (!clua.callfn(par.section.c_str(), ">s", &luatext)
&& !clua.error.empty())
{
par.text += "Lua dump error: " + clua.error + "\n";
}
else
par.text += luatext;
}
#endif
//---------------------------------------------------------------
//
// munge_description
//
// Convert dollar signs to EOL and word wrap to 80 characters.
// (for some obscure reason get_item_description uses dollar
// signs instead of EOL).
// - It uses $ signs because they're easier to manipulate than the EOL
// macro, which is of uncertain length (well, that and I didn't know how
// to do it any better at the time) (LH)
//---------------------------------------------------------------
std::string munge_description(const std::string & inStr)
{
std::string outStr;
std::string eol = "\n";
outStr.reserve(inStr.length() + 32);
const int kIndent = 3;
int lineLen = kIndent;
unsigned int i = 0;
outStr += std::string(kIndent, ' ');
while (i < inStr.length())
{
const char ch = inStr[i];
if (ch == '$')
{
outStr += eol;
outStr += std::string(kIndent, ' ');
lineLen = kIndent;
while (inStr[++i] == '$')
;
}
else if (isspace(ch))
{
if (lineLen >= 79)
{
outStr += eol;
outStr += std::string(kIndent, ' ');
lineLen = kIndent;
}
else if (lineLen > 0)
{
outStr += ch;
++lineLen;
}
++i;
}
else
{
std::string word;
while (i < inStr.length()
&& lineLen + word.length() < 79
&& !isspace(inStr[i]) && inStr[i] != '$')
{
word += inStr[i++];
}
if (lineLen + word.length() >= 79)
{
outStr += eol;
outStr += std::string(kIndent, ' ');
lineLen = kIndent;
}
outStr += word;
lineLen += word.length();
}
}
outStr += eol;
return (outStr);
} // end munge_description()
static void _sdump_messages(dump_params &par)
{
// A little message history:
if (Options.dump_message_count > 0)
{
par.text += "Message History\n\n";
par.text += get_last_messages(Options.dump_message_count);
}
}
static void _sdump_screenshot(dump_params &par)
{
par.text += screenshot();
par.text += "\n\n";
}
static void _sdump_notes(dump_params &par)
{
std::string &text(par.text);
if ( note_list.empty() )
return;
text += "\nNotes\nTurn | Place | Note\n";
text += "--------------------------------------------------------------\n";
for ( unsigned i = 0; i < note_list.size(); ++i )
{
text += note_list[i].describe();
text += "\n";
}
text += "\n";
}
//---------------------------------------------------------------
//
// dump_location
//
//---------------------------------------------------------------
static void _sdump_location(dump_params &par)
{
if (you.your_level == -1
&& you.where_are_you == BRANCH_MAIN_DUNGEON
&& you.level_type == LEVEL_DUNGEON)
{
par.text += "You escaped";
}
else if (par.se)
par.text += "You were " + prep_branch_level_name();
else
par.text += "You are " + prep_branch_level_name();
par.text += ".";
par.text += "\n";
} // end dump_location()
static void _sdump_religion(dump_params &par)
{
std::string &text(par.text);
if (you.religion != GOD_NO_GOD)
{
if (par.se)
text += "You worshipped ";
else
text += "You worship ";
text += god_name(you.religion);
text += ".\n";
if (you.religion != GOD_XOM)
{
if (!player_under_penance())
{
text += god_prayer_reaction();
text += "\n";
}
else
{
std::string verb = par.se ? "was" : "is";
text += god_name(you.religion);
text += " " + verb + " demanding penance.\n";
}
}
else
{
if (par.se)
text += "You were ";
else
text += "You are ";
text += describe_xom_favour(false);
text += "\n";
}
}
}
static bool _dump_item_origin(const item_def &item, int value)
{
#define fs(x) (flags & (x))
const int flags = Options.dump_item_origins;
if (flags == IODS_EVERYTHING)
return (true);
if (fs(IODS_ARTEFACTS)
&& is_artefact(item) && item_ident(item, ISFLAG_KNOW_PROPERTIES))
{
return (true);
}
if (fs(IODS_EGO_ARMOUR) && item.base_type == OBJ_ARMOUR
&& item_type_known( item ))
{
const int spec_ench = get_armour_ego_type( item );
return (spec_ench != SPARM_NORMAL);
}
if (fs(IODS_EGO_WEAPON) && item.base_type == OBJ_WEAPONS
&& item_type_known( item ))
{
return (get_weapon_brand(item) != SPWPN_NORMAL);
}
if (fs(IODS_JEWELLERY) && item.base_type == OBJ_JEWELLERY)
return (true);
if (fs(IODS_RUNES) && item.base_type == OBJ_MISCELLANY
&& item.sub_type == MISC_RUNE_OF_ZOT)
{
return (true);
}
if (fs(IODS_RODS) && item.base_type == OBJ_STAVES
&& item_is_rod(item))
{
return (true);
}
if (fs(IODS_STAVES) && item.base_type == OBJ_STAVES
&& !item_is_rod(item))
{
return (true);
}
if (fs(IODS_BOOKS) && item.base_type == OBJ_BOOKS)
return (true);
const int refpr = Options.dump_item_origin_price;
if (refpr == -1)
return (false);
if (value == -1)
value = item_value( item, false );
return (value >= refpr);
#undef fs
}
//---------------------------------------------------------------
//
// dump_inventory
//
//---------------------------------------------------------------
static void _sdump_inventory(dump_params &par)
{
int i, j;
std::string &text(par.text);
std::string text2;
int inv_class2[OBJ_GOLD];
int inv_count = 0;
char tmp_quant[20];
for (i = 0; i < OBJ_GOLD; i++)
inv_class2[i] = 0;
for (i = 0; i < ENDOFPACK; i++)
{
if (you.inv[i].is_valid())
{
// adds up number of each class in invent.
inv_class2[you.inv[i].base_type]++;
inv_count++;
}
}
if (!inv_count)
{
text += "You aren't carrying anything.";
text += "\n";
}
else
{
text += "Inventory:\n\n";
for (i = 0; i < OBJ_GOLD; i++)
{
if (inv_class2[i] != 0)
{
switch (i)
{
case OBJ_WEAPONS: text += "Hand weapons"; break;
case OBJ_MISSILES: text += "Missiles"; break;
case OBJ_ARMOUR: text += "Armour"; break;
case OBJ_WANDS: text += "Magical devices"; break;
case OBJ_FOOD: text += "Comestibles"; break;
case OBJ_SCROLLS: text += "Scrolls"; break;
case OBJ_JEWELLERY: text += "Jewellery"; break;
case OBJ_POTIONS: text += "Potions"; break;
case OBJ_BOOKS: text += "Books"; break;
case OBJ_STAVES: text += "Magical staves"; break;
case OBJ_ORBS: text += "Orbs of Power"; break;
case OBJ_MISCELLANY: text += "Miscellaneous"; break;
case OBJ_CORPSES: text += "Carrion"; break;
default:
DEBUGSTR("Bad item class");
}
text += "\n";
for (j = 0; j < ENDOFPACK; j++)
{
if (you.inv[j].is_valid() && you.inv[j].base_type == i)
{
text += " ";
text += you.inv[j].name(DESC_INVENTORY_EQUIP);
inv_count--;
int ival = -1;
if (par.show_prices)
{
text += " (";
itoa( ival = item_value( you.inv[j], true ),
tmp_quant, 10 );
text += tmp_quant;
text += " gold)";
}
if (origin_describable(you.inv[j])
&& _dump_item_origin(you.inv[j], ival))
{
text += "\n" " (" + origin_desc(you.inv[j]) + ")";
}
if (is_dumpable_artefact( you.inv[j], false )
|| Options.dump_book_spells
&& you.inv[j].base_type == OBJ_BOOKS)
{
text2 = get_item_description( you.inv[j],
false,
true );
text += munge_description(text2);
}
else
{
text += "\n";
}
}
}
}
}
}
text += "\n\n";
}
//---------------------------------------------------------------
//
// dump_skills
//
//---------------------------------------------------------------
static void _sdump_skills(dump_params &par)
{
std::string &text(par.text);
char tmp_quant[20];
if (par.se)
text += " You had ";
else
text += " You have ";
itoa( you.exp_available, tmp_quant, 10 );
text += tmp_quant;
text += " experience left.";
text += "\n";
text += "\n";
text += " Skills:";
text += "\n";
dump_skills(text);
text += "\n";
text += "\n";
}
//---------------------------------------------------------------
//
// Return string of the i-th spell type, with slash if required
//
//---------------------------------------------------------------
static std::string spell_type_shortname(int spell_class, bool slash)
{
std::string ret;
if (slash)
ret = "/";
ret += spelltype_short_name(spell_class);
return (ret);
} // end spell_type_shortname()
//---------------------------------------------------------------
//
// dump_spells
//
//---------------------------------------------------------------
static void _sdump_spells(dump_params &par)
{
std::string &text(par.text);
char tmp_quant[20];
// This array helps output the spell types in the traditional order.
// this can be tossed as soon as I reorder the enum to the traditional order {dlb}
const int spell_type_index[] =
{
SPTYP_HOLY,
SPTYP_POISON,
SPTYP_FIRE,
SPTYP_ICE,
SPTYP_EARTH,
SPTYP_AIR,
SPTYP_CONJURATION,
SPTYP_ENCHANTMENT,
SPTYP_DIVINATION,
SPTYP_TRANSLOCATION,
SPTYP_SUMMONING,
SPTYP_TRANSMUTATION,
SPTYP_NECROMANCY,
0
};
int spell_levels = player_spell_levels();
std::string verb = par.se? "had" : "have";
if (spell_levels == 1)
text += "You " + verb + " one spell level left.";
else if (spell_levels == 0)
{
verb = par.se? "couldn't" : "cannot";
text += "You " + verb + " memorise any spells.";
}
else
{
if (par.se)
text += "You had ";
else
text += "You have ";
itoa( spell_levels, tmp_quant, 10 );
text += tmp_quant;
text += " spell levels left.";
}
text += "\n";
if (!you.spell_no)
{
verb = par.se? "didn't" : "don't";
text += "You " + verb + " know any spells.\n\n";
}
else
{
verb = par.se? "knew" : "know";
text += "You " + verb + " the following spells:\n\n";
text += " Your Spells Type Power Success Level" "\n";
for (int j = 0; j < 52; j++)
{
const char letter = index_to_letter( j );
const spell_type spell = get_spell_by_letter( letter );
if (spell != SPELL_NO_SPELL)
{
std::string spell_line;
spell_line += letter;
spell_line += " - ";
spell_line += spell_title( spell );
if ( spell_line.length() > 24 )
spell_line = spell_line.substr(0, 24);
for (int i = spell_line.length(); i < 26; i++)
spell_line += ' ';
bool already = false;
for (int i = 0; spell_type_index[i] != 0; i++)
{
if (spell_typematch( spell, spell_type_index[i] ))
{
spell_line += spell_type_shortname(spell_type_index[i],
already);
already = true;
}
}
for (int i = spell_line.length(); i < 41; ++i )
spell_line += ' ';
spell_line += spell_power_string(spell);
for (int i = spell_line.length(); i < 56; ++i )
spell_line += ' ';
spell_line += failure_rate_to_string(spell_fail(spell));
for (int i = spell_line.length(); i < 68; i++)
spell_line += ' ';
itoa(spell_difficulty(spell), tmp_quant, 10 );
spell_line += tmp_quant;
spell_line += "\n";
text += spell_line;
}
}
text += "\n\n";
}
} // end dump_spells()
static void _sdump_kills(dump_params &par)
{
par.text += you.kills->kill_info();
}
static std::string _sdump_kills_place_info(PlaceInfo place_info,
std::string name = "")
{
PlaceInfo gi = you.global_info;
std::string out;
if (name.empty())
name = place_info.short_name();
unsigned int global_total_kills = 0;
for (int i = 0; i < KC_NCATEGORIES; i++)
global_total_kills += you.global_info.mon_kill_num[i];
unsigned int total_kills = 0;
for (int i = 0; i < KC_NCATEGORIES; i++)
total_kills += place_info.mon_kill_num[i];
// Skip places where nothing was killed.
if (total_kills == 0)
return "";
float a, b, c, d, e, f, g;
a = TO_PERCENT(total_kills, global_total_kills);
b = TO_PERCENT(place_info.mon_kill_num[KC_YOU],
you.global_info.mon_kill_num[KC_YOU]);
c = TO_PERCENT(place_info.mon_kill_num[KC_FRIENDLY],
you.global_info.mon_kill_num[KC_FRIENDLY]);
d = TO_PERCENT(place_info.mon_kill_num[KC_OTHER],
you.global_info.mon_kill_num[KC_OTHER]);
e = TO_PERCENT(place_info.mon_kill_exp,
you.global_info.mon_kill_exp);
f = TO_PERCENT(place_info.mon_kill_exp_avail,
you.global_info.mon_kill_exp_avail);
g = std::max<float>(place_info.mon_kill_exp, place_info.mon_kill_exp_avail)
/ place_info.levels_seen;
out =
make_stringf("%14s | %5.1f | %5.1f | %5.1f | %5.1f | %5.1f |"
" %5.1f | %13.1f\n",
name.c_str(), a, b, c , d, e, f, g);
out = replace_all(out, " nan ", " N/A ");
return out;
}
static void _sdump_kills_by_place(dump_params &par)
{
std::string &text(par.text);
std::vector<PlaceInfo> all_visited =
you.get_all_place_info(true);
std::string result = "";
std::string header =
"Table legend:\n"
" A = Kills in this place as a percentage of kills in entire the game.\n"
" B = Kills by you in this place as a percentage of kills by you in\n"
" the entire game.\n"
" C = Kills by friends in this place as a percentage of kills by\n"
" friends in the entire game.\n"
" D = Other kills in this place as a percentage of other kills in the\n"
" entire game.\n"
" E = Character level experience gained in this place as a percentage of\n"
" character level experience gained in the entire game.\n"
" F = Skills experience gained in this place as a percentage of skills\n"
" experience gained in the entire game.\n"
" G = Experience gained in this place divided by the number of levels of\n"
" this place that you have seen.\n\n";
header += " ";
header += " A B C D E F G\n";
header += " ";
header += "+-------+-------+-------+-------+-------+-------+--------------\n";
std::string footer = " ";
footer += "+-------+-------+-------+-------+-------+-------+--------------\n";
result += _sdump_kills_place_info(you.global_info, "Total");
for (unsigned int i = 0; i < all_visited.size(); i++)
{
PlaceInfo pi = all_visited[i];
result += _sdump_kills_place_info(pi);
}
if (result.length() > 0)
text += header + result + footer + "\n";
}
static void _sdump_overview(dump_params &par)
{
std::string overview =
formatted_string::parse_string(overview_description_string());
trim_string(overview);
par.text += overview;
par.text += "\n\n";
}
static void _sdump_hiscore(dump_params &par)
{
if (!par.se)
return;
std::string hiscore = hiscores_format_single_long( *(par.se), true );
trim_string(hiscore);
par.text += hiscore;
par.text += "\n\n";
}
static void _sdump_monster_list(dump_params &par)
{
std::string monlist = mpr_monster_list(par.se);
trim_string(monlist);
par.text += monlist;
par.text += "\n\n";
}
static void _sdump_vault_list(dump_params &par)
{
if (par.full_id || par.se
#ifdef WIZARD
|| you.wizard
#endif
)
{
par.text += "Vault maps used:\n\n";
par.text += dump_vault_maps();
}
}
static void _sdump_mutations(dump_params &par)
{
std::string &text(par.text);
if (how_mutated(true, false))
{
text += "\n";
text += describe_mutations();
text += "\n\n";
}
} // end dump_mutations()
// ========================================================================
// Public Functions
// ========================================================================
const char *hunger_level(void)
{
const bool vamp = (you.species == SP_VAMPIRE);
return ((you.hunger <= 1000) ? (vamp ? "bloodless" : "starving") :
(you.hunger <= 1533) ? (vamp ? "near bloodless" : "near starving") :
(you.hunger <= 2066) ? (vamp ? "very thirsty" : "very hungry") :
(you.hunger <= 2600) ? (vamp ? "thirsty" : "hungry") :
(you.hunger < 7000) ? (vamp ? "not thirsty" : "not hungry") :
(you.hunger < 9000) ? "full" :
(you.hunger < 11000) ? "very full"
: (vamp ? "almost alive" : "completely stuffed"));
}
static std::string morgue_directory()
{
std::string dir = (!Options.morgue_dir.empty() ? Options.morgue_dir :
!SysEnv.crawl_dir.empty() ? SysEnv.crawl_dir
: "");
if (!dir.empty() && dir[dir.length() - 1] != FILE_SEPARATOR)
dir += FILE_SEPARATOR;
return (dir);
}
void dump_map(FILE *fp, bool debug, bool dist)
{
// Duplicate the screenshot() trick.
FixedVector<unsigned, NUM_DCHAR_TYPES> char_table_bk;
char_table_bk = Options.char_table;
init_char_table(CSET_ASCII);
init_show_table();
if (debug)
{
// Write the whole map out without checking for mappedness. Handy
// for debugging level-generation issues.
for (int y = 0; y < GYM; ++y)
{
for (int x = 0; x < GXM; ++x)
{
if (you.pos() == coord_def(x, y))
fputc('@', fp);
else if (grd[x][y] == DNGN_FLOOR_SPECIAL)
fputc('?', fp);
else if (dist && grd[x][y] == DNGN_FLOOR
&& travel_point_distance[x][y] > 0
&& travel_point_distance[x][y] < 10)
{
fputc('0' + travel_point_distance[x][y], fp);
}
else
fputc(get_feature_def(grd[x][y]).symbol, fp);
}
fputc('\n', fp);
}
}
else
{
int min_x = GXM-1, max_x = 0, min_y = GYM-1, max_y = 0;
for (int i = X_BOUND_1; i <= X_BOUND_2; i++)
for (int j = Y_BOUND_1; j <= Y_BOUND_2; j++)
if (env.map_knowledge[i][j].known())
{
if (i > max_x) max_x = i;
if (i < min_x) min_x = i;
if (j > max_y) max_y = j;
if (j < min_y) min_y = j;
}
for (int y = min_y; y <= max_y; ++y)
{
for (int x = min_x; x <= max_x; ++x)
fputc( env.map_knowledge[x][y].glyph(), fp );
fputc('\n', fp);
}
}
// Restore char and feature tables
Options.char_table = char_table_bk;
init_show_table();
}
void dump_map(const char* fname, bool debug, bool dist)
{
FILE* fp = fopen(fname, "w");
if (!fp)
return;
dump_map(fp, debug, dist);
fclose(fp);
}
static bool write_dump( const std::string &fname, dump_params &par)
{
bool succeeded = false;
std::string file_name = morgue_directory();
file_name += strip_filename_unsafe_chars(fname);
StashTrack.update_corpses();
std::string stash_file_name;
stash_file_name = file_name;
stash_file_name += ".lst";
StashTrack.dump(stash_file_name.c_str(), par.full_id);
std::string map_file_name = file_name + ".map";
dump_map(map_file_name.c_str());
file_name += ".txt";
FILE *handle = fopen(file_name.c_str(), "w");
#if DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "File name: %s", file_name.c_str());
#endif
if (handle != NULL)
{
fputs(par.text.c_str(), handle);
fclose(handle);
succeeded = true;
}
else
mprf(MSGCH_ERROR, "Error opening file '%s'", file_name.c_str());
return (succeeded);
}
void display_notes()
{
Menu scr;
scr.set_tag("notes");
scr.set_title(new MenuEntry("Turn | Place | Note"));
for (unsigned int i = 0; i < note_list.size(); ++i)
{
std::string prefix = note_list[i].describe(true, true, false);
std::string suffix = note_list[i].describe(false, false, true);
if (suffix.empty())
continue;
int spaceleft = get_number_of_cols() - prefix.length() - 1;
if (spaceleft <= 0)
return;
// Use smarter linebreak function.
// was: linebreak_string(suffix, spaceleft - 4, spaceleft);
linebreak_string2(suffix, spaceleft);
std::vector<std::string> parts = split_string("\n", suffix);
if (parts.empty()) // Disregard pure-whitespace notes.
continue;
scr.add_entry(new MenuEntry(prefix + parts[0]));
for (unsigned int j = 1; j < parts.size(); ++j)
{
scr.add_entry(new MenuEntry(std::string(prefix.length()-2, ' ') +
std::string("| ") + parts[j]));
}
}
scr.show();
redraw_screen();
}
#ifdef DGL_WHEREIS
///////////////////////////////////////////////////////////////////////////
// whereis player
void whereis_record(const char *status)
{
const std::string file_name =
morgue_directory()
+ strip_filename_unsafe_chars(you.your_name)
+ std::string(".where");
if (FILE *handle = fopen(file_name.c_str(), "w"))
{
fprintf(handle, "%s:status=%s\n",
xlog_status_line().c_str(),
status? status : "");
fclose(handle);
}
}
#endif