Merge branch 'master' into win32-x64-working

* master: (354 commits)
  Add missing protos
  a warning bout lc_error
  Add S_poisoncloud to Guidebooks
  ...

Conflicts:
	.gitattributes
	dat/.gitattributes
	doc/.gitattributes
	doc/Guidebook.mn
	include/config.h
	include/decl.h
	include/extern.h
	include/flag.h
	include/hack.h
	include/ntconf.h
	include/sys.h
	include/wceconf.h
	src/apply.c
	src/attrib.c
	src/bones.c
	src/botl.c
	src/dbridge.c
	src/dig.c
	src/do.c
	src/do_name.c
	src/dog.c
	src/dungeon.c
	src/eat.c
	src/end.c
	src/files.c
	src/fountain.c
	src/hack.c
	src/invent.c
	src/light.c
	src/makemon.c
	src/mhitu.c
	src/mklev.c
	src/mkmaze.c
	src/mkobj.c
	src/mkroom.c
	src/mon.c
	src/objnam.c
	src/options.c
	src/pager.c
	src/pickup.c
	src/potion.c
	src/pray.c
	src/questpgr.c
	src/read.c
	src/restore.c
	src/rnd.c
	src/role.c
	src/rumors.c
	src/save.c
	src/shk.c
	src/sit.c
	src/sp_lev.c
	src/sys.c
	src/teleport.c
	src/trap.c
	src/u_init.c
	src/uhitm.c
	src/wield.c
	src/worn.c
	src/zap.c
	sys/amiga/.gitattributes
	sys/mac/.gitattributes
	sys/msdos/.gitattributes
	sys/msdos/pctiles.c
	sys/msdos/vidvga.c
	sys/os2/.gitattributes
	sys/share/.gitattributes
	sys/share/pcmain.c
	sys/unix/.gitattributes
	sys/unix/hints/.gitattributes
	sys/unix/sysconf
	sys/unix/unixmain.c
	sys/vms/.gitattributes
	sys/wince/.gitattributes
	sys/wince/mhstatus.c
	sys/winnt/.gitattributes
	sys/winnt/Makefile.msc
	sys/winnt/nhsetup.bat
	util/lev_comp.l
	util/makedefs.c
	win/X11/winmenu.c
	win/X11/winstat.c
	win/gnome/gnstatus.c
	win/share/tilemap.c
	win/tty/termcap.c
	win/tty/topl.c
	win/tty/wintty.c
This commit is contained in:
Derek S. Ray
2015-04-05 23:42:15 -04:00
197 changed files with 29181 additions and 15618 deletions

View File

