-
Notifications
You must be signed in to change notification settings - Fork 635
Expand file tree
/
Copy pathinsight.c
More file actions
3506 lines (3250 loc) · 133 KB
/
Copy pathinsight.c
File metadata and controls
3506 lines (3250 loc) · 133 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* NetHack 5.0 insight.c $NHDT-Date: 1781973051 2026/06/20 16:30:51 $ $NHDT-Branch: NetHack-5.0 $:$NHDT-Revision: 1.139 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
/*
* Enlightenment and Conduct+Achievements and Vanquished+Extinct+Geno'd
* and stethoscope/probing feedback.
*
* Most code used to reside in cmd.c, presumably because ^X was originally
* a wizard mode command and the majority of those are in that file.
* Some came from end.c where it is used during end of game disclosure.
* And some came from priest.c that had once been in pline.c.
*/
#include "hack.h"
staticfn void enlght_out(const char *);
staticfn void enlght_line(const char *, const char *, const char *,
const char *);
staticfn char *enlght_combatinc(const char *, int, int, char *);
staticfn void enlght_halfdmg(int, int);
staticfn boolean walking_on_water(void);
staticfn boolean cause_known(int);
staticfn char *attrval(int, int, char *);
staticfn char *fmt_elapsed_time(char *, int);
staticfn char *N_times(long, char *) NONNULL NONNULLARG2;
staticfn void background_enlightenment(int, int);
staticfn void basics_enlightenment(int, int);
staticfn void characteristics_enlightenment(int, int);
staticfn void one_characteristic(int, int, int);
staticfn void status_enlightenment(int, int);
staticfn void weapon_insight(int);
staticfn void attributes_enlightenment(int, int);
staticfn void show_achievements(int);
staticfn int QSORTCALLBACK vanqsort_cmp(const genericptr, const genericptr);
staticfn int num_extinct(void);
staticfn int num_gone(int, int *);
staticfn char *size_str(int);
staticfn void item_resistance_message(int, const char *, int);
extern const char *const hu_stat[]; /* hunger status from eat.c */
extern const char *const enc_stat[]; /* encumbrance status from botl.c */
static const char You_[] = "You ", are[] = "are ", were[] = "were ",
have[] = "have ", had[] = "had ", can[] = "can ",
could[] = "could ";
static const char have_been[] = "have been ", have_never[] = "have never ",
never[] = "never ";
/* for livelogging: */
struct ll_achieve_msg {
long llflag;
const char *msg;
};
/* ordered per 'enum achievements' in you.h */
/* take care to keep them in sync! */
static struct ll_achieve_msg achieve_msg [] = {
{ 0, "" }, /* actual achievements are numbered from 1 */
{ LL_ACHIEVE, "acquired the Bell of Opening" },
{ LL_ACHIEVE, "entered Gehennom" },
{ LL_ACHIEVE, "acquired the Candelabrum of Invocation" },
{ LL_ACHIEVE, "acquired the Book of the Dead" },
{ LL_ACHIEVE, "performed the invocation" },
{ LL_ACHIEVE, "acquired The Amulet of Yendor" },
{ LL_ACHIEVE, "entered the Elemental Planes" },
{ LL_ACHIEVE, "entered the Astral Plane" },
{ LL_ACHIEVE, "ascended" },
/* if the type of item isn't discovered yet, disclosing the event
via #chronicle would be a spoiler (particularly for gray stone);
the ID'd name for the type of item will be appended to the next
two messages, for display via livelog and/or dumplog */
{ LL_ACHIEVE | LL_SPOILER, "acquired the Mines' End" }, /* " luckstone" */
{ LL_ACHIEVE | LL_SPOILER, "acquired the Sokoban" }, /* " <item>" */
{ LL_ACHIEVE | LL_UMONST, "killed Medusa" },
/* these two are not logged */
{ 0, "hero was always blond, no, blind" },
{ 0, "hero never wore armor" },
/* */
{ LL_MINORAC | LL_DUMP, "entered the Gnomish Mines" },
{ LL_ACHIEVE, "reached Mine Town" }, /* probably minor, but dnh logs it */
{ LL_MINORAC, "entered a shop" },
{ LL_MINORAC, "entered a temple" },
{ LL_ACHIEVE, "consulted the Oracle" }, /* minor, but rare enough */
{ LL_MINORAC | LL_DUMP, "read a Discworld novel" }, /* even more so */
{ LL_ACHIEVE, "entered Sokoban" }, /* keep as major for turn comparison
* with completed sokoban */
{ LL_ACHIEVE, "entered the Bigroom" },
/* The following 8 are for advancing through the ranks
and messages differ by role so are created on the fly;
rank 0 (Xp 1 and 2) isn't an achievement */
{ LL_MINORAC | LL_DUMP, "" }, /* Xp 3 */
{ LL_MINORAC | LL_DUMP, "" }, /* Xp 6 */
{ LL_MINORAC | LL_DUMP, "" }, /* Xp 10 */
{ LL_ACHIEVE, "" }, /* Xp 14, so able to attempt the quest */
{ LL_ACHIEVE, "" }, /* Xp 18 */
{ LL_ACHIEVE, "" }, /* Xp 22 */
{ LL_ACHIEVE, "" }, /* Xp 26 */
{ LL_ACHIEVE, "" }, /* Xp 30 */
{ LL_MINORAC, "learned castle drawbridge's tune" }, /* achievement #31 */
{ 0, "" } /* keep this one at the end */
};
/* macros to simplify output of enlightenment messages; also used by
conduct and achievements */
#define enl_msg(prefix, present, past, suffix, ps) \
enlght_line((prefix), final ? (past) : (present), (suffix), (ps))
#define you_are(attr, ps) enl_msg(You_, are, were, (attr), (ps))
#define you_have(attr, ps) enl_msg(You_, have, had, (attr), (ps))
#define you_can(attr, ps) enl_msg(You_, can, could, (attr), (ps))
#define you_have_been(goodthing) \
enl_msg(You_, have_been, were, (goodthing), "")
#define you_have_never(badthing) \
enl_msg(You_, have_never, never, (badthing), "")
#define you_have_X(something) \
enl_msg(You_, have, (const char *) "", (something), "")
staticfn void
enlght_out(const char *buf)
{
if (ge.en_via_menu) {
add_menu_str(ge.en_win, buf);
} else
putstr(ge.en_win, 0, buf);
}
staticfn void
enlght_line(
const char *start,
const char *middle,
const char *end,
const char *ps)
{
#ifndef NO_ENLGHT_CONTRACTIONS
static const struct contrctn {
const char *twowords, *contrctn;
} contra[] = {
{ " are not ", " aren't " },
{ " were not ", " weren't " },
{ " have not ", " haven't " },
{ " had not ", " hadn't " },
{ " can not ", " can't " },
{ " could not ", " couldn't " },
};
int i;
#endif
char buf[BUFSZ];
Sprintf(buf, " %s%s%s%s.", start, middle, end, ps);
#ifndef NO_ENLGHT_CONTRACTIONS
if (strstri(buf, " not ")) { /* TODO: switch to libc strstr() */
for (i = 0; i < SIZE(contra); ++i)
(void) strsubst(buf, contra[i].twowords, contra[i].contrctn);
}
#endif
enlght_out(buf);
}
/* format increased chance to hit or damage or defense (Protection) */
staticfn char *
enlght_combatinc(
const char *inctyp, /* "to hit" or "damage" or "defense" */
int incamt, /* amount of increment (negative if decrement) */
int final, /* ENL_{GAMEINPROGRESS,GAMEOVERALIVE,GAMEOVERDEAD} */
char *outbuf)
{
const char *modif, *bonus;
boolean invrt;
int absamt;
absamt = abs(incamt);
/* Protection amount is typically larger than damage or to-hit;
reduce magnitude by a third in order to stretch modifier ranges
(small:1..5, moderate:6..10, large:11..19, huge:20+) */
if (!strcmp(inctyp, "defense"))
absamt = (absamt * 2) / 3;
if (absamt <= 3)
modif = "small";
else if (absamt <= 6)
modif = "moderate";
else if (absamt <= 12)
modif = "large";
else
modif = "huge";
modif = !incamt ? "no" : an(modif); /* ("no" case shouldn't happen) */
bonus = (incamt >= 0) ? "bonus" : "penalty";
/* "bonus <foo>" (to hit) vs "<bar> bonus" (damage, defense) */
invrt = strcmp(inctyp, "to hit") ? TRUE : FALSE;
Sprintf(outbuf, "%s %s %s", modif, invrt ? inctyp : bonus,
invrt ? bonus : inctyp);
if (final || wizard)
Sprintf(eos(outbuf), " (%s%d)", (incamt > 0) ? "+" : "", incamt);
return outbuf;
}
/* report half physical or half spell damage */
staticfn void
enlght_halfdmg(int category, int final)
{
const char *category_name;
char buf[BUFSZ];
switch (category) {
case HALF_PHDAM:
category_name = "physical";
break;
case HALF_SPDAM:
category_name = "spell";
break;
default:
category_name = "unknown";
break;
}
Sprintf(buf, " %s %s damage", (final || wizard) ? "half" : "reduced",
category_name);
enl_msg(You_, "take", "took", buf, from_what(category));
}
/* is hero actively using water walking capability on water (or lava)? */
staticfn boolean
walking_on_water(void)
{
if (u.uinwater || Levitation || Flying)
return FALSE;
return (boolean) (Wwalking && is_pool_or_lava(u.ux, u.uy));
}
/* describe u.utraptype; used by status_enlightenment() and self_lookat() */
char *
trap_predicament(char *outbuf, int final, boolean wizxtra)
{
struct trap *t;
/* caller has verified u.utrap */
*outbuf = '\0';
switch (u.utraptype) {
case TT_BURIEDBALL:
Strcpy(outbuf, "tethered to something buried");
break;
case TT_LAVA:
Sprintf(outbuf, "sinking into %s", final ? "lava" : hliquid("lava"));
break;
case TT_INFLOOR:
Sprintf(outbuf, "stuck in %s", the(surface(u.ux, u.uy)));
break;
default: /* TT_BEARTRAP, TT_PIT, or TT_WEB */
Strcpy(outbuf, "trapped");
if ((t = t_at(u.ux, u.uy)) != 0) /* should never be null */
Sprintf(eos(outbuf), " in %s", an(trapname(t->ttyp, FALSE)));
break;
}
if (wizxtra) { /* give extra information for wizard mode enlightenment */
/* curly braces: u.utrap is an escape attempt counter rather than a
turn timer so use different ornamentation than usual parentheses */
Sprintf(eos(outbuf), " {%u}", u.utrap);
}
return outbuf;
}
/* check whether hero is wearing something that player definitely knows
confers the target property; item must have been seen and its type
discovered but it doesn't necessarily have to be fully identified */
staticfn boolean
cause_known(
int propindx) /* index of a property which can be conveyed by worn item */
{
struct obj *o;
long mask = W_ARMOR | W_AMUL | W_RING | W_TOOL;
/* simpler than from_what()/what_gives(); we don't attempt to
handle artifacts and we deliberately ignore wielded items */
for (o = gi.invent; o; o = o->nobj) {
if (!(o->owornmask & mask))
continue;
if ((int) objects[o->otyp].oc_oprop == propindx
&& objects[o->otyp].oc_name_known && o->dknown)
return TRUE;
}
return FALSE;
}
/* format a characteristic value, accommodating Strength's strangeness */
staticfn char *
attrval(
int attrindx,
int attrvalue,
char resultbuf[]) /* should be at least [7] to hold "18/100\0" */
{
if (attrindx != A_STR || attrvalue <= 18)
Sprintf(resultbuf, "%d", attrvalue);
else if (attrvalue > STR18(100)) /* 19 to 25 */
Sprintf(resultbuf, "%d", attrvalue - 100);
else /* simplify "18/\**" to be "18/100" */
Sprintf(resultbuf, "18/%02d", attrvalue - 18);
return resultbuf;
}
/* format urealtime.realtime as
" D days, H hours, M minutes and S seconds"
with any fields having a value of 0 omitted:
0-00:00:20 => " 20 seconds"
0-00:15:05 => " 15 minutes and 5 seconds"
0-00:16:00 => " 16 minutes"
0-01:15:10 => " 1 hour, 15 minutes and 10 seconds"
0-02:00:01 => " 2 hours and 1 second"
3-00:25:40 => " 3 days, 25 minutes and 40 seconds"
(note: for a list of more than two entries, nethack usually includes the
[style-wise] optional comma before "and" but in this instance it does not)
*/
staticfn char *
fmt_elapsed_time(char *outbuf, int final)
{
int fieldcnt;
long edays, ehours, eminutes, eseconds;
/* for a game that's over, reallydone() has updated urealtime.realtime
to its final value before calling us during end of game disclosure;
for a game that's still in progress, it holds the amount of elapsed
game time from previous sessions up through most recent save/restore
(or up through latest level change when 'checkpoint' is On);
'.start_timing' has a non-zero value even if '.realtime' is 0 */
long etim = urealtime.realtime;
if (!final)
etim += timet_delta(getnow(), urealtime.start_timing);
/* we could use localtime() to convert the value into a 'struct tm'
to get date and time fields but this is simple and straightforward */
eseconds = etim % 60L, etim /= 60L;
eminutes = etim % 60L, etim /= 60L;
ehours = etim % 24L;
edays = etim / 24L;
fieldcnt = !!edays + !!ehours + !!eminutes + !!eseconds;
Strcpy(outbuf, fieldcnt ? "" : " none"); /* 'none' should never happen */
if (edays) {
Sprintf(eos(outbuf), " %ld day%s", edays, plur(edays));
if (fieldcnt > 1) /* hours and/or minutes and/or seconds to follow */
Strcat(outbuf, (fieldcnt == 2) ? " and" : ",");
--fieldcnt; /* edays has been processed */
}
if (ehours) {
Sprintf(eos(outbuf), " %ld hour%s", ehours, plur(ehours));
if (fieldcnt > 1) /* minutes and/or seconds to follow */
Strcat(outbuf, (fieldcnt == 2) ? " and" : ",");
--fieldcnt; /* ehours has been processed */
}
if (eminutes) {
Sprintf(eos(outbuf), " %ld minute%s", eminutes, plur(eminutes));
if (fieldcnt > 1) /* seconds to follow */
Strcat(outbuf, " and");
/* eminutes has been processed but no need to decrement fieldcnt */
}
if (eseconds)
Sprintf(eos(outbuf), " %ld second%s", eseconds, plur(eseconds));
return outbuf;
}
/* "once" vs "twice" vs "17 times", used in several places */
staticfn char *
N_times(long n, char *outbuf)
{
switch (n) {
case 0:
default:
Sprintf(outbuf, "%ld times", n);
break;
case 1:
Strcpy(outbuf, "once");
break;
case 2:
Strcpy(outbuf, "twice");
break;
case 3:
Strcpy(outbuf, "thrice");
break;
}
return outbuf;
}
void
enlightenment(
int mode, /* BASICENLIGHTENMENT | MAGICENLIGHTENMENT (| both) */
int final) /* ENL_GAMEINPROGRESS:0, ENL_GAMEOVERALIVE, ENL_GAMEOVERDEAD */
{
char buf[BUFSZ], tmpbuf[BUFSZ];
ge.en_win = create_nhwindow(NHW_MENU);
ge.en_via_menu = !final;
if (ge.en_via_menu)
start_menu(ge.en_win, MENU_BEHAVE_STANDARD);
Strcpy(tmpbuf, svp.plname);
*tmpbuf = highc(*tmpbuf); /* same adjustment as bottom line */
/* as in background_enlightenment, when poly'd we need to use the saved
gender in u.mfemale rather than the current you-as-monster gender */
Snprintf(buf, sizeof(buf), "%s the %s's attributes:", tmpbuf,
((Upolyd ? u.mfemale : flags.female) && gu.urole.name.f)
? gu.urole.name.f
: gu.urole.name.m);
/* title */
enlght_out(buf); /* "Conan the Archeologist's attributes:" */
/* background and characteristics; ^X or end-of-game disclosure */
if (mode & BASICENLIGHTENMENT) {
/* role, race, alignment, deities, dungeon level, time, experience */
background_enlightenment(mode, final);
/* hit points, energy points, armor class, gold */
basics_enlightenment(mode, final);
/* strength, dexterity, &c */
characteristics_enlightenment(mode, final);
}
/* expanded status line information, including things which aren't
included there due to space considerations;
shown for both basic and magic enlightenment */
status_enlightenment(mode, final);
/* remaining attributes; shown for potion,&c or wizard mode and
explore mode ^X or end of game disclosure */
if (mode & MAGICENLIGHTENMENT) {
/* intrinsics and other traditional enlightenment feedback */
attributes_enlightenment(mode, final);
}
enlght_out(""); /* separator */
enlght_out("Miscellaneous:");
/* reminder to player and/or information for dumplog */
if ((mode & BASICENLIGHTENMENT) != 0 && (wizard || discover || final)) {
if (wizard || discover) {
Sprintf(buf, "running in %s mode", wizard ? "debug" : "explore");
you_are(buf, "");
}
if (!flags.bones) {
/* mention not saving bones iff hero just died */
Sprintf(buf, "disabled loading%s of bones levels",
(final == ENL_GAMEOVERDEAD) ? " and storing" : "");
you_have_X(buf);
} else if (!u.uroleplay.numbones) {
enl_msg(You_, "haven't encountered", "didn't encounter",
" any bones levels", "");
} else {
Sprintf(buf, "encountered %ld bones level%s",
u.uroleplay.numbones, plur(u.uroleplay.numbones));
you_have_X(buf);
}
}
(void) fmt_elapsed_time(buf, final);
enl_msg("Total elapsed playing time ", "is", "was", buf, "");
if (!ge.en_via_menu) {
display_nhwindow(ge.en_win, TRUE);
} else {
menu_item *selected = 0;
end_menu(ge.en_win, (char *) 0);
if (select_menu(ge.en_win, PICK_NONE, &selected) > 0)
free((genericptr_t) selected);
ge.en_via_menu = FALSE;
}
destroy_nhwindow(ge.en_win);
ge.en_win = WIN_ERR;
}
/*ARGSUSED*/
/* display role, race, alignment and such to en_win */
staticfn void
background_enlightenment(int unused_mode UNUSED, int final)
{
const char *role_titl, *rank_titl;
int innategend, difgend, difalgn;
char buf[BUFSZ], tmpbuf[BUFSZ];
/* note that if poly'd, we need to use u.mfemale instead of flags.female
to access hero's saved gender-as-human/elf/&c rather than current */
innategend = (Upolyd ? u.mfemale : flags.female) ? 1 : 0;
role_titl = (innategend && gu.urole.name.f) ? gu.urole.name.f
: gu.urole.name.m;
rank_titl = rank_of(u.ulevel, Role_switch, innategend);
enlght_out(""); /* separator after title */
enlght_out("Background:");
/* if polymorphed, report current shape before underlying role;
will be repeated as first status: "you are transformed" and also
among various attributes: "you are in beast form" (after being
told about lycanthropy) or "you are polymorphed into <a foo>"
(with countdown timer appended for wizard mode); we really want
the player to know he's not a samurai at the moment... */
if (Upolyd) {
char anbuf[20]; /* includes trailing space; [4] suffices */
struct permonst *uasmon = gy.youmonst.data;
boolean altphrasing = vampshifted(&gy.youmonst);
tmpbuf[0] = '\0';
/* here we always use current gender, not saved role gender */
if (!is_male(uasmon) && !is_female(uasmon) && !is_neuter(uasmon))
Sprintf(tmpbuf, "%s ", genders[flags.female ? 1 : 0].adj);
if (altphrasing)
Sprintf(eos(tmpbuf), "%s in ",
pmname(&mons[gy.youmonst.cham],
flags.female ? FEMALE : MALE));
Snprintf(buf, sizeof(buf), "%s%s%s%s form",
!final ? "currently " : "",
altphrasing ? just_an(anbuf, tmpbuf) : "in ",
tmpbuf, pmname(uasmon, flags.female ? FEMALE : MALE));
you_are(buf, "");
}
/* report role; omit gender if it's redundant (eg, "female priestess") */
tmpbuf[0] = '\0';
if (!gu.urole.name.f
&& ((gu.urole.allow & ROLE_GENDMASK) == (ROLE_MALE | ROLE_FEMALE)
|| innategend != flags.initgend))
Sprintf(tmpbuf, "%s ", genders[innategend].adj);
buf[0] = '\0';
if (Upolyd)
Strcpy(buf, "actually "); /* "You are actually a ..." */
if (!strcmpi(rank_titl, role_titl)) {
/* omit role when rank title matches it */
Sprintf(eos(buf), "%s, level %d %s%s", an(rank_titl), u.ulevel,
tmpbuf, gu.urace.noun);
} else {
Sprintf(eos(buf), "%s, a level %d %s%s %s", an(rank_titl), u.ulevel,
tmpbuf, gu.urace.adj, role_titl);
}
you_are(buf, "");
/* report alignment (bypass you_are() in order to omit ending period);
adverb is used to distinguish between temporary change (helm of opp.
alignment), permanent change (one-time conversion), and original */
Sprintf(buf, " %s%s%s, %son a mission for %s",
You_, !final ? are : were,
align_str(u.ualign.type),
/* helm of opposite alignment (might hide conversion) */
(u.ualign.type != u.ualignbase[A_CURRENT])
/* what's the past tense of "currently"? if we used "formerly"
it would sound like a reference to the original alignment */
? (!final ? "currently " : "temporarily ")
/* permanent conversion */
: (u.ualign.type != u.ualignbase[A_ORIGINAL])
/* and what's the past tense of "now"? certainly not "then"
in a context like this...; "belatedly" == weren't that
way sooner (in other words, didn't start that way) */
? (!final ? "now " : "belatedly ")
/* atheist (ignored in very early game) */
: (!u.uconduct.gnostic && svm.moves > 1000L)
? "nominally "
/* lastly, normal case */
: "",
u_gname());
enlght_out(buf);
/* show the rest of this game's pantheon (finishes previous sentence)
[appending "also Moloch" at the end would allow for straightforward
trailing "and" on all three aligned entries but looks too verbose] */
Sprintf(buf, " who %s opposed by", !final ? "is" : "was");
if (u.ualign.type != A_LAWFUL)
Sprintf(eos(buf), " %s (%s) and", align_gname(A_LAWFUL),
align_str(A_LAWFUL));
if (u.ualign.type != A_NEUTRAL)
Sprintf(eos(buf), " %s (%s)%s", align_gname(A_NEUTRAL),
align_str(A_NEUTRAL),
(u.ualign.type != A_CHAOTIC) ? " and" : "");
if (u.ualign.type != A_CHAOTIC)
Sprintf(eos(buf), " %s (%s)", align_gname(A_CHAOTIC),
align_str(A_CHAOTIC));
Strcat(buf, "."); /* terminate sentence */
enlght_out(buf);
/* show original alignment,gender,race,role if any have been changed;
giving separate message for temporary alignment change bypasses need
for tricky phrasing otherwise necessitated by possibility of having
helm of opposite alignment mask a permanent alignment conversion */
difgend = (innategend != flags.initgend);
difalgn = (((u.ualign.type != u.ualignbase[A_CURRENT]) ? 1 : 0)
+ ((u.ualignbase[A_CURRENT] != u.ualignbase[A_ORIGINAL])
? 2 : 0));
if (difalgn & 1) { /* have temporary alignment so report permanent one */
Sprintf(buf, "actually %s", align_str(u.ualignbase[A_CURRENT]));
you_are(buf, "");
difalgn &= ~1; /* suppress helm from "started out <foo>" message */
}
if (difgend || difalgn) { /* sex change or perm align change or both */
Sprintf(buf, " You started out %s%s%s.",
difgend ? genders[flags.initgend].adj : "",
(difgend && difalgn) ? " and " : "",
difalgn ? align_str(u.ualignbase[A_ORIGINAL]) : "");
enlght_out(buf);
}
/* "You are left-handed." won't work well if polymorphed into something
without hands; use "You are normally left-handed." in that situation */
Sprintf(buf, "%s%s-handed",
!strcmp(body_part(HANDED), "handed") ? "" : "normally ",
URIGHTY ? "right" : "left");
you_are(buf, "");
/* As of 3.6.2: dungeon level, so that ^X really has all status info as
claimed by the comment below; this reveals more information than
the basic status display, but that's one of the purposes of ^X;
similar information is revealed by #overview; the "You died in
<location>" given by really_done() is more rudimentary than this */
*buf = *tmpbuf = '\0';
if (In_endgame(&u.uz)) {
int egdepth = observable_depth(&u.uz);
(void) endgamelevelname(tmpbuf, egdepth);
Snprintf(buf, sizeof(buf), "in the endgame, on the %s%s",
!strncmp(tmpbuf, "Plane", 5) ? "Elemental " : "", tmpbuf);
} else if (Is_knox(&u.uz)) {
/* this gives away the fact that the knox branch is only 1 level */
Sprintf(buf, "on the %s level", svd.dungeons[u.uz.dnum].dname);
/* TODO? maybe phrase it differently when actually inside the fort,
if we're able to determine that (not trivial) */
} else {
char dgnbuf[QBUFSZ];
Strcpy(dgnbuf, svd.dungeons[u.uz.dnum].dname);
if (!strncmpi(dgnbuf, "The ", 4))
*dgnbuf = lowc(*dgnbuf);
Sprintf(tmpbuf, "level %d",
In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz));
/* TODO? maybe extend this bit to include various other automatic
annotations from the dungeon overview code */
if (Is_rogue_level(&u.uz))
Strcat(tmpbuf, ", a primitive area");
else if (Is_bigroom(&u.uz) && !Blind)
Strcat(tmpbuf, ", a very big room");
Snprintf(buf, sizeof(buf), "in %s, on %s", dgnbuf, tmpbuf);
}
you_are(buf, "");
/* this is shown even if the 'time' option is off */
if (svm.moves == 1L) {
you_have("just started your adventure", "");
} else {
/* 'turns' grates on the nerves in this context... */
Sprintf(buf, "the dungeon %ld turn%s ago",
svm.moves, plur(svm.moves));
/* same phrasing for current and final: "entered" is unconditional */
enlght_line(You_, "entered ", buf, "");
}
/* for gameover, these have been obtained in really_done() so that they
won't vary if user leaves a disclosure prompt or --More-- unanswered
long enough for the dynamic value to change between then and now */
if (final ? iflags.at_midnight : midnight()) {
enl_msg("It ", "is ", "was ", "the midnight hour", "");
} else if (final ? iflags.at_night : night()) {
enl_msg("It ", "is ", "was ", "nighttime", "");
}
/* other environmental factors */
if (flags.moonphase == FULL_MOON || flags.moonphase == NEW_MOON) {
/* [This had "tonight" but has been changed to "in effect".
There is a similar issue to Friday the 13th--it's the value
at the start of the current session but that session might
have dragged on for an arbitrary amount of time. We want to
report the values that currently affect play--or affected
play when game ended--rather than actual outside situation.] */
Sprintf(buf, "a %s moon in effect%s",
(flags.moonphase == FULL_MOON) ? "full"
: (flags.moonphase == NEW_MOON) ? "new"
/* showing these would probably just lead to confusion
since they have no effect on game play... */
: (flags.moonphase < FULL_MOON) ? "first quarter"
: "last quarter",
/* we don't have access to 'how' here--aside from survived
vs died--so settle for general platitude */
final ? " when your adventure ended" : "");
enl_msg("There ", "is ", "was ", buf, "");
}
if (flags.friday13) {
/* let player know that friday13 penalty is/was in effect;
we don't say "it is/was Friday the 13th" because that was at
the start of the session and it might be past midnight (or
days later if the game has been paused without save/restore),
so phrase this similar to the start up message */
Sprintf(buf, " Bad things %s on Friday the 13th.",
!final ? "can happen"
: (final == ENL_GAMEOVERALIVE) ? "could have happened"
/* there's no may to tell whether -1 Luck made a
difference but hero has died... */
: "happened");
enlght_out(buf);
}
if (!Upolyd) {
int ulvl = (int) u.ulevel;
/* [flags.showexp currently does not matter; should it?] */
/* experience level is already shown above */
Sprintf(buf, "%-1ld experience point%s", u.uexp, plur(u.uexp));
/* TODO?
* Remove wizard-mode restriction since patient players can
* determine the numbers needed without resorting to spoilers
* (even before this started being disclosed for 'final';
* just enable 'showexp' and look at normal status lines
* after drinking gain level potions or eating wraith corpses
* or being level-drained by vampires).
*/
if (ulvl < 30 && (final || wizard)) {
long nxtlvl = newuexp(ulvl), delta = nxtlvl - u.uexp;
Sprintf(eos(buf), ", %ld %s%sneeded %s level %d",
delta, (u.uexp > 0) ? "more " : "",
/* present tense=="needed", past tense=="were needed" */
!final ? "" : (delta == 1L) ? "was " : "were ",
/* "for": grammatically iffy but less likely to wrap */
(ulvl < 18) ? "to attain" : "for", (ulvl + 1));
}
you_have(buf, "");
}
#ifdef SCORE_ON_BOTL
if (flags.showscore) {
/* describes what's shown on status line, which is an approximation;
only show it here if player has the 'showscore' option enabled */
Sprintf(buf, "%ld%s", botl_score(),
!final ? "" : " before end-of-game adjustments");
enl_msg("Your score ", "is ", "was ", buf, "");
}
#endif
}
/* hit points, energy points, armor class -- essential information which
doesn't fit very well in other categories */
/*ARGSUSED*/
staticfn void
basics_enlightenment(int mode UNUSED, int final)
{
static char Power[] = "energy points (spell power)";
char buf[BUFSZ];
int pw = u.uen, hp = (Upolyd ? u.mh : u.uhp),
pwmax = u.uenmax, hpmax = (Upolyd ? u.mhmax : u.uhpmax);
enlght_out(""); /* separator after background */
enlght_out("Basics:");
if (hp < 0)
hp = 0;
/* "1 out of 1" rather than "all" if max is only 1; should never happen */
if (hp == hpmax && hpmax > 1)
Sprintf(buf, "all %d hit points", hpmax);
else
Sprintf(buf, "%d out of %d hit point%s", hp, hpmax, plur(hpmax));
you_have(buf, "");
/* low max energy is feasible, so handle couple of extra special cases */
if (pwmax == 0 || (pw == pwmax && pwmax == 2)) /* both: not "all 2" */
Sprintf(buf, "%s %s", !pwmax ? "no" : "both", Power);
else if (pw == pwmax && pwmax > 2)
Sprintf(buf, "all %d %s", pwmax, Power);
else
Sprintf(buf, "%d out of %d %s", pw, pwmax, Power);
you_have(buf, "");
if (Upolyd) {
switch (mons[u.umonnum].mlevel) {
case 0:
/* status line currently being explained shows "HD:0" */
Strcpy(buf, "0 hit dice (actually 1/2)");
break;
case 1:
Strcpy(buf, "1 hit die");
break;
default:
Sprintf(buf, "%d hit dice", mons[u.umonnum].mlevel);
break;
}
you_have(buf, "");
}
find_ac(); /* enforces AC_MAX cap */
Sprintf(buf, "%d", u.uac);
if (abs(u.uac) == AC_MAX)
Sprintf(eos(buf), ", the %s possible",
(u.uac < 0) ? "best" : "worst");
enl_msg("Your armor class ", "is ", "was ", buf, "");
/* gold; similar to doprgold (#showgold) but without shop billing info;
includes container contents, unlike status line but like doprgold */
{
long umoney = money_cnt(gi.invent), hmoney = hidden_gold(final);
if (!umoney) {
Sprintf(buf, " Your wallet %s empty", !final ? "is" : "was");
} else {
Sprintf(buf, " Your wallet contain%s %ld %s", !final ? "s" : "ed",
umoney, currency(umoney));
}
/* terminate the wallet line if appropriate, otherwise add an
introduction to subsequent continuation; output now either way */
Strcat(buf, !hmoney ? "." : !umoney ? ", but" : ", and");
enlght_out(buf);
/* put contained gold on its own line to avoid excessive width; it's
phrased as a continuation of the wallet line so not capitalized */
if (hmoney) {
Sprintf(buf, "%ld %s stashed away in your pack",
hmoney, umoney ? "more" : currency(hmoney));
enl_msg("you ", "have ", "had ", buf, "");
}
}
if (flags.pickup) {
char ocl[MAXOCLASSES + 1];
Strcpy(buf, "on");
if (costly_spot(u.ux, u.uy)) {
/* being in a shop inhibits autopickup, even 'pickup_thrown' */
Strcat(buf, ", but temporarily disabled while inside the shop");
} else {
oc_to_str(flags.pickup_types, ocl);
Sprintf(eos(buf), " for %s%s%s", *ocl ? "'" : "",
*ocl ? ocl : "all types", *ocl ? "'" : "");
if (flags.pickup_thrown && *ocl)
Strcat(buf, " plus thrown"); /* show when not 'all types' */
if (ga.apelist)
Strcat(buf, ", with exceptions");
}
} else
Strcpy(buf, "off");
enl_msg("Autopickup ", "is ", "was ", buf, "");
}
/* characteristics: expanded version of bottom line strength, dexterity, &c */
staticfn void
characteristics_enlightenment(int mode, int final)
{
char buf[BUFSZ];
enlght_out("");
Sprintf(buf, "%sCharacteristics:", !final ? "" : "Final ");
enlght_out(buf);
/* bottom line order */
one_characteristic(mode, final, A_STR); /* strength */
one_characteristic(mode, final, A_DEX); /* dexterity */
one_characteristic(mode, final, A_CON); /* constitution */
one_characteristic(mode, final, A_INT); /* intelligence */
one_characteristic(mode, final, A_WIS); /* wisdom */
one_characteristic(mode, final, A_CHA); /* charisma */
}
/* display one attribute value for characteristics_enlightenment() */
staticfn void
one_characteristic(int mode, int final, int attrindx)
{
extern const char *const attrname[]; /* attrib.c */
boolean hide_innate_value = FALSE, interesting_alimit;
int acurrent, abase, apeak, alimit;
const char *paren_pfx;
char subjbuf[BUFSZ], valubuf[BUFSZ], valstring[32];
/* being polymorphed or wearing certain cursed items prevents
hero from reliably tracking changes to characteristics so
we don't show base & peak values then; when the items aren't
cursed, hero could take them off to check underlying values
and we show those in such case so that player doesn't need
to actually resort to doing that */
if (Upolyd) {
hide_innate_value = TRUE;
} else if (Fixed_abil) {
if (stuck_ring(uleft, RIN_SUSTAIN_ABILITY)
|| stuck_ring(uright, RIN_SUSTAIN_ABILITY))
hide_innate_value = TRUE;
}
switch (attrindx) {
case A_STR:
if (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER && uarmg->cursed)
hide_innate_value = TRUE;
break;
case A_DEX:
break;
case A_CON:
if (u_wield_art(ART_OGRESMASHER) && uwep->cursed)
hide_innate_value = TRUE;
break;
case A_INT:
if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed)
hide_innate_value = TRUE;
break;
case A_WIS:
if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed)
hide_innate_value = TRUE;
break;
case A_CHA:
break;
default:
return; /* impossible */
};
/* note: final disclosure includes MAGICENLIGHTENTMENT */
if ((mode & MAGICENLIGHTENMENT) && !Upolyd)
hide_innate_value = FALSE;
acurrent = ACURR(attrindx);
(void) attrval(attrindx, acurrent, valubuf); /* Sprintf(valubuf,"%d",) */
Sprintf(subjbuf, "Your %s ", attrname[attrindx]);
if (!hide_innate_value) {
/* show abase, amax, and/or attrmax if acurr doesn't match abase
(a magic bonus or penalty is in effect) or abase doesn't match
amax (some points have been lost to poison or exercise abuse
and are restorable) or attrmax is different from normal human
(while game is in progress; trying to reduce dependency on
spoilers to keep track of such stuff) or attrmax was different
from abase (at end of game; this attribute wasn't maxed out) */
abase = ABASE(attrindx);
apeak = AMAX(attrindx);
alimit = ATTRMAX(attrindx);
/* criterium for whether the limit is interesting varies */
interesting_alimit =
final ? TRUE /* was originally `(abase != alimit)' */
: (alimit != (attrindx != A_STR ? 18 : STR18(100)));
paren_pfx = final ? " (" : " (current; ";
if (acurrent != abase) {
Sprintf(eos(valubuf), "%sbase:%s", paren_pfx,
attrval(attrindx, abase, valstring));
paren_pfx = ", ";
}
if (abase != apeak) {
Sprintf(eos(valubuf), "%speak:%s", paren_pfx,
attrval(attrindx, apeak, valstring));
paren_pfx = ", ";
}
if (interesting_alimit) {
Sprintf(eos(valubuf), "%s%slimit:%s", paren_pfx,
/* more verbose if exceeding 'limit' due to magic bonus */
(acurrent > alimit) ? "innate " : "",
attrval(attrindx, alimit, valstring));
/* paren_pfx = ", "; */
}
if (acurrent != abase || abase != apeak || interesting_alimit)
Strcat(valubuf, ")");
}
enl_msg(subjbuf, "is ", "was ", valubuf, "");
}
/* status: selected obvious capabilities, assorted troubles */
staticfn void
status_enlightenment(int mode, int final)
{
boolean magic = (mode & MAGICENLIGHTENMENT) ? TRUE : FALSE;
int cap;
char buf[BUFSZ], youtoo[BUFSZ], heldmon[BUFSZ];
boolean Riding = (u.usteed
/* if hero dies while dismounting, u.usteed will still
be set; we want to ignore steed in that situation */
&& !(final == ENL_GAMEOVERDEAD
&& !strcmp(svk.killer.name, "riding accident")));
const char *steedname = (!Riding ? (char *) 0
: x_monnam(u.usteed,
u.usteed->mtame ? ARTICLE_YOUR : ARTICLE_THE,
(char *) 0,
(SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION),
FALSE));
/*\
* Status (many are abbreviated on bottom line; others are or
* should be discernible to the hero hence to the player)
\*/
enlght_out(""); /* separator after title or characteristics */
enlght_out(final ? "Final Status:" : "Status:");
Strcpy(youtoo, You_);
/* not a traditional status but inherently obvious to player; more
detail given below (attributes section) for magic enlightenment */
if (Upolyd) {
Strcpy(buf, "transformed");
if (ugenocided())
Sprintf(eos(buf), " and %s %s inside",
final ? "felt" : "feel", udeadinside());
you_are(buf, "");
}
/* not a trouble, but we want to display riding status before maybe
reporting steed as trapped or hero stuck to cursed saddle */
if (Riding) {
Sprintf(buf, "riding %s", steedname);
you_are(buf, "");
Sprintf(eos(youtoo), "and %s ", steedname);
}
/* other movement situations that hero should always know */
if (Levitation) {
if (Lev_at_will && magic)
you_are("levitating, at will", "");
else
enl_msg(youtoo, are, were, "levitating", from_what(LEVITATION));
} else if (Flying) { /* can only fly when not levitating */
enl_msg(youtoo, are, were, "flying", from_what(FLYING));
}
if (Underwater) {
you_are("underwater", "");
} else if (u.uinwater) {
you_are(Swimming ? "swimming" : "in water", from_what(SWIMMING));
} else if (walking_on_water()) {
/* show active Wwalking here, potential Wwalking elsewhere */
Sprintf(buf, "walking on %s",
is_pool(u.ux, u.uy) ? "water"
: is_lava(u.ux, u.uy) ? "lava"
: surface(u.ux, u.uy)); /* catchall; shouldn't happen */
you_are(buf, from_what(WWALKING));