Files
nethack/src/engrave.c
2015-04-23 23:04:35 -04:00

1243 lines
32 KiB
C

/* NetHack 3.5 engrave.c $NHDT-Date$ $NHDT-Branch$:$NHDT-Revision$ */
/* NetHack 3.5 engrave.c $Date: 2012/12/20 01:48:36 $ $Revision: 1.39 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
#include "lev.h"
#include <ctype.h>
STATIC_VAR NEARDATA struct engr *head_engr;
char *
random_engraving(outbuf)
char *outbuf;
{
const char *rumor;
/* a random engraving may come from the "rumors" file,
or from the list above */
if (!rn2(4) || !(rumor = getrumor(0, outbuf, TRUE)) || !*rumor) {
char buf[BUFSZ];
Strcpy(outbuf, get_rnd_text(ENGRAVEFILE, buf));
}
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"}
};
void
wipeout_text(engr, cnt, seed)
char *engr;
int cnt;
unsigned seed; /* for semi-controlled randomization */
{
char *s;
int i, j, nxt, use_rubout, lth = (int)strlen(engr);
if (lth && cnt > 0) {
while (cnt--) {
/* pick next character */
if (!seed) {
/* random */
nxt = rn2(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 (index("?.,'`-|_", *s)) {
*s = ' ';
continue;
}
if (!use_rubout)
i = SIZE(rubouts);
else
for (i = 0; i < SIZE(rubouts); i++)
if (*s == rubouts[i].wipefrom) {
/*
* Pick one of the substitutes at random.
*/
if (!seed)
j = rn2(strlen(rubouts[i].wipeto));
else {
seed *= 31, seed %= (BUFSZ-1);
j = seed % (strlen(rubouts[i].wipeto));
}
*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(check_pit)
boolean check_pit;
{
struct trap *t;
if (u.uswallow) return FALSE;
/* Restricted/unskilled riders can't reach the floor */
if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) return FALSE;
if (check_pit && (t = t_at(u.ux, u.uy)) != 0 &&
uteetering_at_seen_pit(t) && !Flying)
return FALSE;
return (boolean)((!Levitation ||
Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)) &&
(!u.uundetected || !is_hider(youmonst.data) ||
u.umonnum == PM_TRAPPER));
}
/* give a message after caller has determined that hero can't reach */
void
cant_reach_floor(x, y, up, check_pit)
int x, y;
boolean up, 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(x, y)
register int x, y;
{
register struct rm *lev = &levl[x][y];
if ((x == u.ux) && (y == u.uy) && u.uswallow &&
is_animal(u.ustuck->data))
return "maw";
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" : "water";
else if (is_ice(x,y))
return "ice";
else if (is_lava(x,y))
return "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(x, y)
register int x, y;
{
register 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 above";
return what;
}
struct engr *
engr_at(x, y)
xchar x, 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 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 first word in the engraving.
* ("Elbereth burrito" matches; "o Elbereth" does not.)
*/
int
sengr_at(s, x, y, strict)
const char *s;
xchar x, y;
boolean strict;
{
register struct engr *ep = engr_at(x,y);
if (ep && ep->engr_type != HEADSTONE && ep->engr_time <= moves) {
return strict ? (strncmpi(ep->engr_txt, s, strlen(s)) == 0) :
(strstri(ep->engr_txt, s) != 0);
}
return FALSE;
}
void
u_wipe_engr(cnt)
register int cnt;
{
if (can_reach_floor(TRUE))
wipe_engr_at(u.ux, u.uy, cnt);
}
void
wipe_engr_at(x,y,cnt)
register xchar x,y,cnt;
{
register struct engr *ep = engr_at(x,y);
/* Headstones are indelible */
if(ep && ep->engr_type != HEADSTONE){
if(ep->engr_type != BURN || is_ice(x,y)) {
if(ep->engr_type != DUST && ep->engr_type != ENGR_BLOOD) {
cnt = rn2(1 + 50/(cnt+1)) ? 0 : 1;
}
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(x,y)
register int x,y;
{
register struct engr *ep = engr_at(x,y);
register int sensed = 0;
char buf[BUFSZ];
/* 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;
unsigned maxelen = BUFSZ - sizeof("You feel the words: \"\". ");
if (strlen(ep->engr_txt) > maxelen) {
(void) strncpy(buf, ep->engr_txt, (int)maxelen);
buf[maxelen] = '\0';
et = buf;
} else
et = ep->engr_txt;
You("%s: \"%s\".",
(Blind) ? "feel the words" : "read", et);
if(context.run > 1) nomul(0);
}
}
}
void
make_engr_at(x,y,s,e_time,e_type)
register int x,y;
register const char *s;
register long e_time;
register xchar e_type;
{
register struct engr *ep;
if ((ep = engr_at(x,y)) != 0)
del_engr(ep);
ep = newengr(strlen(s) + 1);
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);
/* engraving Elbereth shows wisdom */
if (!in_mklev && !strcmp(s, "Elbereth")) exercise(A_WIS, TRUE);
ep->engr_time = e_time;
ep->engr_type = e_type > 0 ? e_type : rnd(N_ENGRAVE-1);
ep->engr_lth = strlen(s) + 1;
}
/* delete any engraving at location <x,y> */
void
del_engr_at(x, y)
int x, y;
{
register struct engr *ep = engr_at(x, y);
if (ep) del_engr(ep);
}
/*
* freehand - returns true if player has a free hand
*/
int
freehand()
{
return(!uwep || !welded(uwep) ||
(!bimanual(uwep) && (!uarms || !uarms->cursed)));
/* if ((uwep && bimanual(uwep)) ||
(uwep && uarms))
return(0);
else
return(1);*/
}
static NEARDATA const char styluses[] =
{ ALL_CLASSES, ALLOW_NONE, TOOL_CLASS, WEAPON_CLASS, WAND_CLASS,
GEM_CLASS, RING_CLASS, 0 };
/* 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 a 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
*/
/* return 1 if action took 1 (or more) moves, 0 if error or aborted */
int
doengrave()
{
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 */
xchar type = DUST; /* Type of engraving made */
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 */
int len; /* # of nonspace chars of new engraving text */
int maxelen; /* Max allowable length of 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;
multi = 0; /* moves consumed */
nomovemsg = (char *)0; /* occupation end message */
buf[0] = (char)0;
ebuf[0] = (char)0;
post_engr_text[0] = (char)0;
maxelen = BUFSZ - 1;
if (is_demon(youmonst.data) || youmonst.data->mlet == S_VAMPIRE)
type = ENGR_BLOOD;
/* Can the adventurer engrave at all? */
if(u.uswallow) {
if (is_animal(u.ustuck->data)) {
pline("What would you write? \"Jonah was here\"?");
return(0);
} else if (is_whirly(u.ustuck->data)) {
cant_reach_floor(u.ux, u.uy, FALSE, FALSE);
return(0);
} else
jello = TRUE;
} else if (is_lava(u.ux, u.uy)) {
You_cant("write on the %s!", surface(u.ux, u.uy));
return(0);
} 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(0);
}
if(Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)/* in bubble */) {
You_cant("write in thin air!");
return(0);
} else if (!accessible(u.ux, u.uy)) {
/* stone, tree, wall, secret corridor, pool, lava, bars */
You_cant("write here.");
return 0;
}
if (cantwield(youmonst.data)) {
You_cant("even hold anything!");
return(0);
}
if (check_capacity((char *)0)) return (0);
/* 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(styluses, "write with");
if(!otmp) return(0); /* otmp == zeroobj if fingers */
if (otmp == &zeroobj) {
Strcat(strcpy(fbuf, "your "), makeplural(body_part(FINGER)));
writer = fbuf;
} else writer = yname(otmp);
/* 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(0);
}
if (jello) {
You("tickle %s with %s.", mon_nam(u.ustuck), writer);
Your("message dissolves...");
return(0);
}
if (otmp->oclass != WAND_CLASS && !can_reach_floor(TRUE)) {
cant_reach_floor(u.ux, u.uy, FALSE, TRUE);
return(0);
}
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(0);
}
if (IS_GRAVE(levl[u.ux][u.uy].typ)) {
if (otmp == &zeroobj) { /* using only finger */
You("would only make a small smudge on the %s.",
surface(u.ux, u.uy));
return(0);
} else if (!levl[u.ux][u.uy].disturbed) {
You("disturb the undead!");
levl[u.ux][u.uy].disturbed = 1;
(void) makemon(&mons[PM_GHOUL], u.ux, u.uy, NO_MM_FLAGS);
exercise(A_WIS, FALSE);
return(1);
}
}
/* 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;
}
/* fall through */
/* 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),
is_ice(u.ux,u.uy) ? "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 1;
}
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 = (xchar)0; /* random */
(void) random_engraving(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;
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 (flags.verbose)
pline("This %s is a wand of digging!",
xname(otmp));
doknown = TRUE;
}
if (!Blind)
Strcpy(post_engr_text,
IS_GRAVE(levl[u.ux][u.uy].typ) ?
"Chips fly out from the headstone." :
is_ice(u.ux,u.uy) ?
"Ice chips fly up from the ice surface!" :
"Gravel flies up from the floor.");
else
Strcpy(post_engr_text, "You hear drilling!");
break;
/* type = BURN wands */
case WAN_FIRE:
ptext = TRUE;
type = BURN;
if(!objects[otmp->otyp].oc_name_known) {
if (flags.verbose)
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 (flags.verbose)
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, "You hear crackling!");
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_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(0);
}
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 (!Blind)
You("wipe out the message here.");
else
pline("%s %s.", Yobjnam2(otmp, "get"),
is_ice(u.ux,u.uy) ?
"frosty" : "dusty");
dengr = TRUE;
} else
pline("%s can't wipe out this engraving.",
Yname2(otmp));
else
pline("%s %s.", Yobjnam2(otmp, "get"),
is_ice(u.ux,u.uy) ? "frosty" : "dusty");
break;
default:
break;
}
break;
case VENOM_CLASS:
if (wizard) {
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] = (char)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, moves, type);
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.",
is_ice(u.ux,u.uy) ? "frost" : "dust");
useup(otmp);
otmp = 0; /* wand is now gone */
ptext = FALSE;
}
if (!ptext) { /* Early exit for some implements. */
if (otmp && otmp->oclass == WAND_CLASS && !can_reach_floor(TRUE))
cant_reach_floor(u.ux, u.uy, FALSE, TRUE);
return(1);
}
/* Special effects should have deleted the current engraving (if
* possible) by now.
*/
if (oep) {
register 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');
if (c == 'q') {
pline1(Never_mind);
return(0);
}
}
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) ? "written in the dust" :
((oep->engr_type == ENGR_BLOOD) ? "scrawled in blood" :
"written")));
del_engr(oep);
oep = (struct engr *)0;
} else
/* Don't delete engr until after we *know* we're engraving */
eow = TRUE;
} else
if ( (type == DUST) || (type == MARK) || (type == ENGR_BLOOD) ) {
You(
"cannot wipe out the message that is %s the %s here.",
oep->engr_type == BURN ?
(is_ice(u.ux,u.uy) ? "melted into" : "burned into") :
"engraved in", surface(u.ux,u.uy));
return(1);
} else
if ( (type != oep->engr_type) || (c == 'n') ) {
if (!Blind || can_reach_floor(TRUE))
You("will overwrite the current message.");
eow = TRUE;
}
}
}
eloc = surface(u.ux,u.uy);
switch(type){
default:
everb = (oep && !eow ? "add to the weird writing on" :
"write strangely on");
break;
case DUST:
everb = (oep && !eow ? "add to the writing in" :
"write in");
eloc = is_ice(u.ux,u.uy) ? "frost" : "dust";
break;
case HEADSTONE:
everb = (oep && !eow ? "add to the epitaph on" :
"engrave on");
break;
case ENGRAVE:
everb = (oep && !eow ? "add to the engraving in" :
"engrave in");
break;
case BURN:
everb = (oep && !eow ?
( is_ice(u.ux,u.uy) ? "add to the text melted into" :
"add to the text burned into") :
( is_ice(u.ux,u.uy) ? "melt into" : "burn into"));
break;
case MARK:
everb = (oep && !eow ? "add to the graffiti on" :
"scribble on");
break;
case ENGR_BLOOD:
everb = (oep && !eow ? "add to the scrawl on" :
"scrawl on");
break;
}
/* Tell adventurer what is going on */
if (otmp != &zeroobj)
You("%s the %s with %s.", everb, eloc, doname(otmp));
else
You("%s the %s with your %s.", everb, eloc,
makeplural(body_part(FINGER)));
/* Prompt for engraving! */
Sprintf(qbuf,"What do you want to %s the %s here?", everb, eloc);
getlin(qbuf, ebuf);
/* Count the actual # of chars engraved not including spaces */
len = strlen(ebuf);
for (sp = ebuf; *sp; sp++) if (isspace(*sp)) len -= 1;
if (len == 0 || index(ebuf, '\033')) {
if (zapwand) {
if (!Blind)
pline("%s, then %s.",
Tobjnam(otmp, "glow"), otense(otmp, "fade"));
return(1);
} else {
pline1(Never_mind);
return(0);
}
}
/* A single `x' is the traditional signature of an illiterate person */
if (len != 1 || (!index(ebuf, 'x') && !index(ebuf, 'X')))
u.uconduct.literate++;
/* Mix up engraving if surface or state of mind is unsound.
Note: this won't add or remove any spaces. */
for (sp = ebuf; *sp; sp++) {
if (isspace(*sp)) continue;
if (((type == DUST || type == ENGR_BLOOD) && !rn2(25)) ||
(Blind && !rn2(11)) || (Confusion && !rn2(7)) ||
(Stunned && !rn2(4)) || (Hallucination && !rn2(2)))
*sp = ' ' + rnd(96 - 2); /* ASCII '!' thru '~'
(excludes ' ' and DEL) */
}
/* Previous engraving is overwritten */
if (eow) {
del_engr(oep);
oep = (struct engr *)0;
}
/* Figure out how long it took to engrave, and if player has
* engraved too much.
*/
switch(type){
default:
multi = -(len/10);
if (multi) nomovemsg = "You finish your weird engraving.";
break;
case DUST:
multi = -(len/10);
if (multi) nomovemsg = "You finish writing in the dust.";
break;
case HEADSTONE:
case ENGRAVE:
multi = -(len/10);
if ((otmp->oclass == WEAPON_CLASS) &&
((otmp->otyp != ATHAME) || otmp->cursed)) {
multi = -len;
maxelen = ((otmp->spe + 3) * 2) + 1;
/* -2 = 3, -1 = 5, 0 = 7, +1 = 9, +2 = 11
* Note: this does not allow a +0 anything (except
* an athame) to engrave "Elbereth" all at once.
* However, you could now engrave "Elb", then
* "ere", then "th".
*/
pline("%s dull.", Yobjnam2(otmp, "get"));
costly_alteration(otmp, COST_DEGRD);
if (len > maxelen) {
multi = -maxelen;
otmp->spe = -3;
} else if (len > 1)
otmp->spe -= len >> 1;
else otmp->spe -= 1; /* Prevent infinite engraving */
} else
if ( (otmp->oclass == RING_CLASS) ||
(otmp->oclass == GEM_CLASS) )
multi = -len;
if (multi) nomovemsg = "You finish engraving.";
break;
case BURN:
multi = -(len/10);
if (multi)
nomovemsg = is_ice(u.ux,u.uy) ?
"You finish melting your message into the ice.":
"You finish burning your message into the floor.";
break;
case MARK:
multi = -(len/10);
if ((otmp->oclass == TOOL_CLASS) &&
(otmp->otyp == MAGIC_MARKER)) {
maxelen = (otmp->spe) * 2; /* one charge / 2 letters */
if (len > maxelen) {
Your("marker dries out.");
otmp->spe = 0;
multi = -(maxelen/10);
} else
if (len > 1) otmp->spe -= len >> 1;
else otmp->spe -= 1; /* Prevent infinite grafitti */
}
if (multi) nomovemsg = "You finish defacing the dungeon.";
break;
case ENGR_BLOOD:
multi = -(len/10);
if (multi) nomovemsg = "You finish scrawling.";
break;
}
/* Chop engraving down to size if necessary */
if (len > maxelen) {
for (sp = ebuf; (maxelen && *sp); sp++)
if (!isspace(*sp)) maxelen--;
if (!maxelen && *sp) {
*sp = (char)0;
if (multi) nomovemsg = "You cannot write any more.";
You("are only able to write \"%s\".", ebuf);
}
}
/* Add to existing engraving */
if (oep) Strcpy(buf, oep->engr_txt);
(void) strncat(buf, ebuf, (BUFSZ - (int)strlen(buf) - 1));
make_engr_at(u.ux, u.uy, buf, (moves - multi), type);
if (post_engr_text[0]) pline1(post_engr_text);
if (doblind && !resists_blnd(&youmonst)) {
You("are blinded by the flash!");
make_blinded((long)rnd(50),FALSE);
if (!Blind) Your1(vision_clears);
}
return(1);
}
/* while loading bones, clean up text which might accidentally
or maliciously disrupt player's terminal when displayed */
void
sanitize_engravings()
{
struct engr *ep;
for (ep = head_engr; ep; ep = ep->nxt_engr) {
sanitize_name(ep->engr_txt);
}
}
void
save_engravings(fd, mode)
int fd, mode;
{
register struct engr *ep = head_engr;
register struct engr *ep2;
unsigned no_more_engr = 0;
while (ep) {
ep2 = ep->nxt_engr;
if (ep->engr_lth && ep->engr_txt[0] && perform_bwrite(mode)) {
bwrite(fd, (genericptr_t)&(ep->engr_lth), sizeof(ep->engr_lth));
bwrite(fd, (genericptr_t)ep, sizeof(struct engr) + ep->engr_lth);
}
if (release_data(mode))
dealloc_engr(ep);
ep = ep2;
}
if (perform_bwrite(mode))
bwrite(fd, (genericptr_t)&no_more_engr, sizeof no_more_engr);
if (release_data(mode))
head_engr = 0;
}
void
rest_engravings(fd)
int fd;
{
register struct engr *ep;
unsigned lth;
head_engr = 0;
while(1) {
mread(fd, (genericptr_t) &lth, sizeof(unsigned));
if(lth == 0) return;
ep = newengr(lth);
mread(fd, (genericptr_t) ep, sizeof(struct engr) + lth);
ep->nxt_engr = head_engr;
head_engr = ep;
ep->engr_txt = (char *) (ep + 1); /* Andreas Bormann */
/* mark as finished for bones levels -- no problem for
* normal levels as the player must have finished engraving
* to be able to move again */
ep->engr_time = moves;
}
}
void
del_engr(ep)
register struct engr *ep;
{
if (ep == head_engr) {
head_engr = ep->nxt_engr;
} else {
register struct engr *ept;
for (ept = head_engr; ept; ept = ept->nxt_engr)
if (ept->nxt_engr == ep) {
ept->nxt_engr = ep->nxt_engr;
break;
}
if (!ept) {
impossible("Error in del_engr?");
return;
}
}
dealloc_engr(ep);
}
/* randomly relocate an engraving */
void
rloc_engr(ep)
struct engr *ep;
{
int tx, ty, tryct = 200;
do {
if (--tryct < 0) return;
tx = rn1(COLNO-3,2);
ty = rn2(ROWNO);
} while (engr_at(tx, ty) ||
!goodpos(tx, ty, (struct monst *)0, 0));
ep->engr_x = tx;
ep->engr_y = ty;
}
/* Create a headstone at the given location.
* The caller is responsible for newsym(x, y).
*/
void
make_grave(x, y, str)
int x, y;
const char *str;
{
/* Can we put a grave here? */
if ((levl[x][y].typ != ROOM && levl[x][y].typ != GRAVE) || t_at(x,y)) return;
/* Make the grave */
levl[x][y].typ = GRAVE;
/* Engrave the headstone */
del_engr_at(x, y);
if (str) make_engr_at(x, y, str, 0L, HEADSTONE);
else {
char buf[BUFSZ];
make_engr_at(x, y, get_rnd_text(EPITAPHFILE, buf), 0L, HEADSTONE);
}
return;
}
/*engrave.c*/