@@ -1,5 +1,4 @@
/* NetHack 3.5 trap.c $NHDT-Date$ $NHDT-Branch$:$NHDT-Revision$ */
/* NetHack 3.5 trap.c $Date: 2013/03/14 01:58:21 $ $Revision: 1.179 $ */
/* NetHack 3.5 trap.c $NHDT-Date: 1427934551 2015/04/02 00:29:11 $ $NHDT-Branch: master $:$NHDT-Revision: 1.223 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
@@ -55,7 +54,7 @@ struct monst *victim;
int mat_idx;
if (!victim) return 0;
#define burn_dmg(obj,descr) rust_dmg(obj, descr, 0, FALSE, victim)
#define burn_dmg(obj,descr) erode_obj(obj, descr, ERODE_BURN, EF_GREASE)
while (1) {
switch (rn2(5)) {
case 0:
@@ -101,100 +100,168 @@ struct monst *victim;
#undef burn_dmg
}
/* Generic rust-armor function. Returns TRUE if a message was printed;
* "print", if set, means to print a message (and thus to return TRUE) even
* if the item could not be rusted; otherwise a message is printed and TRUE is
* returned only for rustable items.
/* Generic erode-item function.
* "ostr", if non-null, is an alternate string to print instead of the
* object's name.
* "type" is an ERODE_* value for the erosion type
* "flags" is an or-ed list of EF_* flags
*
* Returns an erosion return value (ER_*)
*/
boolean
rust_dmg(otmp, ostr, type, print, victim)
int
erode_obj(otmp, ostr, type, ef_flags)
register struct obj *otmp;
register const char *ostr;
int type;
boolean print;
struct monst *victim;
int ef_flags;
{
static NEARDATA const char * const action[] = { "smoulder", "rust", "rot", "corrode" };
static NEARDATA const char * const msg[] = { "burnt", "rusted", "rotten", "corroded" };
boolean vulnerable = FALSE;
boolean grprot = FALSE;
boolean is_primary = TRUE;
boolean vismon = (victim != &youmonst) && canseemon(victim);
boolean check_grease = ef_flags & EF_GREASE;
boolean print = ef_flags & EF_VERBOSE;
int erosion;
struct monst *victim;
boolean vismon;
boolean visobj;
int cost_type;
if (!otmp) return ER_NOTHING;
victim = carried(otmp) ? &youmonst : mcarried(otmp) ?
otmp->ocarry : NULL;
vismon = victim && (victim != &youmonst) && canseemon(victim);
/* Is bhitpos correct here? Ugh. */
visobj = !victim && cansee(bhitpos.x, bhitpos.y);
if (!otmp) return(FALSE);
switch(type) {
case 0: vulnerable = is_flammable(otmp);
break;
case 1: vulnerable = is_rustprone(otmp);
grprot = TRUE;
break;
case 2: vulnerable = is_rottable(otmp);
is_primary = FALSE;
break;
case 3: vulnerable = is_corrodeable(otmp);
grprot = TRUE;
is_primary = FALSE;
break;
case ERODE_BURN:
vulnerable = is_flammable(otmp);
check_grease = FALSE;
cost_type = COST_BURN;
break;
case ERODE_RUST:
vulnerable = is_rustprone(otmp);
cost_type = COST_RUST;
break;
case ERODE_ROT:
vulnerable = is_rottable(otmp);
check_grease = FALSE;
is_primary = FALSE;
cost_type = COST_ROT;
break;
case ERODE_CORRODE:
vulnerable = is_corrodeable(otmp);
is_primary = FALSE;
cost_type = COST_CORRODE;
break;
default:
impossible("Invalid erosion type in erode_obj");
return ER_NOTHING;
}
erosion = is_primary ? otmp->oeroded : otmp->oeroded2;
if (!print && (!vulnerable || otmp->oerodeproof || erosion == MAX_ERODE))
return FALSE;
if (!ostr)
ostr = cxname(otmp);
if (!vulnerable) {
if (flags.verbose) {
if (check_grease && otmp->greased) {
grease_protect(otmp, ostr, victim);
return ER_GREASED;
} else if (!vulnerable || (otmp->oerodeproof && otmp->rknown)) {
if (print && flags.verbose) {
if (victim == &youmonst)
Your("%s %s not affected.", ostr, vtense(ostr, "are"));
else if (vismon)
pline("%s's %s %s not affected.", Monnam(victim), ostr,
vtense(ostr, "are"));
pline("%s %s %s not affected.", s_suffix(Monnam(victim)),
ostr, vtense(ostr, "are"));
}
} else if (erosion < MAX_ERODE) {
if (grprot && otmp->greased) {
grease_protect(otmp,ostr,victim);
} else if (otmp->oerodeproof || (otmp->blessed && !rnl(4))) {
if (flags.verbose) {
if (victim == &youmonst)
pline("Somehow, your %s %s not affected.",
ostr, vtense(ostr, "are"));
else if (vismon)
pline("Somehow, %s's %s %s not affected.",
mon_nam(victim), ostr, vtense(ostr, "are"));
}
} else {
return ER_NOTHING;
} else if (otmp->oerodeproof || (otmp->blessed && !rnl(4))) {
if (flags.verbose && (print || otmp->oerodeproof)) {
if (victim == &youmonst)
Your("%s %s%s!", ostr,
vtense(ostr, action[type]),
erosion+1 == MAX_ERODE ? " completely" :
erosion ? " further" : "");
pline("Somehow, your %s %s not affected.",
ostr, vtense(ostr, "are"));
else if (vismon)
pline("%s's %s %s%s!", Monnam(victim), ostr,
vtense(ostr, action[type]),
erosion+1 == MAX_ERODE ? " completely" :
erosion ? " further" : "");
if (is_primary)
otmp->oeroded++;
else
otmp->oeroded2++;
update_inventory();
}
} else {
if (flags.verbose) {
pline("Somehow, %s %s %s not affected.",
s_suffix(mon_nam(victim)), ostr,
vtense(ostr, "are"));
else if (visobj)
pline("Somehow, the %s %s not affected.", ostr,
vtense(ostr, "are"));
}
/* We assume here that if the object is protected because it
* is blessed, it still shows some minor signs of wear, and
* the hero can distinguish this from an object that is
* actually proof against damage. */
if (otmp->oerodeproof) {
otmp->rknown = TRUE;
if (victim == &youmonst)
update_inventory();
}
return ER_NOTHING;
} else if (erosion < MAX_ERODE) {
const char *adverb = (erosion + 1 == MAX_ERODE) ?
" completely" : erosion ? " further" : "";
if (victim == &youmonst)
Your("%s %s%s!", ostr, vtense(ostr, action[type]), adverb);
else if (vismon)
pline("%s %s %s%s!", s_suffix(Monnam(victim)), ostr,
vtense(ostr, action[type]), adverb);
else if (visobj)
pline("The %s %s%s!", ostr, vtense(ostr, action[type]), adverb);
if (ef_flags & EF_PAY)
costly_alteration(otmp, cost_type);
if (is_primary)
otmp->oeroded++;
else
otmp->oeroded2++;
if (victim == &youmonst)
update_inventory();
return ER_DAMAGED;
} else if (ef_flags & EF_DESTROY) {
if (victim == &youmonst)
Your("%s %s away!", ostr, vtense(ostr, action[type]));
else if (vismon)
pline("%s %s %s away!", s_suffix(Monnam(victim)), ostr,
vtense(ostr, action[type]));
else if (visobj)
pline("The %s %s away!", ostr, vtense(ostr, action[type]));
if (ef_flags & EF_PAY)
costly_alteration(otmp, cost_type);
setnotworn(otmp);
delobj(otmp);
return ER_DESTROYED;
} else {
if (flags.verbose && print) {
if (victim == &youmonst)
Your("%s %s completely %s.", ostr,
vtense(ostr, Blind ? "feel" : "look"),
msg[type]);
else if (vismon)
pline("%s's %s %s completely %s.",
Monnam(victim), ostr,
pline("%s %s %s completely %s.",
s_suffix(Monnam(victim)), ostr,
vtense(ostr, "look"), msg[type]);
else if (visobj)
pline("The %s %s completely %s.", ostr,
vtense(ostr, "look"), msg[type]);
}
return ER_NOTHING;
}
return(TRUE);
}
void
/* Protect an item from erosion with grease. Returns TRUE if the grease
* wears off.
*/
boolean
grease_protect(otmp,ostr,victim)
register struct obj *otmp;
register const char *ostr;
@@ -218,7 +285,9 @@ struct monst *victim;
pline_The("grease dissolves.");
update_inventory();
}
return TRUE;
}
return FALSE;
}
struct trap *
@@ -551,7 +620,7 @@ int *fail_reason;
"statue");
pline("%s %s!", upstart(statuename), comes_to_life);
} else if (Hallucination) { /* They don't know it's a statue */
pline_The("%s suddenly seems more animated.", rndmonnam());
pline_The("%s suddenly seems more animated.", rndmonnam(NULL));
} else if (cause == ANIMATE_SHATTER) {
if (cansee(x, y))
Sprintf(statuename, "%s%s", shk_your(tmpbuf, statue),
@@ -876,6 +945,7 @@ unsigned trflags;
} else {
pline("%s bear trap closes on your %s!",
A_Your[trap->madeby_u], body_part(FOOT));
set_wounded_legs(rn2(2) ? RIGHT_SIDE : LEFT_SIDE, rn1(10,10));
if(u.umonnum == PM_OWLBEAR || u.umonnum == PM_BUGBEAR)
You("howl in anger!");
losehp(Maybe_Half_Phys(dmg), "bear trap", KILLED_BY_AN);
@@ -897,18 +967,6 @@ unsigned trflags;
case RUST_TRAP:
seetrap(trap);
if (u.umonnum == PM_IRON_GOLEM) {
int dam = u.mhmax;
pline("%s you!", A_gush_of_water_hits);
You("are covered with rust!");
losehp(Maybe_Half_Phys(dam), "rusting away", KILLED_BY);
break;
} else if (u.umonnum == PM_GREMLIN && rn2(3)) {
pline("%s you!", A_gush_of_water_hits);
(void)split_mon(&youmonst, (struct monst *)0);
break;
}
/* Unlike monsters, traps cannot aim their rust attacks at
* you, so instead of looping through and taking either the
@@ -919,18 +977,18 @@ unsigned trflags;
case 0:
pline("%s you on the %s!", A_gush_of_water_hits,
body_part(HEAD));
(void) rust_dmg(uarmh, helm_simple_name(uarmh),
1, TRUE, &youmonst);
(void) water_damage(uarmh, helm_simple_name(uarmh),
TRUE);
break;
case 1:
pline("%s your left %s!", A_gush_of_water_hits,
body_part(ARM));
if (rust_dmg(uarms, "shield", 1, TRUE, &youmonst))
if (water_damage(uarms, "shield", TRUE) != ER_NOTHING)
break;
if (u.twoweap || (uwep && bimanual(uwep)))
(void) erode_obj(u.twoweap ? uswapwep : uwep,
1, TRUE, FALSE);
glovecheck: (void) rust_dmg(uarmg, "gauntlets", 1, TRUE, &youmonst);
(void) water_damage(u.twoweap ? uswapwep : uwep, 0,
TRUE);
glovecheck: (void) water_damage(uarmg, "gauntlets", TRUE);
/* Not "metal gauntlets" since it gets called
* even if it's leather for the message
*/
@@ -938,7 +996,7 @@ glovecheck: (void) rust_dmg(uarmg, "gauntlets", 1, TRUE, &youmonst);
case 2:
pline("%s your right %s!", A_gush_of_water_hits,
body_part(ARM));
(void) erode_obj(uwep, 1, TRUE, FALSE);
(void) water_damage(uwep, 0, TRUE);
goto glovecheck;
default:
pline("%s you!", A_gush_of_water_hits);
@@ -947,14 +1005,26 @@ glovecheck: (void) rust_dmg(uarmg, "gauntlets", 1, TRUE, &youmonst);
(otmp != uswapwep || !u.twoweap))
(void) snuff_lit(otmp);
if (uarmc)
(void) rust_dmg(uarmc, cloak_simple_name(uarmc),
1, TRUE, &youmonst);
(void) water_damage(uarmc, cloak_simple_name(uarmc),
TRUE);
else if (uarm)
(void) rust_dmg(uarm, "armor", 1, TRUE, &youmonst);
(void) water_damage(uarm, "armor", TRUE);
else if (uarmu)
(void) rust_dmg(uarmu, "shirt", 1, TRUE, &youmonst);
(void) water_damage(uarmu, "shirt", TRUE);
}
update_inventory();
if (u.umonnum == PM_IRON_GOLEM) {
int dam = u.mhmax;
pline("%s you!", A_gush_of_water_hits);
You("are covered with rust!");
losehp(Maybe_Half_Phys(dam), "rusting away", KILLED_BY);
} else if (u.umonnum == PM_GREMLIN && rn2(3)) {
pline("%s you!", A_gush_of_water_hits);
(void)split_mon(&youmonst, (struct monst *)0);
}
break;
case FIRE_TRAP:
@@ -1336,13 +1406,12 @@ struct obj *otmp;
{
struct monst *steed = u.usteed;
int tt;
boolean in_sight, trapkilled, steedhit;
boolean trapkilled, steedhit;
if (!steed || !trap) return 0;
tt = trap->ttyp;
steed->mx = u.ux;
steed->my = u.uy;
in_sight = !Blind;
trapkilled = steedhit = FALSE;
switch (tt) {
@@ -1992,10 +2061,9 @@ register struct monst *mtmp;
pline("%s stops momentarily and appears to cringe.",
Monnam(mtmp));
}
} else if (!Deaf) {
} else
You_hear("a distant %s squeak.",
trapnote(trap,1));
}
/* wake up nearby monsters */
wake_nearto(mtmp->mx, mtmp->my, 40);
break;
@@ -2010,9 +2078,8 @@ register struct monst *mtmp;
Monnam(mtmp), a_your[trap->madeby_u]);
seetrap(trap);
} else {
if((mptr == &mons[PM_OWLBEAR]
if(mptr == &mons[PM_OWLBEAR]
|| mptr == &mons[PM_BUGBEAR])
&& !Deaf)
You_hear("the roaring of an angry bear!");
}
} else if (force_mintrap) {
@@ -2050,27 +2117,27 @@ register struct monst *mtmp;
pline("%s %s on the %s!", A_gush_of_water_hits,
mon_nam(mtmp), mbodypart(mtmp, HEAD));
target = which_armor(mtmp, W_ARMH);
(void) rust_dmg(target, helm_simple_name(target),
1, TRUE, mtmp);
(void) water_damage(target, helm_simple_name(target),
TRUE);
break;
case 1:
if (in_sight)
pline("%s %s's left %s!", A_gush_of_water_hits,
mon_nam(mtmp), mbodypart(mtmp, ARM));
target = which_armor(mtmp, W_ARMS);
if (rust_dmg(target, "shield", 1, TRUE, mtmp))
if (water_damage(target, "shield", TRUE) != ER_NOTHING)
break;
target = MON_WEP(mtmp);
if (target && bimanual(target))
(void) erode_obj(target, 1, TRUE, FALSE);
(void) water_damage(target, 0, TRUE);
glovecheck: target = which_armor(mtmp, W_ARMG);
(void) rust_dmg(target, "gauntlets", 1, TRUE, mtmp);
(void) water_damage(target, "gauntlets", TRUE);
break;
case 2:
if (in_sight)
pline("%s %s's right %s!", A_gush_of_water_hits,
mon_nam(mtmp), mbodypart(mtmp, ARM));
(void) erode_obj(MON_WEP(mtmp), 1, TRUE, FALSE);
(void) water_damage(MON_WEP(mtmp), 0, TRUE);
goto glovecheck;
default:
if (in_sight)
@@ -2081,13 +2148,13 @@ glovecheck: target = which_armor(mtmp, W_ARMG);
(otmp->owornmask & (W_WEP|W_SWAPWEP)) == 0)
(void) snuff_lit(otmp);
if ((target = which_armor(mtmp, W_ARMC)) != 0)
(void) rust_dmg(target,
cloak_simple_name(target),
1, TRUE, mtmp);
(void) water_damage(target,
cloak_simple_name(target),
TRUE);
else if ((target = which_armor(mtmp, W_ARM)) != 0)
(void) rust_dmg(target, "armor", 1, TRUE, mtmp);
(void) water_damage(target, "armor", TRUE);
else if ((target = which_armor(mtmp, W_ARMU)) != 0)
(void) rust_dmg(target, "shirt", 1, TRUE, mtmp);
(void) water_damage(target, "shirt", TRUE);
}
if (mptr == &mons[PM_IRON_GOLEM]) {
@@ -2853,9 +2920,8 @@ domagictrap()
if (!Blind) Your1(vision_clears);
} else if (!Blind) {
You_see("a flash of light!");
} else if (!Deaf) {
} else
You_hear("a deafening roar!");
}
incr_itimeout(&HDeaf, rn1(20,30));
while(cnt--)
(void) makemon((struct permonst *) 0, u.ux, u.uy, NO_MM_FLAGS);
@@ -2933,207 +2999,294 @@ domagictrap()
}
}
/* Set an item on fire.
* "force" means not to roll a luck-based protection check for the
* item.
* "x" and "y" are the coordinates to dump the contents of a
* container, if it burns up.
*
* Return whether the object was destroyed.
*/
boolean
fire_damage(obj, force, x, y)
struct obj *obj;
boolean force;
xchar x, y;
{
int chance;
struct obj *otmp, *ncobj;
int in_sight = !Blind && couldsee(x, y); /* Don't care if it's lit */
int dindx;
/* object might light in a controlled manner */
if (catch_lit(obj))
return FALSE;
if (Is_container(obj)) {
switch (obj->otyp) {
case ICE_BOX:
return FALSE; /* Immune */
case CHEST:
chance = 40;
break;
case LARGE_BOX:
chance = 30;
break;
default:
chance = 20;
break;
}
if ((!force && (Luck + 5) > rn2(chance)) ||
(is_flammable(obj) && obj->oerodeproof))
return FALSE;
/* Container is burnt up - dump contents out */
if (in_sight) pline("%s catches fire and burns.", Yname2(obj));
if (Has_contents(obj)) {
if (in_sight) pline("Its contents fall out.");
for (otmp = obj->cobj; otmp; otmp = ncobj) {
ncobj = otmp->nobj;
obj_extract_self(otmp);
if (!flooreffects(otmp, x, y, ""))
place_object(otmp, x, y);
}
}
setnotworn(obj);
delobj(obj);
return TRUE;
} else if (!force && (Luck + 5) > rn2(20)) {
/* chance per item of sustaining damage:
* max luck (Luck==13): 10%
* avg luck (Luck==0): 75%
* awful luck (Luck<-4): 100%
*/
return FALSE;
} else if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS) {
if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL)
return FALSE;
if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
if (in_sight) pline("Smoke rises from %s.", the(xname(obj)));
return FALSE;
}
dindx = (obj->oclass == SCROLL_CLASS) ? 3 : 4;
if (in_sight)
pline("%s %s.", Yname2(obj),
destroy_strings[dindx][(obj->quan > 1L)]);
setnotworn(obj);
delobj(obj);
return TRUE;
} else if (obj->oclass == POTION_CLASS) {
dindx = (obj->otyp != POT_OIL) ? 1 : 2;
if (in_sight)
pline("%s %s.", Yname2(obj),
destroy_strings[dindx][(obj->quan > 1L)]);
setnotworn(obj);
delobj(obj);
return TRUE;
} else if (erode_obj(obj, NULL, ERODE_BURN, EF_DESTROY) == ER_DESTROYED) {
return TRUE;
}
return FALSE;
}
/*
* Scrolls, spellbooks, potions, and flammable items
* may get affected by the fire.
* Apply fire_damage() to an entire chain.
*
* Return number of objects destroyed. --ALI
*/
int
fire_damage(chain, force, here, x, y)
fire_damage_chain(chain, force, here, x, y)
struct obj *chain;
boolean force, here;
xchar x, y;
{
int chance;
struct obj *obj, *otmp, *nobj, *ncobj;
int retval = 0;
int in_sight = !Blind && couldsee(x, y); /* Don't care if it's lit */
int dindx;
struct obj *obj, *nobj;
int num = 0;
for (obj = chain; obj; obj = nobj) {
nobj = here ? obj->nexthere : obj->nobj;
/* object might light in a controlled manner */
if (catch_lit(obj))
continue;
if (Is_container(obj)) {
switch (obj->otyp) {
case ICE_BOX:
continue; /* Immune */
/*NOTREACHED*/
break;
case CHEST:
chance = 40;
break;
case LARGE_BOX:
chance = 30;
break;
default:
chance = 20;
break;
}
if ((!force && (Luck + 5) > rn2(chance)) ||
(is_flammable(obj) && obj->oerodeproof))
continue;
/* Container is burnt up - dump contents out */
if (in_sight) pline("%s catches fire and burns.", Yname2(obj));
if (Has_contents(obj)) {
if (in_sight) pline("Its contents fall out.");
for (otmp = obj->cobj; otmp; otmp = ncobj) {
ncobj = otmp->nobj;
obj_extract_self(otmp);
if (!flooreffects(otmp, x, y, ""))
place_object(otmp, x, y);
}
}
delobj(obj);
retval++;
} else if (!force && (Luck + 5) > rn2(20)) {
/* chance per item of sustaining damage:
* max luck (Luck==13): 10%
* avg luck (Luck==0): 75%
* awful luck (Luck<-4): 100%
*/
continue;
} else if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS) {
if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL)
continue;
if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
if (in_sight) pline("Smoke rises from %s.", the(xname(obj)));
continue;
}
dindx = (obj->oclass == SCROLL_CLASS) ? 3 : 4;
if (in_sight)
pline("%s %s.", Yname2(obj),
destroy_strings[dindx][(obj->quan > 1L)]);
delobj(obj);
retval++;
} else if (obj->oclass == POTION_CLASS) {
dindx = (obj->otyp != POT_OIL) ? 1 : 2;
if (in_sight)
pline("%s %s.", Yname2(obj),
destroy_strings[dindx][(obj->quan > 1L)]);
delobj(obj);
retval++;
} else if (is_flammable(obj) && obj->oeroded < MAX_ERODE &&
!(obj->oerodeproof || (obj->blessed && !rnl(4)))) {
if (in_sight) {
pline("%s %s%s.", Yname2(obj), otense(obj, "burn"),
obj->oeroded+1 == MAX_ERODE ? " completely" :
obj->oeroded ? " further" : "");
}
obj->oeroded++;
}
if (fire_damage(obj, force, x, y))
++num;
}
if (retval && !in_sight)
if (num && (Blind && !couldsee(x, y)))
You("smell smoke.");
return retval;
return num;
}
void
water_damage(objp, force, here)
struct obj **objp;
boolean force, here;
acid_damage(obj)
struct obj *obj;
{
register struct obj *obj = *objp, *otmp;
boolean loose_obj = (obj && obj->where == OBJ_FREE), exploded = FALSE;
/* Scrolls but not spellbooks can be erased by acid. */
struct monst *victim;
boolean vismon;
if (loose_obj && (obj->nobj || obj->nexthere)) {
/* [this should actually be a panic()] */
impossible("water_damage: loose object has%s%s%s list%s?",
obj->nobj ? " nobj" : "",
(obj->nobj && obj->nexthere) ? " and" : "",
obj->nexthere ? " nexthere" : "",
(obj->nobj && obj->nexthere) ? "s" : "");
}
if (!obj) return;
/* Scrolls, spellbooks, potions, weapons and
pieces of armor may get affected by the water.
victim = carried(obj) ? &youmonst : mcarried(obj) ? obj->ocarry : NULL;
vismon = victim && (victim != &youmonst) && canseemon(victim);
[FIXME? The item transformations here lack shop price/billing
handling [costly_alteration()], which is okay as long as shops
don't contain any pools. However, that's probably not a valid
assumption; a broken fountain might spill new pools far enough
to put one inside a nearby shop, particularly if dug walls in
Minetown haven't been repaired yet. Note that water_damage()
might be getting called right now because we're in the midst
of creating such a pool, not just because one has already been
created and we've walked into it while carrying unpaid stuff.]
*/
for (; obj; obj = otmp) {
otmp = here ? obj->nexthere : obj->nobj;
(void) snuff_lit(obj);
if(obj->otyp == CAN_OF_GREASE && obj->spe > 0) {
continue;
} else if(obj->greased) {
if (force || !rn2(2)) obj->greased = 0;
} else if(Is_container(obj) && !Is_box(obj) &&
(obj->otyp != OILSKIN_SACK || (obj->cursed && !rn2(3)))) {
water_damage(&obj->cobj, force, FALSE);
} else if (!force && (Luck + 5) > rn2(20)) {
/* chance per item of sustaining damage:
* max luck: 10%
* avg luck (Luck==0): 75%
* awful luck (Luck<-4): 100%
*/
continue;
} else if (obj->oclass == SCROLL_CLASS) {
if (obj->greased) {
grease_protect(obj, NULL, victim);
} else if (obj->oclass == SCROLL_CLASS && obj->otyp != SCR_BLANK_PAPER) {
if (obj->otyp != SCR_BLANK_PAPER
#ifdef MAIL
if (obj->otyp == SCR_MAIL) continue;
&& obj->otyp != SCR_MAIL
#endif
obj->otyp = SCR_BLANK_PAPER;
obj->dknown = 0;
obj->spe = 0;
} else if (obj->oclass == SPBOOK_CLASS) {
if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
pline("Steam rises from %s.", the(xname(obj)));
continue;
}
obj->otyp = SPE_BLANK_PAPER;
obj->dknown = 0;
} else if (obj->oclass == POTION_CLASS) {
if (obj->otyp == POT_ACID) {
char *bufp, buf[BUFSZ];
boolean one = (obj->quan == 1L);
) {
if (!Blind) {
if (victim == &youmonst)
pline("Your %s.", aobjnam(obj, "fade"));
else if (vismon)
pline("%s %s.", s_suffix(Monnam(victim)),
aobjnam(obj, "fade"));
}
}
obj->otyp = SCR_BLANK_PAPER;
obj->spe = 0;
obj->dknown = 0;
} else
erode_obj(obj, NULL, ERODE_CORRODE, EF_GREASE | EF_VERBOSE);
}
bufp = strcpy(buf, "potion");
if (!one) bufp = makeplural(bufp);
/* [should we damage player/monster?] */
pline("%s %s %s!", /* "A potion explodes!" */
!exploded ? (one ? "A" : "Some") :
(one ? "Another" : "More"),
bufp, vtense(bufp, "explode"));
exploded = TRUE;
/* let caller know that obj has gone away
[when obj is part of a list, delobj()'s
obj_extract_self() takes care of this;
for loose_obj, obj should always equal
*objp and otmp should always be null] */
if (loose_obj && obj == *objp) *objp = otmp;
delobj(obj);
continue;
} else if (obj->odiluted) {
obj->otyp = POT_WATER;
obj->dknown = 0;
obj->blessed = obj->cursed = 0;
obj->odiluted = 0;
} else if (obj->otyp != POT_WATER)
obj->odiluted++;
} else if (is_rustprone(obj) && obj->oeroded < MAX_ERODE &&
!(obj->oerodeproof || (obj->blessed && !rnl(4)))) {
/* all metal stuff and armor except (body armor
protected by oilskin cloak) */
if(obj->oclass != ARMOR_CLASS || obj != uarm ||
!uarmc || uarmc->otyp != OILSKIN_CLOAK ||
(uarmc->cursed && !rn2(3)))
obj->oeroded++;
}
}
/* context for water_damage(), managed by water_damage_chain();
when more than one stack of potions of acid explode while processing
a chain of objects, use alternate phrasing after the first message */
static struct h2o_ctx {
int dkn_boom, unk_boom; /* track dknown, !dknown separately */
boolean ctx_valid;
} acid_ctx = { 0, 0, FALSE };
/* Get an object wet and damage it appropriately.
* "ostr", if present, is used instead of the object name in some
* messages.
* "force" means not to roll luck to protect some objects.
* Returns an erosion return value (ER_*)
*/
int
water_damage(obj, ostr, force)
struct obj *obj;
const char *ostr;
boolean force;
{
if (!obj) return ER_NOTHING;
if (snuff_lit(obj))
return ER_DAMAGED;
if(obj->otyp == CAN_OF_GREASE && obj->spe > 0) {
return ER_NOTHING;
} else if(obj->greased) {
if (!rn2(2)) obj->greased = 0;
if (carried(obj)) update_inventory();
return ER_GREASED;
} else if(Is_container(obj) && !Is_box(obj) &&
(obj->otyp != OILSKIN_SACK || (obj->cursed && !rn2(3)))) {
water_damage_chain(obj->cobj, FALSE);
return ER_NOTHING;
} else if (!force && (Luck + 5) > rn2(20)) {
/* chance per item of sustaining damage:
* max luck: 10%
* avg luck (Luck==0): 75%
* awful luck (Luck<-4): 100%
*/
return ER_NOTHING;
} else if (obj->oclass == SCROLL_CLASS) {
#ifdef MAIL
if (obj->otyp == SCR_MAIL) return 0;
#endif
obj->otyp = SCR_BLANK_PAPER;
obj->dknown = 0;
obj->spe = 0;
if (carried(obj))
update_inventory();
return ER_DAMAGED;
} else if (obj->oclass == SPBOOK_CLASS) {
if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
pline("Steam rises from %s.", the(xname(obj)));
return 0;
}
obj->otyp = SPE_BLANK_PAPER;
obj->dknown = 0;
if (carried(obj))
update_inventory();
return ER_DAMAGED;
} else if (obj->oclass == POTION_CLASS) {
if (obj->otyp == POT_ACID) {
char *bufp;
boolean one = (obj->quan == 1L),
update = carried(obj),
exploded = FALSE;
if (Blind && !carried(obj)) obj->dknown = 0;
if (acid_ctx.ctx_valid)
exploded = ((obj->dknown ? acid_ctx.dkn_boom
: acid_ctx.unk_boom) > 0);
/* First message is
* "a [potion|<color> potion|potion of acid] explodes"
* depending on obj->dknown (potion has been seen) and
* objects[POT_ACID].oc_name_known (fully discovered),
* or "some {plural version} explode" when relevant.
* Second and subsequent messages for same chain and
* matching dknown status are
* "another [potion|<color> &c] explodes" or plural
* variant.
*/
bufp = simpleonames(obj);
pline("%s %s %s!", /* "A potion explodes!" */
!exploded ? (one ? "A" : "Some") :
(one ? "Another" : "More"),
bufp, vtense(bufp, "explode"));
if (acid_ctx.ctx_valid) {
if (obj->dknown)
acid_ctx.dkn_boom++;
else
acid_ctx.unk_boom++;
}
setnotworn(obj);
delobj(obj);
if (update)
update_inventory();
return ER_DESTROYED;
} else if (obj->odiluted) {
obj->otyp = POT_WATER;
obj->dknown = 0;
obj->blessed = obj->cursed = 0;
obj->odiluted = 0;
if (carried(obj))
update_inventory();
return ER_DAMAGED;
} else if (obj->otyp != POT_WATER) {
obj->odiluted++;
if (carried(obj))
update_inventory();
return ER_DAMAGED;
}
} else {
return erode_obj(obj, ostr, ERODE_RUST, EF_NONE);
}
return ER_NOTHING;
}
void
water_damage_chain(obj,here)
struct obj *obj;
boolean here;
{
struct obj *otmp;
/* initialize acid context: so far, neither seen (dknown) potions of
acid nor unseen have exploded during this water damage sequence */
acid_ctx.dkn_boom = acid_ctx.unk_boom = 0;
acid_ctx.ctx_valid = TRUE;
for (; obj; obj = otmp) {
otmp = here ? obj->nexthere : obj->nobj;
water_damage(obj, NULL, FALSE);
}
/* reset acid context */
acid_ctx.dkn_boom = acid_ctx.unk_boom = 0;
acid_ctx.ctx_valid = FALSE;
}
/*
@@ -3212,7 +3365,7 @@ drown()
Hallucination ? "the Titanic" : "a rock");
}
water_damage(&invent, FALSE, FALSE);
water_damage_chain(invent, FALSE);
if (u.umonnum == PM_GREMLIN && rn2(3))
(void)split_mon(&youmonst, (struct monst *)0);
@@ -3421,7 +3574,7 @@ boolean bury_it;
place_object(otmp, ttmp->tx, ttmp->ty);
if (bury_it) {
/* magical digging first disarms this trap, then will unearth it */
(void) bury_an_obj(otmp);
(void) bury_an_obj(otmp, NULL);
} else {
/* Sell your own traps only... */
if (ttmp->madeby_u) sellobj(otmp, ttmp->tx, ttmp->ty);
@@ -4293,6 +4446,7 @@ boolean disarm;
if (!Free_action) {
pline("Suddenly you are frozen in place!");
nomul(-d(5, 6));
multi_reason = "frozen by a trap";
exercise(A_DEX, FALSE);
nomovemsg = You_can_move_again;
} else You("momentarily stiffen.");
@@ -4614,6 +4768,9 @@ lava_effects()
} else
You("fall into the lava!");
usurvive = Lifesaved || discover;
if (wizard) usurvive = TRUE;
/* prevent remove_worn_item() -> Boots_off(WATER_WALKING_BOOTS) ->
spoteffects() -> lava_effects() recursion which would
successfully delete (via useupall) the no-longer-worn boots;
@@ -4625,16 +4782,18 @@ lava_effects()
obj2 = obj->nobj;
/* above, we set in_use for objects which are to be destroyed */
if (obj->otyp == SPE_BOOK_OF_THE_DEAD && !Blind) {
pline("%s glows a strange %s, but remains intact.",
The(xname(obj)), hcolor("dark red"));
if (usurvive)
pline("%s glows a strange %s, but remains intact.",
The(xname(obj)), hcolor("dark red"));
} else if (obj->in_use) {
if (obj->owornmask) {
pline("%s into flame!", Yobjnam2(obj, "burst"));
if (usurvive)
pline("%s into flame!", Yobjnam2(obj, "burst"));
remove_worn_item(obj, TRUE);
}
useupall(obj);
}
}
}
iflags.in_lava_effects--;