forked from NetHack/NetHack
-
Notifications
You must be signed in to change notification settings - Fork 0
/
engrave.c
1523 lines (1402 loc) · 50.2 KB
/
engrave.c
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 3.7 engrave.c $NHDT-Date: 1664616835 2022/10/01 09:33:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.131 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2012. */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
static int stylus_ok(struct obj *);
static boolean u_can_engrave(void);
static int engrave(void);
static const char *blengr(void);
char *
random_engraving(char *outbuf)
{
const char *rumor;
/* a random engraving may come from the "rumors" file,
or from the "engrave" file (formerly in an array here) */
if (!rn2(4) || !(rumor = getrumor(0, outbuf, TRUE)) || !*rumor)
(void) get_rnd_text(ENGRAVEFILE, outbuf, rn2, MD_PAD_RUMORS);
wipeout_text(outbuf, (int) (strlen(outbuf) / 4), 0);
return outbuf;
}
/* Partial rubouts for engraving characters. -3. */
static const struct {
char wipefrom;
const char *wipeto;
} rubouts[] = { { 'A', "^" },
{ 'B', "Pb[" },
{ 'C', "(" },
{ 'D', "|)[" },
{ 'E', "|FL[_" },
{ 'F', "|-" },
{ 'G', "C(" },
{ 'H', "|-" },
{ 'I', "|" },
{ 'K', "|<" },
{ 'L', "|_" },
{ 'M', "|" },
{ 'N', "|\\" },
{ 'O', "C(" },
{ 'P', "F" },
{ 'Q', "C(" },
{ 'R', "PF" },
{ 'T', "|" },
{ 'U', "J" },
{ 'V', "/\\" },
{ 'W', "V/\\" },
{ 'Z', "/" },
{ 'b', "|" },
{ 'd', "c|" },
{ 'e', "c" },
{ 'g', "c" },
{ 'h', "n" },
{ 'j', "i" },
{ 'k', "|" },
{ 'l', "|" },
{ 'm', "nr" },
{ 'n', "r" },
{ 'o', "c" },
{ 'q', "c" },
{ 'w', "v" },
{ 'y', "v" },
{ ':', "." },
{ ';', ",:" },
{ ',', "." },
{ '=', "-" },
{ '+', "-|" },
{ '*', "+" },
{ '@', "0" },
{ '0', "C(" },
{ '1', "|" },
{ '6', "o" },
{ '7', "/" },
{ '8', "3o" } };
/* degrade some of the characters in a string */
void
wipeout_text(
char *engr, /* engraving text */
int cnt, /* number of chars to degrade */
unsigned seed) /* for semi-controlled randomization */
{
char *s;
int i, j, nxt, use_rubout;
unsigned lth = (unsigned) strlen(engr);
if (lth && cnt > 0) {
while (cnt--) {
/* pick next character */
if (!seed) {
/* random */
nxt = rn2((int) lth);
use_rubout = rn2(4);
} else {
/* predictable; caller can reproduce the same sequence by
supplying the same arguments later, or a pseudo-random
sequence by varying any of them */
nxt = seed % lth;
seed *= 31, seed %= (BUFSZ - 1);
use_rubout = seed & 3;
}
s = &engr[nxt];
if (*s == ' ')
continue;
/* rub out unreadable & small punctuation marks */
if (strchr("?.,'`-|_", *s)) {
*s = ' ';
continue;
}
if (!use_rubout) {
i = SIZE(rubouts);
} else {
for (i = 0; i < SIZE(rubouts); i++)
if (*s == rubouts[i].wipefrom) {
unsigned ln = (unsigned) strlen(rubouts[i].wipeto);
/*
* Pick one of the substitutes at random.
*/
if (!seed) {
j = rn2((int) ln);
} else {
seed *= 31, seed %= (BUFSZ - 1);
j = seed % ln;
}
*s = rubouts[i].wipeto[j];
break;
}
}
/* didn't pick rubout; use '?' for unreadable character */
if (i == SIZE(rubouts))
*s = '?';
}
}
/* trim trailing spaces */
while (lth && engr[lth - 1] == ' ')
engr[--lth] = '\0';
}
/* check whether hero can reach something at ground level */
boolean
can_reach_floor(boolean check_pit)
{
struct trap *t;
if (u.uswallow
|| (u.ustuck && !sticks(gy.youmonst.data)
/* assume that arms are pinned rather than that the hero
has been lifted up above the floor [doesn't explain
how hero can attack the creature holding him or her;
that's life in nethack...] */
&& attacktype(u.ustuck->data, AT_HUGS))
|| (Levitation && !(Is_airlevel(&u.uz) || Is_waterlevel(&u.uz))))
return FALSE;
/* Restricted/unskilled riders can't reach the floor */
if (u.usteed && P_SKILL(P_RIDING) < P_BASIC)
return FALSE;
if (u.uundetected && ceiling_hider(gy.youmonst.data))
return FALSE;
if (Flying || gy.youmonst.data->msize >= MZ_HUGE)
return TRUE;
if (check_pit && (t = t_at(u.ux, u.uy)) != 0
&& (uteetering_at_seen_pit(t) || uescaped_shaft(t)))
return FALSE;
return TRUE;
}
/* give a message after caller has determined that hero can't reach */
void
cant_reach_floor(coordxy x, coordxy y, boolean up, boolean check_pit)
{
You("can't reach the %s.",
up ? ceiling(x, y)
: (check_pit && can_reach_floor(FALSE))
? "bottom of the pit"
: surface(x, y));
}
const char *
surface(coordxy x, coordxy y)
{
struct rm *lev = &levl[x][y];
if (u_at(x, y) && u.uswallow && is_animal(u.ustuck->data))
/* 'husk' is iffy but maw is wrong for 't' class */
return digests(u.ustuck->data) ? "maw"
: enfolds(u.ustuck->data) ? "husk"
: "nonesuch"; /* can't happen (fingers crossed...) */
else if (IS_AIR(lev->typ) && Is_airlevel(&u.uz))
return "air";
else if (is_pool(x, y))
return (Underwater && !Is_waterlevel(&u.uz))
? "bottom" : hliquid("water");
else if (is_ice(x, y))
return "ice";
else if (is_lava(x, y))
return hliquid("lava");
else if (lev->typ == DRAWBRIDGE_DOWN)
return "bridge";
else if (IS_ALTAR(levl[x][y].typ))
return "altar";
else if (IS_GRAVE(levl[x][y].typ))
return "headstone";
else if (IS_FOUNTAIN(levl[x][y].typ))
return "fountain";
else if ((IS_ROOM(lev->typ) && !Is_earthlevel(&u.uz))
|| IS_WALL(lev->typ) || IS_DOOR(lev->typ) || lev->typ == SDOOR)
return "floor";
else
return "ground";
}
const char *
ceiling(coordxy x, coordxy y)
{
struct rm *lev = &levl[x][y];
const char *what;
/* other room types will no longer exist when we're interested --
* see check_special_room()
*/
if (*in_rooms(x, y, VAULT))
what = "vault's ceiling";
else if (*in_rooms(x, y, TEMPLE))
what = "temple's ceiling";
else if (*in_rooms(x, y, SHOPBASE))
what = "shop's ceiling";
else if (Is_waterlevel(&u.uz))
/* water plane has no surface; its air bubbles aren't below sky */
what = "water above";
else if (IS_AIR(lev->typ))
what = "sky";
else if (Underwater)
what = "water's surface";
else if ((IS_ROOM(lev->typ) && !Is_earthlevel(&u.uz))
|| IS_WALL(lev->typ) || IS_DOOR(lev->typ) || lev->typ == SDOOR)
what = "ceiling";
else
what = "rock cavern";
return what;
}
struct engr *
engr_at(coordxy x, coordxy y)
{
register struct engr *ep = head_engr;
while (ep) {
if (x == ep->engr_x && y == ep->engr_y)
return ep;
ep = ep->nxt_engr;
}
return (struct engr *) 0;
}
/* Decide whether a particular string is engraved at a specified
* location; a case-insensitive substring match is used.
* Ignore headstones, in case the player names herself "Elbereth".
*
* If strict checking is requested, the word is only considered to be
* present if it is intact and is the entire content of the engraving.
*/
struct engr *
sengr_at(const char *s, coordxy x, coordxy y, boolean strict)
{
struct engr *ep = engr_at(x, y);
if (ep && ep->engr_type != HEADSTONE && ep->engr_time <= gm.moves) {
if (strict ? !strcmpi(ep->engr_txt, s)
: (strstri(ep->engr_txt, s) != 0))
return ep;
}
return (struct engr *) NULL;
}
void
u_wipe_engr(int cnt)
{
if (can_reach_floor(TRUE))
wipe_engr_at(u.ux, u.uy, cnt, FALSE);
}
void
wipe_engr_at(coordxy x, coordxy y, xint16 cnt, boolean magical)
{
register struct engr *ep = engr_at(x, y);
/* Headstones are indelible */
if (ep && ep->engr_type != HEADSTONE) {
debugpline1("asked to erode %d characters", cnt);
if (ep->engr_type != BURN || is_ice(x, y) || (magical && !rn2(2))) {
if (ep->engr_type != DUST && ep->engr_type != ENGR_BLOOD) {
cnt = rn2(1 + 50 / (cnt + 1)) ? 0 : 1;
debugpline1("actually eroding %d characters", cnt);
}
wipeout_text(ep->engr_txt, (int) cnt, 0);
while (ep->engr_txt[0] == ' ')
ep->engr_txt++;
if (!ep->engr_txt[0])
del_engr(ep);
}
}
}
void
read_engr_at(coordxy x, coordxy y)
{
struct engr *ep = engr_at(x, y);
int sensed = 0;
/* Sensing an engraving does not require sight,
* nor does it necessarily imply comprehension (literacy).
*/
if (ep && ep->engr_txt[0]) {
switch (ep->engr_type) {
case DUST:
if (!Blind) {
sensed = 1;
pline("%s is written here in the %s.", Something,
is_ice(x, y) ? "frost" : "dust");
}
break;
case ENGRAVE:
case HEADSTONE:
if (!Blind || can_reach_floor(TRUE)) {
sensed = 1;
pline("%s is engraved here on the %s.", Something,
surface(x, y));
}
break;
case BURN:
if (!Blind || can_reach_floor(TRUE)) {
sensed = 1;
pline("Some text has been %s into the %s here.",
is_ice(x, y) ? "melted" : "burned", surface(x, y));
}
break;
case MARK:
if (!Blind) {
sensed = 1;
pline("There's some graffiti on the %s here.", surface(x, y));
}
break;
case ENGR_BLOOD:
/* "It's a message! Scrawled in blood!"
* "What's it say?"
* "It says... `See you next Wednesday.'" -- Thriller
*/
if (!Blind) {
sensed = 1;
You_see("a message scrawled in blood here.");
}
break;
default:
impossible("%s is written in a very strange way.", Something);
sensed = 1;
}
if (sensed) {
char *et, buf[BUFSZ];
int maxelen = (int) (sizeof buf
/* sizeof "literal" counts terminating \0 */
- sizeof "You feel the words: \"\".");
if ((int) strlen(ep->engr_txt) > maxelen) {
(void) strncpy(buf, ep->engr_txt, maxelen);
buf[maxelen] = '\0';
et = buf;
} else {
et = ep->engr_txt;
}
You("%s: \"%s\".", (Blind) ? "feel the words" : "read", et);
if (gc.context.run > 0)
nomul(0);
}
}
}
void
make_engr_at(coordxy x, coordxy y, const char *s, long e_time, xint16 e_type)
{
struct engr *ep;
unsigned smem = Strlen(s) + 1;
if ((ep = engr_at(x, y)) != 0)
del_engr(ep);
ep = newengr(smem);
(void) memset((genericptr_t) ep, 0, smem + sizeof (struct engr));
ep->nxt_engr = head_engr;
head_engr = ep;
ep->engr_x = x;
ep->engr_y = y;
ep->engr_txt = (char *) (ep + 1);
Strcpy(ep->engr_txt, s);
if (!strcmp(s, "Elbereth")) {
/* engraving "Elbereth": if done when making a level, it creates
an old-style Elbereth that deters monsters when any objects are
present; otherwise (done by the player), exercises wisdom */
if (gi.in_mklev)
ep->guardobjects = 1;
else
exercise(A_WIS, TRUE);
}
ep->engr_time = e_time;
ep->engr_type = e_type > 0 ? e_type : rnd(N_ENGRAVE - 1);
ep->engr_lth = smem;
}
/* delete any engraving at location <x,y> */
void
del_engr_at(coordxy x, coordxy y)
{
struct engr *ep = engr_at(x, y);
if (ep)
del_engr(ep);
}
/*
* freehand - returns true if player has a free hand
*/
int
freehand(void)
{
return (!uwep || !welded(uwep)
|| (!bimanual(uwep) && (!uarms || !uarms->cursed)));
}
/* getobj callback for an object to engrave with */
static int
stylus_ok(struct obj *obj)
{
if (!obj)
return GETOBJ_SUGGEST;
/* Potential extension: exclude weapons that don't make any sense (such as
* bullwhips) and downplay rings and gems that wouldn't be good to write
* with (such as glass and non-gem rings) */
if (obj->oclass == WEAPON_CLASS || obj->oclass == WAND_CLASS
|| obj->oclass == GEM_CLASS || obj->oclass == RING_CLASS)
return GETOBJ_SUGGEST;
/* Only markers and towels are recommended tools. */
if (obj->oclass == TOOL_CLASS
&& (obj->otyp == TOWEL || obj->otyp == MAGIC_MARKER))
return GETOBJ_SUGGEST;
return GETOBJ_DOWNPLAY;
}
/* can hero engrave at all (at their location)? */
static boolean
u_can_engrave(void)
{
if (u.uswallow) {
if (is_animal(u.ustuck->data)) {
pline("What would you write? \"Jonah was here\"?");
return FALSE;
} else if (is_whirly(u.ustuck->data)) {
cant_reach_floor(u.ux, u.uy, FALSE, FALSE);
return FALSE;
}
/* Note: for amorphous engulfers, writing attempt is allowed here
but yields the 'jello' result in doengrave() */
} else if (is_lava(u.ux, u.uy)) {
You_cant("write on the %s!", surface(u.ux, u.uy));
return FALSE;
} else if (is_pool(u.ux, u.uy) || IS_FOUNTAIN(levl[u.ux][u.uy].typ)) {
You_cant("write on the %s!", surface(u.ux, u.uy));
return FALSE;
}
if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) /* in bubble */) {
You_cant("write in thin air!");
return FALSE;
} else if (!accessible(u.ux, u.uy)) {
/* stone, tree, wall, secret corridor, pool, lava, bars */
You_cant("write here.");
return FALSE;
}
if (cantwield(gy.youmonst.data)) {
You_cant("even hold anything!");
return FALSE;
}
if (check_capacity((char *) 0))
return FALSE;
return TRUE;
}
/* Mohs' Hardness Scale:
* 1 - Talc 6 - Orthoclase
* 2 - Gypsum 7 - Quartz
* 3 - Calcite 8 - Topaz
* 4 - Fluorite 9 - Corundum
* 5 - Apatite 10 - Diamond
*
* Since granite is an igneous rock hardness ~ 7, anything >= 8 should
* probably be able to scratch the rock.
* Devaluation of less hard gems is not easily possible because obj struct
* does not contain individual oc_cost currently. 7/91
*
* steel - 5-8.5 (usu. weapon)
* diamond - 10 * jade - 5-6 (nephrite)
* ruby - 9 (corundum) * turquoise - 5-6
* sapphire - 9 (corundum) * opal - 5-6
* topaz - 8 * glass - ~5.5
* emerald - 7.5-8 (beryl) * dilithium - 4-5??
* aquamarine - 7.5-8 (beryl) * iron - 4-5
* garnet - 7.25 (var. 6.5-8) * fluorite - 4
* agate - 7 (quartz) * brass - 3-4
* amethyst - 7 (quartz) * gold - 2.5-3
* jasper - 7 (quartz) * silver - 2.5-3
* onyx - 7 (quartz) * copper - 2.5-3
* moonstone - 6 (orthoclase) * amber - 2-2.5
*/
/* the #engrave command */
int
doengrave(void)
{
boolean dengr = FALSE; /* TRUE if we wipe out the current engraving */
boolean doblind = FALSE; /* TRUE if engraving blinds the player */
boolean doknown = FALSE; /* TRUE if we identify the stylus */
boolean eow = FALSE; /* TRUE if we are overwriting oep */
boolean jello = FALSE; /* TRUE if we are engraving in slime */
boolean ptext = TRUE; /* TRUE if we must prompt for engrave text */
boolean teleengr = FALSE; /* TRUE if we move the old engraving */
boolean zapwand = FALSE; /* TRUE if we remove a wand charge */
xint16 type = DUST; /* Type of engraving made */
xint16 oetype = 0; /* will be set to type of current engraving */
char buf[BUFSZ]; /* Buffer for final/poly engraving text */
char ebuf[BUFSZ]; /* Buffer for initial engraving text */
char fbuf[BUFSZ]; /* Buffer for "your fingers" */
char qbuf[QBUFSZ]; /* Buffer for query text */
char post_engr_text[BUFSZ]; /* Text displayed after engraving prompt */
const char *everb; /* Present tense of engraving type */
const char *eloc; /* Where the engraving is (ie dust/floor/...) */
char *sp; /* Place holder for space count of engr text */
size_t len; /* # of nonspace chars of new engraving text */
struct engr *oep = engr_at(u.ux, u.uy); /* The current engraving */
struct obj *otmp; /* Object selected with which to engrave */
char *writer;
boolean frosted, adding;
gm.multi = 0; /* moves consumed */
gn.nomovemsg = (char *) 0; /* occupation end message */
buf[0] = (char) 0;
ebuf[0] = (char) 0;
post_engr_text[0] = (char) 0;
if (oep)
oetype = oep->engr_type;
if (is_demon(gy.youmonst.data) || is_vampire(gy.youmonst.data))
type = ENGR_BLOOD;
/* Can the adventurer engrave at all? */
if (!u_can_engrave())
return ECMD_OK;
jello = (u.uswallow && !(is_animal(u.ustuck->data)
|| is_whirly(u.ustuck->data)));
/* One may write with finger, or weapon, or wand, or..., or...
* Edited by GAN 10/20/86 so as not to change weapon wielded.
*/
otmp = getobj("write with", stylus_ok, GETOBJ_PROMPT);
if (!otmp) /* otmp == cg.zeroobj if fingers */
return ECMD_CANCEL;
if (otmp == &cg.zeroobj) {
Strcat(strcpy(fbuf, "your "), body_part(FINGERTIP));
writer = fbuf;
} else {
writer = yname(otmp);
}
frosted = is_ice(u.ux, u.uy);
/* There's no reason you should be able to write with a wand
* while both your hands are tied up.
*/
if (!freehand() && otmp != uwep && !otmp->owornmask) {
You("have no free %s to write with!", body_part(HAND));
return ECMD_OK;
}
if (jello) {
You("tickle %s with %s.", mon_nam(u.ustuck), writer);
Your("message dissolves...");
return ECMD_OK;
}
if (otmp->oclass != WAND_CLASS && !can_reach_floor(TRUE)) {
cant_reach_floor(u.ux, u.uy, FALSE, TRUE);
return ECMD_OK;
}
if (IS_ALTAR(levl[u.ux][u.uy].typ)) {
You("make a motion towards the altar with %s.", writer);
altar_wrath(u.ux, u.uy);
return ECMD_OK;
}
if (IS_GRAVE(levl[u.ux][u.uy].typ)) {
if (otmp == &cg.zeroobj) { /* using only finger */
You("would only make a small smudge on the %s.",
surface(u.ux, u.uy));
return ECMD_OK;
} else if (!levl[u.ux][u.uy].disturbed) {
/* disturb the grave: summon a ghoul, same as sometimes
happens when kicking; sets levl[ux][uy]->disturbed so
that it'll only happen once */
disturb_grave(u.ux, u.uy);
return ECMD_TIME;
}
}
/* SPFX for items */
switch (otmp->oclass) {
default:
case AMULET_CLASS:
case CHAIN_CLASS:
case POTION_CLASS:
case COIN_CLASS:
break;
case RING_CLASS:
/* "diamond" rings and others should work */
case GEM_CLASS:
/* diamonds & other hard gems should work */
if (objects[otmp->otyp].oc_tough) {
type = ENGRAVE;
break;
}
break;
case ARMOR_CLASS:
if (is_boots(otmp)) {
type = DUST;
break;
}
/*FALLTHRU*/
/* Objects too large to engrave with */
case BALL_CLASS:
case ROCK_CLASS:
You_cant("engrave with such a large object!");
ptext = FALSE;
break;
/* Objects too silly to engrave with */
case FOOD_CLASS:
case SCROLL_CLASS:
case SPBOOK_CLASS:
pline("%s would get %s.", Yname2(otmp),
frosted ? "all frosty" : "too dirty");
ptext = FALSE;
break;
case RANDOM_CLASS: /* This should mean fingers */
break;
/* The charge is removed from the wand before prompting for
* the engraving text, because all kinds of setup decisions
* and pre-engraving messages are based upon knowing what type
* of engraving the wand is going to do. Also, the player
* will have potentially seen "You wrest .." message, and
* therefore will know they are using a charge.
*/
case WAND_CLASS:
if (zappable(otmp)) {
check_unpaid(otmp);
if (otmp->cursed && !rn2(WAND_BACKFIRE_CHANCE)) {
wand_explode(otmp, 0);
return ECMD_TIME;
}
zapwand = TRUE;
if (!can_reach_floor(TRUE))
ptext = FALSE;
switch (otmp->otyp) {
/* DUST wands */
default:
break;
/* NODIR wands */
case WAN_LIGHT:
case WAN_SECRET_DOOR_DETECTION:
case WAN_CREATE_MONSTER:
case WAN_WISHING:
case WAN_ENLIGHTENMENT:
zapnodir(otmp);
break;
/* IMMEDIATE wands */
/* If wand is "IMMEDIATE", remember to affect the
* previous engraving even if turning to dust.
*/
case WAN_STRIKING:
Strcpy(post_engr_text,
"The wand unsuccessfully fights your attempt to write!");
break;
case WAN_SLOW_MONSTER:
if (!Blind) {
Sprintf(post_engr_text, "The bugs on the %s slow down!",
surface(u.ux, u.uy));
}
break;
case WAN_SPEED_MONSTER:
if (!Blind) {
Sprintf(post_engr_text, "The bugs on the %s speed up!",
surface(u.ux, u.uy));
}
break;
case WAN_POLYMORPH:
if (oep) {
if (!Blind) {
type = (xint16) 0; /* random */
(void) random_engraving(buf);
} else {
/* keep the same type so that feels don't
change and only the text is altered,
but you won't know anyway because
you're a _blind writer_ */
if (oetype)
type = oetype;
xcrypt(blengr(), buf);
}
dengr = TRUE;
}
break;
case WAN_NOTHING:
case WAN_UNDEAD_TURNING:
case WAN_OPENING:
case WAN_LOCKING:
case WAN_PROBING:
break;
/* RAY wands */
case WAN_MAGIC_MISSILE:
ptext = TRUE;
if (!Blind) {
Sprintf(post_engr_text,
"The %s is riddled by bullet holes!",
surface(u.ux, u.uy));
}
break;
/* can't tell sleep from death - Eric Backus */
case WAN_SLEEP:
case WAN_DEATH:
if (!Blind) {
Sprintf(post_engr_text, "The bugs on the %s stop moving!",
surface(u.ux, u.uy));
}
break;
case WAN_COLD:
if (!Blind)
Strcpy(post_engr_text,
"A few ice cubes drop from the wand.");
if (!oep || (oep->engr_type != BURN))
break;
/*FALLTHRU*/
case WAN_CANCELLATION:
case WAN_MAKE_INVISIBLE:
if (oep && oep->engr_type != HEADSTONE) {
if (!Blind)
pline_The("engraving on the %s vanishes!",
surface(u.ux, u.uy));
dengr = TRUE;
}
break;
case WAN_TELEPORTATION:
if (oep && oep->engr_type != HEADSTONE) {
if (!Blind)
pline_The("engraving on the %s vanishes!",
surface(u.ux, u.uy));
teleengr = TRUE;
}
break;
/* type = ENGRAVE wands */
case WAN_DIGGING:
ptext = TRUE;
type = ENGRAVE;
if (!objects[otmp->otyp].oc_name_known) {
if (Verbose(1, doengrave1))
pline("This %s is a wand of digging!", xname(otmp));
doknown = TRUE;
}
Strcpy(post_engr_text,
(Blind && !Deaf)
? "You hear drilling!" /* Deaf-aware */
: Blind
? "You feel tremors."
: IS_GRAVE(levl[u.ux][u.uy].typ)
? "Chips fly out from the headstone."
: frosted
? "Ice chips fly up from the ice surface!"
: (gl.level.locations[u.ux][u.uy].typ
== DRAWBRIDGE_DOWN)
? "Splinters fly up from the bridge."
: "Gravel flies up from the floor.");
break;
/* type = BURN wands */
case WAN_FIRE:
ptext = TRUE;
type = BURN;
if (!objects[otmp->otyp].oc_name_known) {
if (Verbose(1, doengrave2))
pline("This %s is a wand of fire!", xname(otmp));
doknown = TRUE;
}
Strcpy(post_engr_text, Blind ? "You feel the wand heat up."
: "Flames fly from the wand.");
break;
case WAN_LIGHTNING:
ptext = TRUE;
type = BURN;
if (!objects[otmp->otyp].oc_name_known) {
if (Verbose(1, doengrave3))
pline("This %s is a wand of lightning!", xname(otmp));
doknown = TRUE;
}
if (!Blind) {
Strcpy(post_engr_text, "Lightning arcs from the wand.");
doblind = TRUE;
} else {
Strcpy(post_engr_text, !Deaf
? "You hear crackling!" /* Deaf-aware */
: "Your hair stands up!");
}
break;
/* type = MARK wands */
/* type = ENGR_BLOOD wands */
}
} else { /* end if zappable */
/* failing to wrest one last charge takes time */
ptext = FALSE; /* use "early exit" below, return 1 */
/* give feedback here if we won't be getting the
"can't reach floor" message below */
if (can_reach_floor(TRUE)) {
/* cancelled wand turns to dust */
if (otmp->spe < 0)
zapwand = TRUE;
/* empty wand just doesn't write */
else
pline_The("wand is too worn out to engrave.");
}
}
break;
case WEAPON_CLASS:
if (is_art(otmp, ART_FIRE_BRAND)) {
type = BURN; /* doesn't dull weapon */
} else if (is_blade(otmp)) {
if ((int) otmp->spe > -3)
type = ENGRAVE;
else
pline("%s too dull for engraving.", Yobjnam2(otmp, "are"));
}
break;
case TOOL_CLASS:
if (otmp == ublindf) {
pline(
"That is a bit difficult to engrave with, don't you think?");
return ECMD_OK;
}
switch (otmp->otyp) {
case MAGIC_MARKER:
if (otmp->spe <= 0)
Your("marker has dried out.");
else
type = MARK;
break;
case TOWEL:
/* Can't really engrave with a towel */
ptext = FALSE;
if (oep) {
if (oep->engr_type == DUST
|| oep->engr_type == ENGR_BLOOD
|| oep->engr_type == MARK) {
if (is_wet_towel(otmp))
dry_a_towel(otmp, -1, TRUE);
if (!Blind)
You("wipe out the message here.");
else
pline("%s %s.", Yobjnam2(otmp, "get"),
frosted ? "frosty" : "dusty");
dengr = TRUE;
} else {
pline("%s can't wipe out this engraving.", Yname2(otmp));
}
} else {
pline("%s %s.", Yobjnam2(otmp, "get"),
frosted ? "frosty" : "dusty");
}
break;
default:
break;
}
break;
case VENOM_CLASS:
/* this used to be ``if (wizard)'' and fall through to ILLOBJ_CLASS
for normal play, but splash of venom isn't "illegal" because it
could occur in normal play via wizard mode bones */
pline("Writing a poison pen letter?");
break;
case ILLOBJ_CLASS:
impossible("You're engraving with an illegal object!");
break;
}
if (IS_GRAVE(levl[u.ux][u.uy].typ)) {
if (type == ENGRAVE || type == 0) {
type = HEADSTONE;
} else {
/* ensures the "cannot wipe out" case */
type = DUST;
dengr = FALSE;
teleengr = FALSE;
buf[0] = '\0';
}
}
/*
* End of implement setup
*/
/* Identify stylus */
if (doknown) {
learnwand(otmp);
if (objects[otmp->otyp].oc_name_known)
more_experienced(0, 10);
}
if (teleengr) {
rloc_engr(oep);
oep = (struct engr *) 0;
}
if (dengr) {
del_engr(oep);
oep = (struct engr *) 0;
}
/* Something has changed the engraving here */
if (*buf) {
make_engr_at(u.ux, u.uy, buf, gm.moves, type);
if (!Blind)
pline_The("engraving now reads: \"%s\".", buf);
ptext = FALSE;
}
if (zapwand && (otmp->spe < 0)) {
pline("%s %sturns to dust.", The(xname(otmp)),
Blind ? "" : "glows violently, then ");
if (!IS_GRAVE(levl[u.ux][u.uy].typ))
You(
"are not going to get anywhere trying to write in the %s with your dust.",
frosted ? "frost" : "dust");
useup(otmp);
otmp = 0; /* wand is now gone */
ptext = FALSE;
}
/* Early exit for some implements. */
if (!ptext) {
if (otmp && otmp->oclass == WAND_CLASS && !can_reach_floor(TRUE))
cant_reach_floor(u.ux, u.uy, FALSE, TRUE);
return ECMD_TIME;
}
/*
* Special effects should have deleted the current engraving (if
* possible) by now.
*/
if (oep) {
char c = 'n';
/* Give player the choice to add to engraving. */
if (type == HEADSTONE) {
/* no choice, only append */
c = 'y';
} else if (type == oep->engr_type
&& (!Blind || oep->engr_type == BURN
|| oep->engr_type == ENGRAVE)) {
c = yn_function("Do you want to add to the current engraving?",
ynqchars, 'y', TRUE);
if (c == 'q') {
pline1(Never_mind);
return ECMD_OK;
}
}
if (c == 'n' || Blind) {
if (oep->engr_type == DUST
|| oep->engr_type == ENGR_BLOOD
|| oep->engr_type == MARK) {
if (!Blind) {
You("wipe out the message that was %s here.",
(oep->engr_type == DUST)
? (frosted
? "written in the frost"