1 /*
2 * File: chardump.cc
3 * Summary: Dumps character info out to the morgue file.
4 * Written by: Linley Henzell
5 *
6 * Change History (most recent first):
7 *
8 *
9 * <4> 19 June 2000 GDL Changed handles to FILE *
10 * <3> 6/13/99 BWR Improved spell listing
11 * <2> 5/30/99 JDJ dump_spells dumps failure rates (from Brent).
12 * <1> 4/20/99 JDJ Reformatted, uses string objects, split out 7
13 * functions from dump_char, dumps artifact info.
14 */
15
16 #include "AppHdr.h"
17 #include "chardump.h"
18
19 #include <string>
20 #include <stdio.h>
21 #include <string.h>
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #if !(defined(__IBMCPP__) || defined(__BCPLUSPLUS__))
25 #include <unistd.h>
26 #endif
27 #include <ctype.h>
28
29 #ifdef USE_EMX
30 #include <sys/types.h>
31 #endif
32
33 #ifdef OS9
34 #include <stat.h>
35 #else
36 #include <sys/stat.h>
37 #endif
38
39 #ifdef DOS
40 #include <conio.h>
41 #endif
42
43 #include "externs.h"
44
45 #include "debug.h"
46 #include "describe.h"
47 #include "itemname.h"
48 #include "items.h"
49 #include "macro.h"
50 #include "mutation.h"
51 #include "player.h"
52 #include "religion.h"
53 #include "shopping.h"
54 #include "skills2.h"
55 #include "spl-book.h"
56 #include "spl-cast.h"
57 #include "spl-util.h"
58 #include "stuff.h"
59 #include "version.h"
60
61
62 // ========================================================================
63 // Internal Functions
64 // ========================================================================
65
66 // fillstring() is a hack to get around a missing constructor in
67 // Borland C++ implementation of the STD basic_string. Argh!!!
fillstring(size_t strlen,char filler)68 static std::string fillstring(size_t strlen, char filler)
69 {
70 std::string s;
71
72 for (size_t i=0; i<strlen; i++)
73 s += filler;
74
75 return s;
76 }
77
78 //---------------------------------------------------------------
79 //
80 // munge_description
81 //
82 // Convert dollar signs to EOL and word wrap to 80 characters.
83 // (for some obscure reason get_item_description uses dollar
84 // signs instead of EOL).
85 // - It uses $ signs because they're easier to manipulate than the EOL
86 // macro, which is of uncertain length (well, that and I didn't know how
87 // to do it any better at the time) (LH)
88 //---------------------------------------------------------------
munge_description(const std::string & inStr)89 static std::string munge_description(const std::string & inStr)
90 {
91 std::string outStr;
92
93 outStr.reserve(inStr.length() + 32);
94
95 const long kIndent = 3;
96 long lineLen = kIndent;
97
98 long i = 0;
99
100 outStr += fillstring(kIndent, ' ');
101
102 while (i < (long) inStr.length())
103 {
104 char ch = inStr[i];
105
106 if (ch == '$')
107 {
108 outStr += EOL;
109
110 outStr += fillstring(kIndent, ' ');
111 lineLen = kIndent;
112
113 while (inStr[++i] == '$')
114 ;
115 }
116 else if (isspace(ch))
117 {
118 if (lineLen >= 79)
119 {
120 outStr += EOL;
121 outStr += fillstring(kIndent, ' ');
122 lineLen = kIndent;
123
124 }
125 else if (lineLen > 0)
126 {
127 outStr += ch;
128 ++lineLen;
129 }
130 ++i;
131 }
132 else
133 {
134 std::string word;
135
136 while (i < (long) inStr.length()
137 && lineLen + (long) word.length() < 79
138 && !isspace(inStr[i]) && inStr[i] != '$')
139 {
140 word += inStr[i++];
141 }
142
143 if (lineLen + word.length() >= 79)
144 {
145 outStr += EOL;
146 outStr += fillstring(kIndent, ' ');
147 lineLen = kIndent;
148 }
149
150 outStr += word;
151 lineLen += word.length();
152 }
153 }
154
155 outStr += EOL;
156
157 return (outStr);
158 } // end munge_description()
159
160 //---------------------------------------------------------------
161 //
162 // dump_stats
163 //
164 //---------------------------------------------------------------
dump_stats(std::string & text)165 static void dump_stats( std::string & text )
166 {
167 char st_prn[20];
168
169 text += you.your_name;
170 text += " the ";
171
172 text += player_title();
173 text += " (";
174 text += species_name(you.species, you.experience_level);
175 text += ")";
176 text += EOL;
177
178 text += "(Level ";
179 itoa(you.experience_level, st_prn, 10);
180 text += st_prn;
181 text += " ";
182 text += you.class_name;
183 text += ")";
184 text += EOL EOL;
185
186 if (you.real_time != -1)
187 {
188 const time_t curr = you.real_time + (time(NULL) - you.start_time);
189 char buff[200];
190
191 make_time_string( curr, buff, sizeof(buff) );
192
193 text += "Play time: ";
194 text += buff;
195
196 text += " Number of turns: ";
197 itoa( you.num_turns, st_prn, 10 );
198 text += st_prn;
199 text += EOL EOL;
200 }
201
202 text += "Experience : ";
203 itoa(you.experience_level, st_prn, 10);
204 text += st_prn;
205 text += "/";
206 itoa(you.experience, st_prn, 10);
207 text += st_prn;
208 text += EOL;
209
210 text += "Strength ";
211 itoa(you.strength, st_prn, 10);
212 text += st_prn;
213 if (you.strength < you.max_strength)
214 {
215 text += "/";
216 itoa(you.max_strength, st_prn, 10);
217 text += st_prn;
218 }
219
220 text += " Dexterity ";
221 itoa(you.dex, st_prn, 10);
222 text += st_prn;
223 if (you.dex < you.max_dex)
224 {
225 text += "/";
226 itoa(you.max_dex, st_prn, 10);
227 text += st_prn;
228 }
229
230 text += " Intelligence ";
231 itoa(you.intel, st_prn, 10);
232 text += st_prn;
233 if (you.intel < you.max_intel)
234 {
235 text += "/";
236 itoa(you.max_intel, st_prn, 10);
237 text += st_prn;
238 }
239 text += EOL;
240
241 text += "Hit Points : ";
242 itoa(you.hp, st_prn, 10);
243 text += st_prn;
244
245 int max_max_hp = you.hp_max + player_rotted();
246
247 if (you.hp < you.hp_max || max_max_hp != you.hp_max)
248 {
249 text += "/";
250 itoa(you.hp_max, st_prn, 10);
251 text += st_prn;
252
253 if (max_max_hp != you.hp_max)
254 {
255 text += " (";
256 itoa(max_max_hp, st_prn, 10);
257 text += st_prn;
258 text += ")";
259 }
260
261 if (you.hp < 1)
262 {
263 text += " ";
264 text += ((!you.deaths_door) ? "(dead)" : "(almost dead)");
265 }
266 }
267
268 text += " Magic Points : ";
269 itoa(you.magic_points, st_prn, 10);
270 text += st_prn;
271 if (you.magic_points < you.max_magic_points)
272 {
273 text += "/";
274 itoa(you.max_magic_points, st_prn, 10);
275 text += st_prn;
276 }
277 text += EOL;
278
279 text += "AC : ";
280 itoa(player_AC(), st_prn, 10);
281 text += st_prn;
282
283 text += " Evasion : ";
284 itoa(player_evasion(), st_prn, 10);
285 text += st_prn;
286
287 text += " Shield : ";
288 itoa(player_shield_class(), st_prn, 10);
289 text += st_prn;
290 text += EOL;
291
292 text += "GP : ";
293 itoa( you.gold, st_prn, 10 );
294 text += st_prn;
295 text += EOL;
296 text += EOL;
297 } // end dump_stats()
298
299 //---------------------------------------------------------------
300 //
301 // dump_location
302 //
303 //---------------------------------------------------------------
dump_location(std::string & text)304 static void dump_location( std::string & text )
305 {
306 if (you.level_type != LEVEL_DUNGEON || you.your_level != -1)
307 text += "You are ";
308
309 if (you.level_type == LEVEL_PANDEMONIUM)
310 text += "in Pandemonium";
311 else if (you.level_type == LEVEL_ABYSS)
312 text += "in the Abyss";
313 else if (you.level_type == LEVEL_LABYRINTH)
314 text += "in a labyrinth";
315 else if (you.where_are_you == BRANCH_DIS)
316 text += "in Dis";
317 else if (you.where_are_you == BRANCH_GEHENNA)
318 text += "in Gehenna";
319 else if (you.where_are_you == BRANCH_VESTIBULE_OF_HELL)
320 text += "in the Vestibule of Hell";
321 else if (you.where_are_you == BRANCH_COCYTUS)
322 text += "in Cocytus";
323 else if (you.where_are_you == BRANCH_TARTARUS)
324 text += "in Tartarus";
325 else if (you.where_are_you == BRANCH_INFERNO)
326 text += "in the Inferno";
327 else if (you.where_are_you == BRANCH_THE_PIT)
328 text += "in the Pit";
329 else if (you.where_are_you == BRANCH_ORCISH_MINES)
330 text += "in the Mines";
331 else if (you.where_are_you == BRANCH_HIVE)
332 text += "in the Hive";
333 else if (you.where_are_you == BRANCH_LAIR)
334 text += "in the Lair";
335 else if (you.where_are_you == BRANCH_SLIME_PITS)
336 text += "in the Slime Pits";
337 else if (you.where_are_you == BRANCH_VAULTS)
338 text += "in the Vaults";
339 else if (you.where_are_you == BRANCH_CRYPT)
340 text += "in the Crypt";
341 else if (you.where_are_you == BRANCH_HALL_OF_BLADES)
342 text += "in the Hall of Blades";
343 else if (you.where_are_you == BRANCH_HALL_OF_ZOT)
344 text += "in the Hall of Zot";
345 else if (you.where_are_you == BRANCH_ECUMENICAL_TEMPLE)
346 text += "in the Ecumenical Temple";
347 else if (you.where_are_you == BRANCH_SNAKE_PIT)
348 text += "in the Snake Pit";
349 else if (you.where_are_you == BRANCH_ELVEN_HALLS)
350 text += "in the Elven Halls";
351 else if (you.where_are_you == BRANCH_TOMB)
352 text += "in the Tomb";
353 else if (you.where_are_you == BRANCH_SWAMP)
354 text += "in the Swamp";
355 else
356 {
357 if (you.your_level == -1)
358 text += "You escaped";
359 else
360 {
361 text += "on level ";
362
363 char st_prn[20];
364 itoa(you.your_level + 1, st_prn, 10);
365 text += st_prn;
366 }
367 }
368
369 text += ".";
370 text += EOL;
371 } // end dump_location()
372
373 //---------------------------------------------------------------
374 //
375 // dump_religion
376 //
377 //---------------------------------------------------------------
dump_religion(std::string & text)378 static void dump_religion( std::string & text )
379 {
380 if (you.religion != GOD_NO_GOD)
381 {
382 text += "You worship ";
383 text += god_name(you.religion);
384 text += ".";
385 text += EOL;
386
387 if (!player_under_penance())
388 {
389 if (you.religion != GOD_XOM)
390 { // Xom doesn't care
391 text += god_name(you.religion);
392 text += " is ";
393 text += ((you.piety <= 5) ? "displeased" :
394 (you.piety <= 20) ? "noncommittal" :
395 (you.piety <= 40) ? "pleased with you" :
396 (you.piety <= 70) ? "most pleased with you" :
397 (you.piety <= 100) ? "greatly pleased with you" :
398 (you.piety <= 130) ? "extremely pleased with you"
399 : "exalted by your worship");
400 text += ".";
401 text += EOL;
402 }
403 }
404 else
405 {
406 text += god_name(you.religion);
407 text += " is demanding penance.";
408 text += EOL;
409 }
410 }
411 } // end dump_religion()
412
413 //---------------------------------------------------------------
414 //
415 // dump_inventory
416 //
417 //---------------------------------------------------------------
dump_inventory(std::string & text,bool show_prices)418 static void dump_inventory( std::string & text, bool show_prices )
419 {
420 int i, j;
421 char temp_id[4][50];
422
423 std::string text2;
424
425 for (i = 0; i < 4; i++)
426 {
427 for (j = 0; j < 50; j++)
428 {
429 temp_id[i][j] = 1;
430 }
431 }
432
433 char st_pass[ ITEMNAME_SIZE ] = "";
434 int inv_class2[OBJ_GOLD];
435 int inv_count = 0;
436 char tmp_quant[20];
437
438 for (i = 0; i < OBJ_GOLD; i++)
439 {
440 inv_class2[i] = 0;
441 }
442
443 for (i = 0; i < ENDOFPACK; i++)
444 {
445 if (is_valid_item( you.inv[i] ))
446 {
447 // adds up number of each class in invent.
448 inv_class2[you.inv[i].base_type]++;
449 inv_count++;
450 }
451 }
452
453 if (!inv_count)
454 {
455 text += "You aren't carrying anything.";
456 text += EOL;
457 }
458 else
459 {
460 text += " Inventory:";
461 text += EOL;
462
463 for (i = 0; i < OBJ_GOLD; i++)
464 {
465 if (inv_class2[i] != 0)
466 {
467 switch (i)
468 {
469 case OBJ_WEAPONS: text += "Hand weapons"; break;
470 case OBJ_MISSILES: text += "Missiles"; break;
471 case OBJ_ARMOUR: text += "Armour"; break;
472 case OBJ_WANDS: text += "Magical devices"; break;
473 case OBJ_FOOD: text += "Comestibles"; break;
474 case OBJ_SCROLLS: text += "Scrolls"; break;
475 case OBJ_JEWELLERY: text += "Jewellery"; break;
476 case OBJ_POTIONS: text += "Potions"; break;
477 case OBJ_BOOKS: text += "Books"; break;
478 case OBJ_STAVES: text += "Magical staves"; break;
479 case OBJ_ORBS: text += "Orbs of Power"; break;
480 case OBJ_MISCELLANY: text += "Miscellaneous"; break;
481 case OBJ_CORPSES: text += "Carrion"; break;
482
483 default:
484 DEBUGSTR("Bad item class");
485 }
486 text += EOL;
487
488 for (j = 0; j < ENDOFPACK; j++)
489 {
490 if (is_valid_item(you.inv[j]) && you.inv[j].base_type == i)
491 {
492 text += " ";
493
494 in_name( j, DESC_INVENTORY_EQUIP, st_pass );
495 text += st_pass;
496
497 inv_count--;
498
499 if (show_prices)
500 {
501 text += " (";
502
503 itoa( item_value( you.inv[j], temp_id, true ),
504 tmp_quant, 10 );
505
506 text += tmp_quant;
507 text += " gold)";
508 }
509
510 if (is_dumpable_artifact( you.inv[j],
511 Options.verbose_dump ))
512 {
513 text2 = get_item_description( you.inv[j],
514 Options.verbose_dump,
515 true );
516
517 text += munge_description(text2);
518 }
519 else
520 {
521 text += EOL;
522 }
523 }
524 }
525 }
526 }
527 }
528 } // end dump_inventory()
529
530 //---------------------------------------------------------------
531 //
532 // dump_skills
533 //
534 //---------------------------------------------------------------
dump_skills(std::string & text)535 static void dump_skills( std::string & text )
536 {
537 char tmp_quant[20];
538
539 text += EOL;
540 text += EOL;
541 text += " Skills:";
542 text += EOL;
543
544 for (unsigned char i = 0; i < 50; i++)
545 {
546 if (you.skills[i] > 0)
547 {
548 text += ( (you.skills[i] == 27) ? " * " :
549 (you.practise_skill[i]) ? " + "
550 : " - " );
551
552 text += "Level ";
553 itoa( you.skills[i], tmp_quant, 10 );
554 text += tmp_quant;
555 text += " ";
556 text += skill_name(i);
557 text += EOL;
558 }
559 }
560
561 text += EOL;
562 text += EOL;
563 } // end dump_skills()
564
565 //---------------------------------------------------------------
566 //
567 // Return string of the i-th spell type, with slash if required
568 //
569 //---------------------------------------------------------------
spell_type_name(int spell_class,bool slash)570 static std::string spell_type_name(int spell_class, bool slash)
571 {
572 std::string ret;
573
574 if (slash)
575 ret = "/";
576
577 ret += spelltype_name(spell_class);
578
579 return (ret);
580 } // end spell_type_name()
581
582 //---------------------------------------------------------------
583 //
584 // dump_spells
585 //
586 //---------------------------------------------------------------
dump_spells(std::string & text)587 static void dump_spells( std::string & text )
588 {
589 char tmp_quant[20];
590
591 // This array helps output the spell types in the traditional order.
592 // this can be tossed as soon as I reorder the enum to the traditional order {dlb}
593 const int spell_type_index[] = {
594 SPTYP_HOLY,
595 SPTYP_POISON,
596 SPTYP_FIRE,
597 SPTYP_ICE,
598 SPTYP_EARTH,
599 SPTYP_AIR,
600 SPTYP_CONJURATION,
601 SPTYP_ENCHANTMENT,
602 SPTYP_DIVINATION,
603 SPTYP_TRANSLOCATION,
604 SPTYP_SUMMONING,
605 SPTYP_TRANSMIGRATION,
606 SPTYP_NECROMANCY,
607 0
608 };
609
610 int spell_levels = player_spell_levels();
611
612 if (spell_levels == 1)
613 text += "You have one spell level left.";
614 else if (spell_levels == 0)
615 text += "You cannot memorise any spells.";
616 else
617 {
618 text += "You have ";
619 itoa( spell_levels, tmp_quant, 10 );
620 text += tmp_quant;
621 text += " spell levels left.";
622 }
623
624 text += EOL;
625
626 if (!you.spell_no)
627 {
628 text += "You don't know any spells.";
629 text += EOL;
630
631 }
632 else
633 {
634 text += "You know the following spells:" EOL;
635 text += EOL;
636
637 text += " Your Spells Type Success Level" EOL;
638
639 for (int j = 0; j < 52; j++)
640 {
641 const char letter = index_to_letter( j );
642 const int spell = get_spell_by_letter( letter );
643
644 if (spell != SPELL_NO_SPELL)
645 {
646 std::string spell_line = " ";
647
648 char strng[2];
649 strng[0] = letter;
650 strng[1] = '\0';
651
652 spell_line += strng;
653 spell_line += " - ";
654 spell_line += spell_title( spell );
655
656 for (int i = spell_line.length(); i < 34; i++)
657 {
658 spell_line += ' ';
659 }
660
661 bool already = false;
662
663 for (int i = 0; spell_type_index[i] != 0; i++)
664 {
665 if (spell_typematch( spell, spell_type_index[i] ))
666 {
667 spell_line +=
668 spell_type_name(spell_type_index[i], already);
669 already = true;
670 }
671 }
672
673 for (int i = spell_line.length(); i < 58; i++)
674 {
675 spell_line += ' ';
676 }
677
678 int fail_rate = spell_fail( spell );
679
680 spell_line += (fail_rate == 100) ? "Useless" :
681 (fail_rate > 90) ? "Terrible" :
682 (fail_rate > 80) ? "Cruddy" :
683 (fail_rate > 70) ? "Bad" :
684 (fail_rate > 60) ? "Very Poor" :
685 (fail_rate > 50) ? "Poor" :
686 (fail_rate > 40) ? "Fair" :
687 (fail_rate > 30) ? "Good" :
688 (fail_rate > 20) ? "Very Good" :
689 (fail_rate > 10) ? "Great" :
690 (fail_rate > 0) ? "Excellent"
691 : "Perfect";
692
693 for (int i = spell_line.length(); i < 70; i++)
694 spell_line += ' ';
695
696 itoa((int) spell_difficulty( spell ), tmp_quant, 10 );
697 spell_line += tmp_quant;
698 spell_line += EOL;
699
700 text += spell_line;
701 }
702 }
703 }
704 } // end dump_spells()
705
706 //---------------------------------------------------------------
707 //
708 // dump_mutations
709 //
710 //---------------------------------------------------------------
dump_mutations(std::string & text)711 static void dump_mutations( std::string & text )
712 {
713 // Can't use how_mutated() here, as it doesn't count demonic powers
714 int xz = 0;
715
716 for (int xy = 0; xy < 100; xy++)
717 {
718 if (you.mutation[xy] > 0)
719 xz++;
720 }
721
722 if (xz > 0)
723 {
724 text += "";
725 text += EOL;
726 text += " Mutations & Other Weirdness";
727 text += EOL;
728
729 for (int j = 0; j < 100; j++)
730 {
731 if (you.mutation[j])
732 {
733 if (you.demon_pow[j] > 0)
734 text += "* ";
735
736 text += mutation_name(j);
737 text += EOL;
738 }
739 }
740 }
741 } // end dump_mutations()
742
743 #if MAC
744 #pragma mark -
745 #endif
746
747 // ========================================================================
748 // Public Functions
749 // ========================================================================
750
751 //---------------------------------------------------------------
752 //
753 // dump_char
754 //
755 // Creates a disk record of a character. Returns true if the
756 // character was successfully saved.
757 //
758 //---------------------------------------------------------------
dump_char(const char fname[30],bool show_prices)759 bool dump_char( const char fname[30], bool show_prices ) // $$$ a try block?
760 {
761 bool succeeded = false;
762
763 std::string text;
764
765 // start with enough room for 100 80 character lines
766 text.reserve(100 * 80);
767
768 text += " Dungeon Crawl version " VERSION " character file.";
769 text += EOL;
770 text += EOL;
771
772 dump_stats(text);
773 dump_location(text);
774 dump_religion(text);
775
776 switch (you.burden_state)
777 {
778 case BS_OVERLOADED:
779 text += "You are overloaded with stuff.";
780 text += EOL;
781 break;
782 case BS_ENCUMBERED:
783 text += "You are encumbered.";
784 text += EOL;
785 break;
786 }
787
788 text += "You are ";
789
790 text += ((you.hunger <= 1000) ? "starving" :
791 (you.hunger <= 2600) ? "hungry" :
792 (you.hunger < 7000) ? "not hungry" :
793 (you.hunger < 11000) ? "full" : "completely stuffed");
794
795 text += ".";
796 text += EOL;
797 text += EOL;
798
799 if (you.attribute[ATTR_TRANSFORMATION])
800 {
801 switch (you.attribute[ATTR_TRANSFORMATION])
802 {
803 case TRAN_SPIDER:
804 text += "You are in spider-form.";
805 break;
806 case TRAN_BLADE_HANDS:
807 text += "Your hands are blades.";
808 break;
809 case TRAN_STATUE:
810 text += "You are a stone statue.";
811 break;
812 case TRAN_ICE_BEAST:
813 text += "You are a creature of crystalline ice.";
814 break;
815 case TRAN_DRAGON:
816 text += "You are a fearsome dragon!";
817 break;
818 case TRAN_LICH:
819 text += "You are in lich-form.";
820 break;
821 case TRAN_SERPENT_OF_HELL:
822 text += "You are a huge, demonic serpent!";
823 break;
824 case TRAN_AIR:
825 text += "You are a cloud of diffuse gas.";
826 break;
827 }
828
829 text += EOL;
830 text += EOL;
831 }
832
833 dump_inventory(text, show_prices);
834
835 char tmp_quant[20];
836
837 text += EOL;
838 text += EOL;
839 text += " You have ";
840 itoa( you.exp_available, tmp_quant, 10 );
841 text += tmp_quant;
842 text += " experience left.";
843
844 dump_skills(text);
845 dump_spells(text);
846 dump_mutations(text);
847
848 char file_name[kPathLen] = "\0";
849
850 if (SysEnv.crawl_dir)
851 strncpy(file_name, SysEnv.crawl_dir, kPathLen);
852
853 strncat(file_name, fname, kPathLen);
854
855 if (strcmp(fname, "morgue.txt") != 0)
856 strncat(file_name, ".txt", kPathLen);
857
858 FILE *handle = fopen(file_name, "wb");
859
860 #if DEBUG_DIAGNOSTICS
861 strcpy( info, "File name: " );
862 strcat( info, file_name );
863 mpr( info, MSGCH_DIAGNOSTICS );
864 #endif
865
866 if (handle != NULL)
867 {
868 size_t begin = 0;
869 size_t end = text.find(EOL);
870
871 while (end != std::string::npos)
872 {
873 end += strlen(EOL);
874
875 size_t len = end - begin;
876
877 if (len > 80)
878 len = 80;
879
880 fwrite(text.c_str() + begin, len, 1, handle);
881
882 begin = end;
883 end = text.find(EOL, end);
884 }
885
886 fclose(handle);
887 succeeded = true;
888 }
889 else
890 mpr("Error opening file.");
891
892 return (succeeded);
893 } // end dump_char()
894