3 * Summary: Dumps character info out to the morgue file.
4 * Written by: Linley Henzell
17 #if !defined(__IBMCPP__) && !defined(TARGET_COMPILER_VC)
28 #include "dgn-overview.h"
31 #include "godprayer.h"
56 #include "transform.h"
64 static void _sdump_header(dump_params
&);
65 static void _sdump_stats(dump_params
&);
66 static void _sdump_location(dump_params
&);
67 static void _sdump_religion(dump_params
&);
68 static void _sdump_burden(dump_params
&);
69 static void _sdump_hunger(dump_params
&);
70 static void _sdump_transform(dump_params
&);
71 static void _sdump_visits(dump_params
&);
72 static void _sdump_gold(dump_params
&);
73 static void _sdump_misc(dump_params
&);
74 static void _sdump_turns_by_place(dump_params
&);
75 static void _sdump_notes(dump_params
&);
76 static void _sdump_inventory(dump_params
&);
77 static void _sdump_skills(dump_params
&);
78 static void _sdump_spells(dump_params
&);
79 static void _sdump_mutations(dump_params
&);
80 static void _sdump_messages(dump_params
&);
81 static void _sdump_screenshot(dump_params
&);
82 static void _sdump_kills_by_place(dump_params
&);
83 static void _sdump_kills(dump_params
&);
84 static void _sdump_newline(dump_params
&);
85 static void _sdump_overview(dump_params
&);
86 static void _sdump_hiscore(dump_params
&);
87 static void _sdump_monster_list(dump_params
&);
88 static void _sdump_vault_list(dump_params
&);
89 static void _sdump_separator(dump_params
&);
91 static void _sdump_lua(dump_params
&);
93 static bool write_dump(const std::string
&fname
, dump_params
&);
95 struct dump_section_handler
98 void (*handler
)(dump_params
&);
107 const scorefile_entry
*se
;
109 dump_params(std::string
&_text
, const std::string
&sec
= "",
110 bool prices
= false, bool id
= false,
111 const scorefile_entry
*s
= NULL
)
112 : text(_text
), section(sec
), show_prices(prices
), full_id(id
),
118 static dump_section_handler dump_handlers
[] = {
119 { "header", _sdump_header
},
120 { "stats", _sdump_stats
},
121 { "location", _sdump_location
},
122 { "religion", _sdump_religion
},
123 { "burden", _sdump_burden
},
124 { "hunger", _sdump_hunger
},
125 { "transform", _sdump_transform
},
126 { "visits", _sdump_visits
},
127 { "gold", _sdump_gold
},
128 { "misc", _sdump_misc
},
129 { "turns_by_place", _sdump_turns_by_place
},
130 { "notes", _sdump_notes
},
131 { "inventory", _sdump_inventory
},
132 { "skills", _sdump_skills
},
133 { "spells", _sdump_spells
},
134 { "mutations", _sdump_mutations
},
135 { "messages", _sdump_messages
},
136 { "screenshot", _sdump_screenshot
},
137 { "kills_by_place", _sdump_kills_by_place
},
138 { "kills", _sdump_kills
},
139 { "overview", _sdump_overview
},
140 { "hiscore", _sdump_hiscore
},
141 { "monlist", _sdump_monster_list
},
142 { "vaults", _sdump_vault_list
},
144 // Conveniences for the .crawlrc artist.
145 { "", _sdump_newline
},
146 { "-", _sdump_separator
},
155 static void dump_section(dump_params
&par
)
157 for (int i
= 0; ; ++i
)
159 if (!dump_handlers
[i
].name
|| par
.section
== dump_handlers
[i
].name
)
161 if (dump_handlers
[i
].handler
)
162 (*dump_handlers
[i
].handler
)(par
);
168 bool dump_char(const std::string
&fname
, bool show_prices
, bool full_id
,
169 const scorefile_entry
*se
)
171 // Start with enough room for 100 80 character lines.
173 text
.reserve(100 * 80);
175 dump_params
par(text
, "", show_prices
, full_id
, se
);
177 for (int i
= 0, size
= Options
.dump_order
.size(); i
< size
; ++i
)
179 par
.section
= Options
.dump_order
[i
];
183 return write_dump(fname
, par
);
186 static void _sdump_header(dump_params
&par
)
188 std::string type
= crawl_state
.game_type_name();
194 par
.text
+= " " + type
+ " version " + Version::Long();
195 par
.text
+= " character file.\n\n";
198 static void _sdump_stats(dump_params
&par
)
200 par
.text
+= dump_overview_screen(par
.full_id
);
204 static void _sdump_burden(dump_params
&par
)
206 std::string verb
= par
.se
? "were" : "are";
208 switch (you
.burden_state
)
211 par
.text
+= "You " + verb
+ " overloaded with stuff.\n";
214 par
.text
+= "You " + verb
+ " encumbered.\n";
221 static void _sdump_hunger(dump_params
&par
)
224 par
.text
+= "You were ";
226 par
.text
+= "You are ";
228 par
.text
+= hunger_level();
232 static void _sdump_transform(dump_params
&par
)
234 std::string
&text(par
.text
);
237 std::string verb
= par
.se
? "were" : "are";
242 text
+= "You " + verb
+ " in spider-form.";
245 text
+= "You " + verb
+ " in ";
246 if (you
.species
== SP_VAMPIRE
)
250 case TRAN_BLADE_HANDS
:
251 text
+= "Your " + blade_parts() + " " + verb
+ " blades.";
254 text
+= "You " + verb
+ " a stone statue.";
257 text
+= "You " + verb
+ " a creature of crystalline ice.";
260 text
+= "You " + verb
+ " a fearsome dragon!";
263 text
+= "You " + verb
+ " in lich-form.";
266 text
+= "You " + verb
+ " a filthy swine.";
276 static void _sdump_visits(dump_params
&par
)
278 std::string
&text(par
.text
);
280 std::string have
= "have ";
281 std::string seen
= "seen";
282 if (par
.se
) // you died -> past tense
288 std::vector
<PlaceInfo
> branches_visited
=
289 you
.get_all_place_info(true, true);
291 PlaceInfo branches_total
;
292 for (unsigned int i
= 0; i
< branches_visited
.size(); i
++)
293 branches_total
+= branches_visited
[i
];
295 text
+= make_stringf("You %svisited %d branch",
296 have
.c_str(), branches_visited
.size());
297 if (branches_visited
.size() != 1)
299 text
+= make_stringf(" of the dungeon, and %s %d of its levels.\n",
300 seen
.c_str(), branches_total
.levels_seen
);
302 PlaceInfo place_info
= you
.get_place_info(LEVEL_PANDEMONIUM
);
303 if (place_info
.num_visits
> 0)
305 text
+= make_stringf("You %svisited Pandemonium %d time",
306 have
.c_str(), place_info
.num_visits
);
307 if (place_info
.num_visits
> 1)
309 text
+= make_stringf(", and %s %d of its levels.\n",
310 seen
.c_str(), place_info
.levels_seen
);
313 place_info
= you
.get_place_info(LEVEL_ABYSS
);
314 if (place_info
.num_visits
> 0)
316 text
+= make_stringf("You %svisited the Abyss %d time",
317 have
.c_str(), place_info
.num_visits
);
318 if (place_info
.num_visits
> 1)
323 place_info
= you
.get_place_info(LEVEL_LABYRINTH
);
324 if (place_info
.num_visits
> 0)
326 text
+= make_stringf("You %svisited %d Labyrinth",
327 have
.c_str(), place_info
.num_visits
);
328 if (place_info
.num_visits
> 1)
333 place_info
= you
.get_place_info(LEVEL_PORTAL_VAULT
);
334 if (place_info
.num_visits
> 0)
336 CrawlVector
&vaults
=
337 you
.props
[YOU_PORTAL_VAULT_NAMES_KEY
].get_vector();
342 std::vector
<std::string
> misc_portals
;
344 for (unsigned int i
= 0; i
< vaults
.size(); i
++)
346 std::string name
= vaults
[i
].get_string();
348 if (name
.find("Ziggurat") != std::string::npos
)
352 if (name
== "Ziggurat:1")
355 else if (name
== "bazaar")
358 misc_portals
.push_back(name
);
363 text
+= make_stringf("You %svisited %d bazaar",
364 have
.c_str(), num_bazaars
);
373 text
+= make_stringf("You %svisited %d Ziggurat",
374 have
.c_str(), num_zigs
);
377 text
+= make_stringf(", and %s %d of %s levels.\n",
378 seen
.c_str(), zig_levels
,
379 num_zigs
> 1 ? "their" : "its");
382 if (!misc_portals
.empty())
384 text
+= make_stringf("You %svisited %d portal chamber",
385 have
.c_str(), misc_portals
.size());
386 if (misc_portals
.size() > 1)
389 text
+= comma_separated_line(misc_portals
.begin(),
399 static void _sdump_gold(dump_params
&par
)
401 std::string
&text(par
.text
);
405 const char* have
= "have ";
406 if (par
.se
) // you died -> past tense
409 if (you
.attribute
[ATTR_GOLD_FOUND
] > 0)
412 text
+= make_stringf("You %scollected %d gold pieces.\n", have
,
413 you
.attribute
[ATTR_GOLD_FOUND
]);
416 if (you
.attribute
[ATTR_PURCHASES
] > 0)
419 text
+= make_stringf("You %sspent %d gold pieces at shops.\n", have
,
420 you
.attribute
[ATTR_PURCHASES
]);
423 if (you
.attribute
[ATTR_DONATIONS
] > 0)
426 text
+= make_stringf("You %sdonated %d gold pieces.\n", have
,
427 you
.attribute
[ATTR_DONATIONS
]);
430 if (you
.attribute
[ATTR_MISC_SPENDING
] > 0)
433 text
+= make_stringf("You %sused %d gold pieces for miscellaneous "
435 you
.attribute
[ATTR_MISC_SPENDING
]);
442 static void _sdump_misc(dump_params
&par
)
444 _sdump_location(par
);
445 _sdump_religion(par
);
448 _sdump_transform(par
);
453 #define TO_PERCENT(x, y) (100.0f * (static_cast<float>(x)) / (static_cast<float>(y)))
455 static std::string
_sdump_turns_place_info(PlaceInfo place_info
,
456 std::string name
= "")
458 PlaceInfo gi
= you
.global_info
;
462 name
= place_info
.short_name();
464 float a
, b
, c
, d
, e
, f
;
465 unsigned int non_interlevel
=
466 place_info
.turns_total
- place_info
.turns_interlevel
;
467 unsigned int global_non_interlevel
=
468 gi
.turns_total
- gi
.turns_interlevel
;
471 a
= TO_PERCENT(place_info
.turns_total
, gi
.turns_total
);
472 b
= TO_PERCENT(non_interlevel
, global_non_interlevel
);
473 c
= TO_PERCENT(place_info
.turns_interlevel
, place_info
.turns_total
);
474 d
= TO_PERCENT(place_info
.turns_resting
, non_interlevel
);
475 e
= TO_PERCENT(place_info
.turns_explore
, non_interlevel
);
476 f
= static_cast<float>(non_interlevel
) /
477 static_cast<float>(place_info
.levels_seen
);
480 make_stringf("%14s | %5.1f | %5.1f | %5.1f | %5.1f | %5.1f | %13.1f\n",
481 name
.c_str(), a
, b
, c
, d
, e
, f
);
483 out
= replace_all(out
, " nan ", " N/A ");
488 static void _sdump_turns_by_place(dump_params
&par
)
490 std::string
&text(par
.text
);
492 std::vector
<PlaceInfo
> all_visited
=
493 you
.get_all_place_info(true);
497 " A = Turns spent in this place as a percentage of turns spent in the\n"
499 " B = Non-inter-level travel turns spent in this place as a percentage of\n"
500 " non-inter-level travel turns spent in the entire game.\n"
501 " C = Inter-level travel turns spent in this place as a percentage of\n"
502 " turns spent in this place.\n"
503 " D = Turns resting spent in this place as a percentage of non-inter-level\n"
504 " travel turns spent in this place.\n"
505 " E = Turns spent auto-exploring this place as a percentage of\n"
506 " non-inter-level travel turns spent in this place.\n"
507 " F = Non-inter-level travel turns spent in this place divided by the\n"
508 " number of levels of this place that you've seen.\n\n";
511 text
+= " A B C D E F\n";
513 text
+= "+-------+-------+-------+-------+-------+----------------------\n";
515 text
+= _sdump_turns_place_info(you
.global_info
, "Total");
517 for (unsigned int i
= 0; i
< all_visited
.size(); i
++)
519 PlaceInfo pi
= all_visited
[i
];
520 text
+= _sdump_turns_place_info(pi
);
524 text
+= "+-------+-------+-------+-------+-------+----------------------\n";
529 static void _sdump_newline(dump_params
&par
)
534 static void _sdump_separator(dump_params
&par
)
536 par
.text
+= std::string(79, '-') + "\n";
540 // Assume this is an arbitrary Lua function name, call the function and
541 // dump whatever it returns.
542 static void _sdump_lua(dump_params
&par
)
545 if (!clua
.callfn(par
.section
.c_str(), ">s", &luatext
)
546 && !clua
.error
.empty())
548 par
.text
+= "Lua dump error: " + clua
.error
+ "\n";
555 //---------------------------------------------------------------
559 // word wrap to 80 characters.
560 // XXX: should be replaced by some other linewrapping function
561 // now EOL munging is gone
562 //---------------------------------------------------------------
563 std::string
munge_description(const std::string
& inStr
)
567 outStr
.reserve(inStr
.length() + 32);
569 const int kIndent
= 3;
570 int lineLen
= kIndent
;
574 outStr
+= std::string(kIndent
, ' ');
576 while (i
< inStr
.length())
578 const char ch
= inStr
[i
];
584 outStr
+= std::string(kIndent
, ' ');
587 while (inStr
[++i
] == '\n')
590 else if (isspace(ch
))
595 outStr
+= std::string(kIndent
, ' ');
599 else if (lineLen
> 0)
610 while (i
< inStr
.length()
611 && lineLen
+ word
.length() < 79
612 && !isspace(inStr
[i
]) && inStr
[i
] != '\n')
617 if (lineLen
+ word
.length() >= 79)
620 outStr
+= std::string(kIndent
, ' ');
625 lineLen
+= word
.length();
632 } // end munge_description()
634 static void _sdump_messages(dump_params
&par
)
636 // A little message history:
637 if (Options
.dump_message_count
> 0)
639 par
.text
+= "Message History\n\n";
640 par
.text
+= get_last_messages(Options
.dump_message_count
);
644 static void _sdump_screenshot(dump_params
&par
)
646 par
.text
+= screenshot();
650 static void _sdump_notes(dump_params
&par
)
652 std::string
&text(par
.text
);
653 if (note_list
.empty())
656 text
+= "\nNotes\nTurn | Place | Note\n";
657 text
+= "--------------------------------------------------------------\n";
658 for (unsigned i
= 0; i
< note_list
.size(); ++i
)
660 text
+= note_list
[i
].describe();
666 //---------------------------------------------------------------
670 //---------------------------------------------------------------
671 static void _sdump_location(dump_params
&par
)
673 if (you
.absdepth0
== -1
674 && you
.where_are_you
== BRANCH_MAIN_DUNGEON
675 && you
.level_type
== LEVEL_DUNGEON
)
677 par
.text
+= "You escaped";
680 par
.text
+= "You were " + prep_branch_level_name();
682 par
.text
+= "You are " + prep_branch_level_name();
686 } // end dump_location()
688 static void _sdump_religion(dump_params
&par
)
690 std::string
&text(par
.text
);
691 if (you
.religion
!= GOD_NO_GOD
)
694 text
+= "You worshipped ";
696 text
+= "You worship ";
697 text
+= god_name(you
.religion
);
700 if (you
.religion
!= GOD_XOM
)
702 if (!player_under_penance())
704 text
+= god_prayer_reaction();
709 std::string verb
= par
.se
? "was" : "is";
711 text
+= god_name(you
.religion
);
712 text
+= " " + verb
+ " demanding penance.\n";
721 text
+= describe_xom_favour(false);
727 static bool _dump_item_origin(const item_def
&item
, int value
)
729 #define fs(x) (flags & (x))
730 const int flags
= Options
.dump_item_origins
;
731 if (flags
== IODS_EVERYTHING
)
734 if (fs(IODS_ARTEFACTS
)
735 && is_artefact(item
) && item_ident(item
, ISFLAG_KNOW_PROPERTIES
))
739 if (fs(IODS_EGO_ARMOUR
) && item
.base_type
== OBJ_ARMOUR
740 && item_type_known(item
))
742 const int spec_ench
= get_armour_ego_type(item
);
743 return (spec_ench
!= SPARM_NORMAL
);
746 if (fs(IODS_EGO_WEAPON
) && item
.base_type
== OBJ_WEAPONS
747 && item_type_known(item
))
749 return (get_weapon_brand(item
) != SPWPN_NORMAL
);
752 if (fs(IODS_JEWELLERY
) && item
.base_type
== OBJ_JEWELLERY
)
755 if (fs(IODS_RUNES
) && item
.base_type
== OBJ_MISCELLANY
756 && item
.sub_type
== MISC_RUNE_OF_ZOT
)
761 if (fs(IODS_RODS
) && item
.base_type
== OBJ_STAVES
762 && item_is_rod(item
))
767 if (fs(IODS_STAVES
) && item
.base_type
== OBJ_STAVES
768 && !item_is_rod(item
))
773 if (fs(IODS_BOOKS
) && item
.base_type
== OBJ_BOOKS
)
776 const int refpr
= Options
.dump_item_origin_price
;
780 value
= item_value(item
, false);
781 return (value
>= refpr
);
785 //---------------------------------------------------------------
789 //---------------------------------------------------------------
790 static void _sdump_inventory(dump_params
&par
)
794 std::string
&text(par
.text
);
797 int inv_class2
[OBJ_GOLD
];
801 for (i
= 0; i
< OBJ_GOLD
; i
++)
804 for (i
= 0; i
< ENDOFPACK
; i
++)
806 if (you
.inv
[i
].defined())
808 // adds up number of each class in invent.
809 inv_class2
[you
.inv
[i
].base_type
]++;
816 text
+= "You aren't carrying anything.";
821 text
+= "Inventory:\n\n";
823 for (i
= 0; i
< OBJ_GOLD
; i
++)
825 if (inv_class2
[i
] != 0)
829 case OBJ_WEAPONS
: text
+= "Hand weapons"; break;
830 case OBJ_MISSILES
: text
+= "Missiles"; break;
831 case OBJ_ARMOUR
: text
+= "Armour"; break;
832 case OBJ_WANDS
: text
+= "Magical devices"; break;
833 case OBJ_FOOD
: text
+= "Comestibles"; break;
834 case OBJ_SCROLLS
: text
+= "Scrolls"; break;
835 case OBJ_JEWELLERY
: text
+= "Jewellery"; break;
836 case OBJ_POTIONS
: text
+= "Potions"; break;
837 case OBJ_BOOKS
: text
+= "Books"; break;
838 case OBJ_STAVES
: text
+= "Magical staves"; break;
839 case OBJ_ORBS
: text
+= "Orbs of Power"; break;
840 case OBJ_MISCELLANY
: text
+= "Miscellaneous"; break;
841 case OBJ_CORPSES
: text
+= "Carrion"; break;
844 die("Bad item class");
848 for (j
= 0; j
< ENDOFPACK
; j
++)
850 if (you
.inv
[j
].defined() && you
.inv
[j
].base_type
== i
)
853 text
+= you
.inv
[j
].name(DESC_INVENTORY_EQUIP
);
862 itoa(ival
= item_value(you
.inv
[j
], true),
869 if (origin_describable(you
.inv
[j
])
870 && _dump_item_origin(you
.inv
[j
], ival
))
872 text
+= "\n" " (" + origin_desc(you
.inv
[j
]) + ")";
875 if (is_dumpable_artefact(you
.inv
[j
], false)
876 || Options
.dump_book_spells
877 && you
.inv
[j
].base_type
== OBJ_BOOKS
)
879 text2
= get_item_description(you
.inv
[j
],
883 text
+= munge_description(text2
);
897 //---------------------------------------------------------------
901 //---------------------------------------------------------------
902 static void _sdump_skills(dump_params
&par
)
904 std::string
&text(par
.text
);
910 text
+= " You have ";
912 itoa(you
.exp_available
, tmp_quant
, 10);
914 text
+= " experience left.";
926 //---------------------------------------------------------------
928 // Return string of the i-th spell type, with slash if required
930 //---------------------------------------------------------------
931 static std::string
spell_type_shortname(int spell_class
, bool slash
)
938 ret
+= spelltype_short_name(spell_class
);
941 } // end spell_type_shortname()
943 //---------------------------------------------------------------
947 //---------------------------------------------------------------
948 static void _sdump_spells(dump_params
&par
)
950 std::string
&text(par
.text
);
953 // This array helps output the spell types in the traditional order.
954 // this can be tossed as soon as I reorder the enum to the traditional order {dlb}
955 const int spell_type_index
[] =
974 int spell_levels
= player_spell_levels();
976 std::string verb
= par
.se
? "had" : "have";
978 if (spell_levels
== 1)
979 text
+= "You " + verb
+ " one spell level left.";
980 else if (spell_levels
== 0)
982 verb
= par
.se
? "couldn't" : "cannot";
984 text
+= "You " + verb
+ " memorise any spells.";
992 itoa(spell_levels
, tmp_quant
, 10);
994 text
+= " spell levels left.";
1001 verb
= par
.se
? "didn't" : "don't";
1003 text
+= "You " + verb
+ " know any spells.\n\n";
1007 verb
= par
.se
? "knew" : "know";
1009 text
+= "You " + verb
+ " the following spells:\n\n";
1011 text
+= " Your Spells Type Power Success Level Hunger" "\n";
1013 for (int j
= 0; j
< 52; j
++)
1015 const char letter
= index_to_letter(j
);
1016 const spell_type spell
= get_spell_by_letter(letter
);
1018 if (spell
!= SPELL_NO_SPELL
)
1020 std::string spell_line
;
1022 spell_line
+= letter
;
1023 spell_line
+= " - ";
1024 spell_line
+= spell_title(spell
);
1026 if (spell_line
.length() > 24)
1027 spell_line
= spell_line
.substr(0, 24);
1029 for (int i
= spell_line
.length(); i
< 26; i
++)
1032 bool already
= false;
1034 for (int i
= 0; spell_type_index
[i
] != 0; i
++)
1036 if (spell_typematch(spell
, spell_type_index
[i
]))
1038 spell_line
+= spell_type_shortname(spell_type_index
[i
],
1044 for (int i
= spell_line
.length(); i
< 41; ++i
)
1047 spell_line
+= spell_power_string(spell
);
1049 for (int i
= spell_line
.length(); i
< 54; ++i
)
1052 spell_line
+= failure_rate_to_string(spell_fail(spell
));
1054 for (int i
= spell_line
.length(); i
< 66; i
++)
1057 itoa(spell_difficulty(spell
), tmp_quant
, 10);
1058 spell_line
+= tmp_quant
;
1060 for (int i
= spell_line
.length(); i
< 71; i
++)
1063 spell_line
+= spell_hunger_string(spell
);
1071 } // end dump_spells()
1074 static void _sdump_kills(dump_params
&par
)
1076 par
.text
+= you
.kills
->kill_info();
1079 static std::string
_sdump_kills_place_info(PlaceInfo place_info
,
1080 std::string name
= "")
1085 name
= place_info
.short_name();
1087 unsigned int global_total_kills
= 0;
1088 for (int i
= 0; i
< KC_NCATEGORIES
; i
++)
1089 global_total_kills
+= you
.global_info
.mon_kill_num
[i
];
1091 unsigned int total_kills
= 0;
1092 for (int i
= 0; i
< KC_NCATEGORIES
; i
++)
1093 total_kills
+= place_info
.mon_kill_num
[i
];
1095 // Skip places where nothing was killed.
1096 if (total_kills
== 0)
1099 float a
, b
, c
, d
, e
, f
, g
;
1101 a
= TO_PERCENT(total_kills
, global_total_kills
);
1102 b
= TO_PERCENT(place_info
.mon_kill_num
[KC_YOU
],
1103 you
.global_info
.mon_kill_num
[KC_YOU
]);
1104 c
= TO_PERCENT(place_info
.mon_kill_num
[KC_FRIENDLY
],
1105 you
.global_info
.mon_kill_num
[KC_FRIENDLY
]);
1106 d
= TO_PERCENT(place_info
.mon_kill_num
[KC_OTHER
],
1107 you
.global_info
.mon_kill_num
[KC_OTHER
]);
1108 e
= TO_PERCENT(place_info
.mon_kill_exp
,
1109 you
.global_info
.mon_kill_exp
);
1110 f
= TO_PERCENT(place_info
.mon_kill_exp_avail
,
1111 you
.global_info
.mon_kill_exp_avail
);
1113 g
= std::max
<float>(place_info
.mon_kill_exp
, place_info
.mon_kill_exp_avail
)
1114 / place_info
.levels_seen
;
1117 make_stringf("%14s | %5.1f | %5.1f | %5.1f | %5.1f | %5.1f |"
1118 " %5.1f | %13.1f\n",
1119 name
.c_str(), a
, b
, c
, d
, e
, f
, g
);
1121 out
= replace_all(out
, " nan ", " N/A ");
1126 static void _sdump_kills_by_place(dump_params
&par
)
1128 std::string
&text(par
.text
);
1130 std::vector
<PlaceInfo
> all_visited
=
1131 you
.get_all_place_info(true);
1133 std::string result
= "";
1135 std::string header
=
1137 " A = Kills in this place as a percentage of kills in entire the game.\n"
1138 " B = Kills by you in this place as a percentage of kills by you in\n"
1139 " the entire game.\n"
1140 " C = Kills by friends in this place as a percentage of kills by\n"
1141 " friends in the entire game.\n"
1142 " D = Other kills in this place as a percentage of other kills in the\n"
1144 " E = Character level experience gained in this place as a percentage of\n"
1145 " character level experience gained in the entire game.\n"
1146 " F = Skills experience gained in this place as a percentage of skills\n"
1147 " experience gained in the entire game.\n"
1148 " G = Experience gained in this place divided by the number of levels of\n"
1149 " this place that you have seen.\n\n";
1152 header
+= " A B C D E F G\n";
1154 header
+= "+-------+-------+-------+-------+-------+-------+--------------\n";
1156 std::string footer
= " ";
1157 footer
+= "+-------+-------+-------+-------+-------+-------+--------------\n";
1159 result
+= _sdump_kills_place_info(you
.global_info
, "Total");
1161 for (unsigned int i
= 0; i
< all_visited
.size(); i
++)
1163 PlaceInfo pi
= all_visited
[i
];
1164 result
+= _sdump_kills_place_info(pi
);
1167 if (result
.length() > 0)
1168 text
+= header
+ result
+ footer
+ "\n";
1171 static void _sdump_overview(dump_params
&par
)
1173 std::string overview
=
1174 formatted_string::parse_string(overview_description_string(false));
1175 trim_string(overview
);
1176 par
.text
+= overview
;
1180 static void _sdump_hiscore(dump_params
&par
)
1185 std::string hiscore
= hiscores_format_single_long(*(par
.se
), true);
1186 trim_string(hiscore
);
1187 par
.text
+= hiscore
;
1191 static void _sdump_monster_list(dump_params
&par
)
1193 std::string monlist
= mpr_monster_list(par
.se
);
1194 trim_string(monlist
);
1195 par
.text
+= monlist
;
1199 static void _sdump_vault_list(dump_params
&par
)
1201 if (par
.full_id
|| par
.se
1207 par
.text
+= "Vault maps used:\n\n";
1208 par
.text
+= dump_vault_maps();
1212 static void _sdump_mutations(dump_params
&par
)
1214 std::string
&text(par
.text
);
1216 if (how_mutated(true, false))
1219 text
+= describe_mutations();
1222 } // end dump_mutations()
1224 // ========================================================================
1226 // ========================================================================
1228 const char *hunger_level(void)
1230 const bool vamp
= (you
.species
== SP_VAMPIRE
);
1232 return ((you
.hunger
<= 1000) ? (vamp
? "bloodless" : "starving") :
1233 (you
.hunger
<= 1533) ? (vamp
? "near bloodless" : "near starving") :
1234 (you
.hunger
<= 2066) ? (vamp
? "very thirsty" : "very hungry") :
1235 (you
.hunger
<= 2600) ? (vamp
? "thirsty" : "hungry") :
1236 (you
.hunger
< 7000) ? (vamp
? "not thirsty" : "not hungry") :
1237 (you
.hunger
< 9000) ? "full" :
1238 (you
.hunger
< 11000) ? "very full"
1239 : (vamp
? "almost alive" : "completely stuffed"));
1242 static std::string
morgue_directory()
1244 std::string dir
= (!Options
.morgue_dir
.empty() ? Options
.morgue_dir
:
1245 !SysEnv
.crawl_dir
.empty() ? SysEnv
.crawl_dir
1248 if (!dir
.empty() && dir
[dir
.length() - 1] != FILE_SEPARATOR
)
1249 dir
+= FILE_SEPARATOR
;
1254 void dump_map(FILE *fp
, bool debug
, bool dist
)
1256 // Duplicate the screenshot() trick.
1257 FixedVector
<unsigned, NUM_DCHAR_TYPES
> char_table_bk
;
1258 char_table_bk
= Options
.char_table
;
1260 init_char_table(CSET_ASCII
);
1265 // Write the whole map out without checking for mappedness. Handy
1266 // for debugging level-generation issues.
1267 for (int y
= 0; y
< GYM
; ++y
)
1269 for (int x
= 0; x
< GXM
; ++x
)
1271 if (you
.pos() == coord_def(x
, y
))
1273 else if (testbits(env
.pgrid
[x
][y
], FPROP_HIGHLIGHT
))
1275 else if (dist
&& grd
[x
][y
] == DNGN_FLOOR
1276 && travel_point_distance
[x
][y
] > 0
1277 && travel_point_distance
[x
][y
] < 10)
1279 fputc('0' + travel_point_distance
[x
][y
], fp
);
1282 fputc(get_feature_def(grd
[x
][y
]).symbol
, fp
);
1289 int min_x
= GXM
-1, max_x
= 0, min_y
= GYM
-1, max_y
= 0;
1291 for (int i
= X_BOUND_1
; i
<= X_BOUND_2
; i
++)
1292 for (int j
= Y_BOUND_1
; j
<= Y_BOUND_2
; j
++)
1293 if (env
.map_knowledge
[i
][j
].known())
1295 if (i
> max_x
) max_x
= i
;
1296 if (i
< min_x
) min_x
= i
;
1297 if (j
> max_y
) max_y
= j
;
1298 if (j
< min_y
) min_y
= j
;
1301 for (int y
= min_y
; y
<= max_y
; ++y
)
1303 for (int x
= min_x
; x
<= max_x
; ++x
)
1304 fputc(get_cell_glyph(coord_def(x
, y
)).ch
, fp
);
1310 // Restore char and feature tables
1311 Options
.char_table
= char_table_bk
;
1315 void dump_map(const char* fname
, bool debug
, bool dist
)
1317 FILE* fp
= fopen_replace(fname
);
1321 dump_map(fp
, debug
, dist
);
1326 static bool write_dump(const std::string
&fname
, dump_params
&par
)
1328 bool succeeded
= false;
1330 std::string file_name
= morgue_directory();
1332 file_name
+= strip_filename_unsafe_chars(fname
);
1334 StashTrack
.update_corpses();
1336 std::string stash_file_name
;
1337 stash_file_name
= file_name
;
1338 stash_file_name
+= ".lst";
1339 StashTrack
.dump(stash_file_name
.c_str(), par
.full_id
);
1341 std::string map_file_name
= file_name
+ ".map";
1342 dump_map(map_file_name
.c_str());
1344 file_name
+= ".txt";
1345 FILE *handle
= fopen_replace(file_name
.c_str());
1347 #ifdef DEBUG_DIAGNOSTICS
1348 mprf(MSGCH_DIAGNOSTICS
, "File name: %s", file_name
.c_str());
1353 fputs(par
.text
.c_str(), handle
);
1356 mprf("Char dumped to '%s'.", file_name
.c_str());
1359 mprf(MSGCH_ERROR
, "Error opening file '%s'", file_name
.c_str());
1364 void display_notes()
1366 formatted_scroller scr
;
1367 scr
.set_flags(MF_START_AT_END
);
1368 scr
.set_tag("notes");
1369 scr
.set_highlighter(new MenuHighlighter
);
1370 scr
.set_title(new MenuEntry("Turn | Place | Note"));
1371 for (unsigned int i
= 0; i
< note_list
.size(); ++i
)
1373 std::string prefix
= note_list
[i
].describe(true, true, false);
1374 std::string suffix
= note_list
[i
].describe(false, false, true);
1378 int spaceleft
= get_number_of_cols() - prefix
.length() - 1;
1382 // Use smarter linebreak function.
1383 // was: linebreak_string(suffix, spaceleft - 4, spaceleft);
1384 linebreak_string2(suffix
, spaceleft
);
1385 std::vector
<std::string
> parts
= split_string("\n", suffix
);
1386 if (parts
.empty()) // Disregard pure-whitespace notes.
1389 scr
.add_entry(new MenuEntry(prefix
+ parts
[0]));
1390 for (unsigned int j
= 1; j
< parts
.size(); ++j
)
1392 scr
.add_entry(new MenuEntry(std::string(prefix
.length()-2, ' ') +
1393 std::string("| ") + parts
[j
]));
1401 ///////////////////////////////////////////////////////////////////////////
1403 void whereis_record(const char *status
)
1405 const std::string file_name
=
1407 + strip_filename_unsafe_chars(you
.your_name
)
1408 + std::string(".where");
1410 if (FILE *handle
= fopen_replace(file_name
.c_str()))
1412 fprintf(handle
, "%s:status=%s\n",
1413 xlog_status_line().c_str(),
1414 status
? status
: "");
1421 ///////////////////////////////////////////////////////////////////////////////
1424 // For DGL installs, write a timestamp at regular intervals into a file in
1425 // the morgue directory. The timestamp file is named
1426 // "timestamp-<player>-<starttime>.ts". All timestamps are standard Unix
1427 // time_t, but currently only the low 4 bytes are saved even on systems
1428 // with 64-bit time_t.
1430 // Timestamp files are append only, and Crawl will check and handle cases
1431 // where a previous Crawl process crashed at a higher turn count on the same
1434 // Having timestamps associated with the game allows for much easier seeking
1435 // within Crawl ttyrecs by external tools such as FooTV.
1437 #ifdef DGL_TURN_TIMESTAMPS
1439 #include <sys/stat.h>
1441 // File-format version for timestamp files. Crawl will never append to a
1442 const uint32_t DGL_TIMESTAMP_VERSION
= 1;
1443 const int VERSION_SIZE
= sizeof(DGL_TIMESTAMP_VERSION
);
1444 const int TIMESTAMP_SIZE
= sizeof(uint32_t);
1446 // Returns the size of the opened file with the give FILE* handle.
1447 unsigned long _file_size(FILE *handle
)
1450 const int err
= fstat(fileno(handle
), &fs
);
1451 return err
? 0 : fs
.st_size
;
1454 // Returns the name of the timestamp file based on the morgue_dir,
1455 // character name and the game start time.
1456 std::string
dgl_timestamp_filename()
1458 const std::string filename
=
1459 ("timestamp-" + you
.your_name
+ "-" + make_file_time(you
.birth_time
));
1460 return morgue_directory() + strip_filename_unsafe_chars(filename
) + ".ts";
1463 // Returns true if the given file exists and is not a timestamp file
1464 // of a known version.
1465 bool dgl_unknown_timestamp_file(const std::string
&filename
)
1467 if (FILE *inh
= fopen(filename
.c_str(), "rb"))
1470 const uint32_t file_version
= unmarshallInt(r
);
1472 return (file_version
!= DGL_TIMESTAMP_VERSION
);
1477 // Returns a filehandle to use to write turn timestamps, NULL if
1478 // timestamps should not be written.
1479 FILE *dgl_timestamp_filehandle()
1481 static FILE *timestamp_file
;
1482 static bool opened_file
= false;
1487 const std::string filename
= dgl_timestamp_filename();
1488 // First check if there's already a timestamp file. If it exists
1489 // but has a different version, we cannot safely modify it, so bail.
1490 if (!dgl_unknown_timestamp_file(filename
))
1491 timestamp_file
= fopen(filename
.c_str(), "ab");
1493 return timestamp_file
;
1496 // Records a timestamp in the .ts file at the given offset. If no timestamp
1497 // file exists, a new file will be created.
1498 void dgl_record_timestamp(unsigned long file_offset
, time_t time
)
1500 static bool timestamp_first_write
= true;
1501 if (FILE *ftimestamp
= dgl_timestamp_filehandle())
1503 writer
w(dgl_timestamp_filename(), ftimestamp
, true);
1504 if (timestamp_first_write
)
1506 unsigned long ts_size
= _file_size(ftimestamp
);
1509 marshallInt(w
, DGL_TIMESTAMP_VERSION
);
1510 ts_size
+= sizeof(DGL_TIMESTAMP_VERSION
);
1513 // It's possible that the file we want to write is already
1514 // larger than the offset we expect if the game previously
1515 // crashed. When the game crashes, turn count is
1516 // effectively rewound to the point of the last save. In
1517 // such cases, we should not add timestamps until we reach
1518 // the correct turn count again.
1519 if (ts_size
&& ts_size
> file_offset
)
1522 if (file_offset
> ts_size
)
1525 (file_offset
- ts_size
) / TIMESTAMP_SIZE
;
1526 for (int i
= 0; i
< backlog
; ++i
)
1530 timestamp_first_write
= false;
1532 fseek(ftimestamp
, 0, SEEK_END
);
1533 // [ds] FIXME: Eventually switch to 8 byte timestamps.
1534 marshallInt(w
, static_cast<uint32_t>(time
));
1539 // Record timestamps every so many turns:
1540 const int TIMESTAMP_TURN_INTERVAL
= 100;
1541 // Stop recording timestamps after this turncount.
1542 const long TIMESTAMP_TURN_MAX
= 500000L;
1543 void dgl_record_timestamp(long turn
)
1545 if (turn
&& turn
< TIMESTAMP_TURN_MAX
&& !(turn
% TIMESTAMP_TURN_INTERVAL
))
1547 const time_t now
= time(NULL
);
1548 const unsigned long offset
=
1550 (turn
/ TIMESTAMP_TURN_INTERVAL
- 1) * TIMESTAMP_SIZE
);
1551 dgl_record_timestamp(offset
, now
);
1557 // Records a timestamp for the current player turn if appropriate.
1558 void record_turn_timestamp()
1560 #ifdef DGL_TURN_TIMESTAMPS
1561 if (crawl_state
.need_save
)
1562 dgl_record_timestamp(you
.num_turns
);