1339 lines
37 KiB
C
1339 lines
37 KiB
C
/* SCCS Id: @(#)artifact.c 3.3 2001/11/17 */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "hack.h"
|
|
#include "artifact.h"
|
|
#ifdef OVLB
|
|
#include "artilist.h"
|
|
#else
|
|
STATIC_DCL struct artifact artilist[];
|
|
#endif
|
|
/*
|
|
* Note: both artilist[] and artiexist[] have a dummy element #0,
|
|
* so loops over them should normally start at #1. The primary
|
|
* exception is the save & restore code, which doesn't care about
|
|
* the contents, just the total size.
|
|
*/
|
|
|
|
extern boolean notonhead; /* for long worms */
|
|
|
|
#define get_artifact(o) \
|
|
(((o)&&(o)->oartifact) ? &artilist[(int) (o)->oartifact] : 0)
|
|
STATIC_DCL int FDECL(spec_applies, (const struct artifact *,struct monst *));
|
|
STATIC_DCL int FDECL(arti_invoke, (struct obj*));
|
|
|
|
/* The amount added to the victim's total hit points to insure that the
|
|
victim will be killed even after damage bonus/penalty adjustments.
|
|
Most such penalties are small, and 200 is plenty; the exception is
|
|
half physical damage. 3.3.2 and previous versions tried to use a very
|
|
large number to account for this case; now, we just compute the fatal
|
|
damage by adding it to 2 times the total hit points instead of 1 time.
|
|
Note: this will still break if they have more than about half the number
|
|
of hit points that will fit in a 15 bit integer. */
|
|
#define FATAL_DAMAGE_MODIFIER 200
|
|
|
|
#ifndef OVLB
|
|
STATIC_DCL int spec_dbon_applies;
|
|
STATIC_DCL xchar artidisco[NROFARTIFACTS];
|
|
#else /* OVLB */
|
|
/* coordinate effects from spec_dbon() with messages in artifact_hit() */
|
|
STATIC_OVL int spec_dbon_applies = 0;
|
|
|
|
/* flags including which artifacts have already been created */
|
|
static boolean artiexist[1+NROFARTIFACTS+1];
|
|
/* and a discovery list for them (no dummy first entry here) */
|
|
STATIC_OVL xchar artidisco[NROFARTIFACTS];
|
|
|
|
STATIC_DCL void NDECL(hack_artifacts);
|
|
STATIC_DCL boolean FDECL(attacks, (int,struct obj *));
|
|
|
|
/* handle some special cases; must be called after u_init() */
|
|
STATIC_OVL void
|
|
hack_artifacts()
|
|
{
|
|
struct artifact *art;
|
|
int alignmnt = aligns[flags.initalign].value;
|
|
|
|
/* Fix up the alignments of "gift" artifacts */
|
|
for (art = artilist+1; art->otyp; art++)
|
|
if (art->role == Role_switch && art->alignment != A_NONE)
|
|
art->alignment = alignmnt;
|
|
|
|
/* Excalibur can be used by any lawful character, not just knights */
|
|
if (!Role_if(PM_KNIGHT))
|
|
artilist[ART_EXCALIBUR].role = NON_PM;
|
|
|
|
/* Fix up the quest artifact */
|
|
if (urole.questarti) {
|
|
artilist[urole.questarti].alignment = alignmnt;
|
|
artilist[urole.questarti].role = Role_switch;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* zero out the artifact existence list */
|
|
void
|
|
init_artifacts()
|
|
{
|
|
(void) memset((genericptr_t) artiexist, 0, sizeof artiexist);
|
|
(void) memset((genericptr_t) artidisco, 0, sizeof artidisco);
|
|
hack_artifacts();
|
|
}
|
|
|
|
void
|
|
save_artifacts(fd)
|
|
int fd;
|
|
{
|
|
bwrite(fd, (genericptr_t) artiexist, sizeof artiexist);
|
|
bwrite(fd, (genericptr_t) artidisco, sizeof artidisco);
|
|
}
|
|
|
|
void
|
|
restore_artifacts(fd)
|
|
int fd;
|
|
{
|
|
mread(fd, (genericptr_t) artiexist, sizeof artiexist);
|
|
mread(fd, (genericptr_t) artidisco, sizeof artidisco);
|
|
hack_artifacts(); /* redo non-saved special cases */
|
|
}
|
|
|
|
const char *
|
|
artiname(artinum)
|
|
int artinum;
|
|
{
|
|
if (artinum <= 0 || artinum > NROFARTIFACTS) return("");
|
|
return(artilist[artinum].name);
|
|
}
|
|
|
|
/*
|
|
Make an artifact. If a specific alignment is specified, then an object of
|
|
the appropriate alignment is created from scratch, or 0 is returned if
|
|
none is available. (If at least one aligned artifact has already been
|
|
given, then unaligned ones also become eligible for this.)
|
|
If no alignment is given, then 'otmp' is converted
|
|
into an artifact of matching type, or returned as-is if that's not possible.
|
|
For the 2nd case, caller should use ``obj = mk_artifact(obj, A_NONE);''
|
|
for the 1st, ``obj = mk_artifact((struct obj *)0, some_alignment);''.
|
|
*/
|
|
struct obj *
|
|
mk_artifact(otmp, alignment)
|
|
struct obj *otmp; /* existing object; ignored if alignment specified */
|
|
aligntyp alignment; /* target alignment, or A_NONE */
|
|
{
|
|
const struct artifact *a;
|
|
int n, m;
|
|
boolean by_align = (alignment != A_NONE);
|
|
short o_typ = (by_align || !otmp) ? 0 : otmp->otyp;
|
|
boolean unique = !by_align && otmp && objects[o_typ].oc_unique;
|
|
short eligible[NROFARTIFACTS];
|
|
|
|
/* gather eligible artifacts */
|
|
for (n = 0, a = artilist+1, m = 1; a->otyp; a++, m++)
|
|
if ((!by_align ? a->otyp == o_typ :
|
|
(a->alignment == alignment ||
|
|
(a->alignment == A_NONE && u.ugifts > 0))) &&
|
|
(!(a->spfx & SPFX_NOGEN) || unique) && !artiexist[m]) {
|
|
if (by_align && a->race != NON_PM && race_hostile(&mons[a->race]))
|
|
continue; /* skip enemies' equipment */
|
|
else if (by_align && Role_if(a->role))
|
|
goto make_artif; /* 'a' points to the desired one */
|
|
else
|
|
eligible[n++] = m;
|
|
}
|
|
|
|
if (n) { /* found at least one candidate */
|
|
m = eligible[rn2(n)]; /* [0..n-1] */
|
|
a = &artilist[m];
|
|
|
|
/* make an appropriate object if necessary, then christen it */
|
|
make_artif: if (by_align) otmp = mksobj((int)a->otyp, TRUE, FALSE);
|
|
otmp = oname(otmp, a->name);
|
|
otmp->oartifact = m;
|
|
artiexist[m] = TRUE;
|
|
} else {
|
|
/* nothing appropriate could be found; return the original object */
|
|
if (by_align) otmp = 0; /* (there was no original object) */
|
|
}
|
|
return otmp;
|
|
}
|
|
|
|
/*
|
|
* Returns the full name (with articles and correct capitalization) of an
|
|
* artifact named "name" if one exists, or NULL, it not.
|
|
* The given name must be rather close to the real name for it to match.
|
|
* The object type of the artifact is returned in otyp if the return value
|
|
* is non-NULL.
|
|
*/
|
|
const char*
|
|
artifact_name(name, otyp)
|
|
const char *name;
|
|
short *otyp;
|
|
{
|
|
register const struct artifact *a;
|
|
register const char *aname;
|
|
|
|
if(!strncmpi(name, "the ", 4)) name += 4;
|
|
|
|
for (a = artilist+1; a->otyp; a++) {
|
|
aname = a->name;
|
|
if(!strncmpi(aname, "the ", 4)) aname += 4;
|
|
if(!strcmpi(name, aname)) {
|
|
*otyp = a->otyp;
|
|
return a->name;
|
|
}
|
|
}
|
|
|
|
return (char *)0;
|
|
}
|
|
|
|
boolean
|
|
exist_artifact(otyp, name)
|
|
register int otyp;
|
|
register const char *name;
|
|
{
|
|
register const struct artifact *a;
|
|
register boolean *arex;
|
|
|
|
if (otyp && *name)
|
|
for (a = artilist+1,arex = artiexist+1; a->otyp; a++,arex++)
|
|
if ((int) a->otyp == otyp && !strcmp(a->name, name))
|
|
return *arex;
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
artifact_exists(otmp, name, mod)
|
|
register struct obj *otmp;
|
|
register const char *name;
|
|
register boolean mod;
|
|
{
|
|
register const struct artifact *a;
|
|
|
|
if (otmp && *name)
|
|
for (a = artilist+1; a->otyp; a++)
|
|
if (a->otyp == otmp->otyp && !strcmp(a->name, name)) {
|
|
register int m = a - artilist;
|
|
otmp->oartifact = (char)(mod ? m : 0);
|
|
otmp->age = 0;
|
|
if(otmp->otyp == RIN_INCREASE_DAMAGE)
|
|
otmp->spe = 0;
|
|
artiexist[m] = mod;
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
int
|
|
nartifact_exist()
|
|
{
|
|
int a = 0;
|
|
int n = SIZE(artiexist);
|
|
|
|
while(n > 1)
|
|
if(artiexist[--n]) a++;
|
|
|
|
return a;
|
|
}
|
|
#endif /* OVLB */
|
|
#ifdef OVL0
|
|
|
|
boolean
|
|
spec_ability(otmp, abil)
|
|
struct obj *otmp;
|
|
unsigned long abil;
|
|
{
|
|
const struct artifact *arti = get_artifact(otmp);
|
|
|
|
return((boolean)(arti && (arti->spfx & abil)));
|
|
}
|
|
|
|
#endif /* OVL0 */
|
|
#ifdef OVLB
|
|
|
|
boolean
|
|
restrict_name(otmp, name) /* returns 1 if name is restricted for otmp->otyp */
|
|
register struct obj *otmp;
|
|
register const char *name;
|
|
{
|
|
register const struct artifact *a;
|
|
register const char *aname;
|
|
|
|
if (!*name) return FALSE;
|
|
if (!strncmpi(name, "the ", 4)) name += 4;
|
|
|
|
/* Since almost every artifact is SPFX_RESTR, it doesn't cost
|
|
us much to do the string comparison before the spfx check.
|
|
Bug fix: don't name multiple elven daggers "Sting".
|
|
*/
|
|
for (a = artilist+1; a->otyp; a++) {
|
|
if (a->otyp != otmp->otyp) continue;
|
|
aname = a->name;
|
|
if (!strncmpi(aname, "the ", 4)) aname += 4;
|
|
if (!strcmp(aname, name))
|
|
return ((boolean)((a->spfx & (SPFX_NOGEN|SPFX_RESTR)) != 0 ||
|
|
otmp->quan > 1L));
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
STATIC_OVL boolean
|
|
attacks(adtyp, otmp)
|
|
register int adtyp;
|
|
register struct obj *otmp;
|
|
{
|
|
register const struct artifact *weap;
|
|
|
|
if ((weap = get_artifact(otmp)) != 0)
|
|
return((boolean)(weap->attk.adtyp == adtyp));
|
|
return FALSE;
|
|
}
|
|
|
|
boolean
|
|
defends(adtyp, otmp)
|
|
register int adtyp;
|
|
register struct obj *otmp;
|
|
{
|
|
register const struct artifact *weap;
|
|
|
|
if ((weap = get_artifact(otmp)) != 0)
|
|
return((boolean)(weap->defn.adtyp == adtyp));
|
|
return FALSE;
|
|
}
|
|
|
|
/* used for monsters */
|
|
boolean
|
|
protects(adtyp, otmp)
|
|
int adtyp;
|
|
struct obj *otmp;
|
|
{
|
|
register const struct artifact *weap;
|
|
|
|
if ((weap = get_artifact(otmp)) != 0)
|
|
return (boolean)(weap->cary.adtyp == adtyp);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* a potential artifact has just been worn/wielded/picked-up or
|
|
* unworn/unwielded/dropped. Pickup/drop only set/reset the W_ART mask.
|
|
*/
|
|
void
|
|
set_artifact_intrinsic(otmp,on,wp_mask)
|
|
register struct obj *otmp;
|
|
boolean on;
|
|
long wp_mask;
|
|
{
|
|
long *mask = 0;
|
|
register const struct artifact *oart = get_artifact(otmp);
|
|
uchar dtyp;
|
|
long spfx;
|
|
|
|
if (!oart) return;
|
|
|
|
/* effects from the defn field */
|
|
dtyp = (wp_mask != W_ART) ? oart->defn.adtyp : oart->cary.adtyp;
|
|
|
|
if (dtyp == AD_FIRE)
|
|
mask = &EFire_resistance;
|
|
else if (dtyp == AD_COLD)
|
|
mask = &ECold_resistance;
|
|
else if (dtyp == AD_ELEC)
|
|
mask = &EShock_resistance;
|
|
else if (dtyp == AD_MAGM)
|
|
mask = &EAntimagic;
|
|
else if (dtyp == AD_DISN)
|
|
mask = &EDisint_resistance;
|
|
else if (dtyp == AD_DRST)
|
|
mask = &EPoison_resistance;
|
|
|
|
if (mask && wp_mask == W_ART && !on) {
|
|
/* find out if some other artifact also confers this intrinsic */
|
|
/* if so, leave the mask alone */
|
|
register struct obj* obj;
|
|
for(obj = invent; obj; obj = obj->nobj)
|
|
if(obj != otmp && obj->oartifact) {
|
|
register const struct artifact *art = get_artifact(obj);
|
|
if(art->cary.adtyp == dtyp) {
|
|
mask = (long *) 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (mask) {
|
|
if (on) *mask |= wp_mask;
|
|
else *mask &= ~wp_mask;
|
|
}
|
|
|
|
/* intrinsics from the spfx field; there could be more than one */
|
|
spfx = (wp_mask != W_ART) ? oart->spfx : oart->cspfx;
|
|
if(spfx && wp_mask == W_ART && !on) {
|
|
/* don't change any spfx also conferred by other artifacts */
|
|
register struct obj* obj;
|
|
for(obj = invent; obj; obj = obj->nobj)
|
|
if(obj != otmp && obj->oartifact) {
|
|
register const struct artifact *art = get_artifact(obj);
|
|
spfx &= ~art->cspfx;
|
|
}
|
|
}
|
|
|
|
if (spfx & SPFX_SEARCH) {
|
|
if(on) ESearching |= wp_mask;
|
|
else ESearching &= ~wp_mask;
|
|
}
|
|
if (spfx & SPFX_HALRES) {
|
|
/* make_hallucinated must (re)set the mask itself to get
|
|
* the display right */
|
|
/* restoring needed because this is the only artifact intrinsic
|
|
* that can print a message--need to guard against being printed
|
|
* when restoring a game
|
|
*/
|
|
make_hallucinated((long)!on, restoring ? FALSE : TRUE, wp_mask);
|
|
}
|
|
if (spfx & SPFX_ESP) {
|
|
if(on) ETelepat |= wp_mask;
|
|
else ETelepat &= ~wp_mask;
|
|
see_monsters();
|
|
}
|
|
if (spfx & SPFX_STLTH) {
|
|
if (on) EStealth |= wp_mask;
|
|
else EStealth &= ~wp_mask;
|
|
}
|
|
if (spfx & SPFX_REGEN) {
|
|
if (on) ERegeneration |= wp_mask;
|
|
else ERegeneration &= ~wp_mask;
|
|
}
|
|
if (spfx & SPFX_TCTRL) {
|
|
if (on) ETeleport_control |= wp_mask;
|
|
else ETeleport_control &= ~wp_mask;
|
|
}
|
|
if (spfx & SPFX_WARN) {
|
|
if (spec_m2(otmp)) {
|
|
if (on) {
|
|
EWarn_of_mon |= wp_mask;
|
|
flags.warntype |= spec_m2(otmp);
|
|
} else {
|
|
EWarn_of_mon &= ~wp_mask;
|
|
flags.warntype &= ~spec_m2(otmp);
|
|
}
|
|
see_monsters();
|
|
} else {
|
|
if (on) EWarning |= wp_mask;
|
|
else EWarning &= ~wp_mask;
|
|
}
|
|
}
|
|
if (spfx & SPFX_EREGEN) {
|
|
if (on) EEnergy_regeneration |= wp_mask;
|
|
else EEnergy_regeneration &= ~wp_mask;
|
|
}
|
|
if (spfx & SPFX_HSPDAM) {
|
|
if (on) EHalf_spell_damage |= wp_mask;
|
|
else EHalf_spell_damage &= ~wp_mask;
|
|
}
|
|
if (spfx & SPFX_HPHDAM) {
|
|
if (on) EHalf_physical_damage |= wp_mask;
|
|
else EHalf_physical_damage &= ~wp_mask;
|
|
}
|
|
if (spfx & SPFX_XRAY) {
|
|
/* this assumes that no one else is using xray_range */
|
|
if (on) u.xray_range = 3;
|
|
else u.xray_range = -1;
|
|
}
|
|
if ((spfx & SPFX_REFLECT) && (wp_mask & W_WEP)) {
|
|
if (on) EReflecting |= wp_mask;
|
|
else EReflecting &= ~wp_mask;
|
|
}
|
|
|
|
if(wp_mask == W_ART && !on && oart->inv_prop) {
|
|
/* might have to turn off invoked power too */
|
|
if (oart->inv_prop <= LAST_PROP &&
|
|
(u.uprops[oart->inv_prop].extrinsic & W_ARTI))
|
|
(void) arti_invoke(otmp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* creature (usually player) tries to touch (pick up or wield) an artifact obj.
|
|
* Returns 0 if the object refuses to be touched.
|
|
* This routine does not change any object chains.
|
|
* Ignores such things as gauntlets, assuming the artifact is not
|
|
* fooled by such trappings.
|
|
*/
|
|
int
|
|
touch_artifact(obj,mon)
|
|
struct obj *obj;
|
|
struct monst *mon;
|
|
{
|
|
register const struct artifact *oart = get_artifact(obj);
|
|
boolean badclass, badalign, self_willed, yours;
|
|
|
|
if(!oart) return 1;
|
|
|
|
yours = (mon == &youmonst);
|
|
/* all quest artifacts are self-willed; it this ever changes, `badclass'
|
|
will have to be extended to explicitly include quest artifacts */
|
|
self_willed = ((oart->spfx & SPFX_INTEL) != 0);
|
|
if (yours || !(mon->data->mflags3 & M3_WANTSALL)) {
|
|
badclass = (self_willed && (!yours ||
|
|
(oart->role != NON_PM && !Role_if(oart->role)) ||
|
|
(oart->race != NON_PM && !Race_if(oart->race))));
|
|
badalign = (oart->spfx & SPFX_RESTR) &&
|
|
oart->alignment != A_NONE &&
|
|
((oart->alignment !=
|
|
(yours ? u.ualign.type : sgn(mon->data->maligntyp))) ||
|
|
(yours && u.ualign.record < 0));
|
|
} else { /* an M3_WANTSxxx monster */
|
|
/* special monsters trying to take the Amulet, invocation tools or
|
|
quest item can touch anything except for `spec_applies' artifacts */
|
|
badclass = badalign = FALSE;
|
|
}
|
|
/* weapons which attack specific categories of monsters are
|
|
bad for them even if their alignments happen to match */
|
|
if (!badalign && (oart->spfx & SPFX_DBONUS) != 0) {
|
|
struct artifact tmp;
|
|
|
|
tmp = *oart;
|
|
tmp.spfx &= SPFX_DBONUS;
|
|
badalign = !!spec_applies(&tmp, mon);
|
|
}
|
|
|
|
if (((badclass || badalign) && self_willed) ||
|
|
(badalign && (!yours || !rn2(4)))) {
|
|
int dmg;
|
|
char buf[BUFSZ];
|
|
|
|
if (!yours) return 0;
|
|
You("are blasted by %s power!", s_suffix(the(xname(obj))));
|
|
dmg = d((Antimagic ? 2 : 4), (self_willed ? 10 : 4));
|
|
Sprintf(buf, "touching %s", oart->name);
|
|
losehp(dmg, buf, KILLED_BY);
|
|
exercise(A_WIS, FALSE);
|
|
}
|
|
|
|
/* can pick it up unless you're totally non-synch'd with the artifact */
|
|
if (badclass && badalign && self_willed) {
|
|
if (yours) pline("%s evades your grasp!", The(xname(obj)));
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
#endif /* OVLB */
|
|
#ifdef OVL1
|
|
|
|
/* decide whether an artifact's special attacks apply against mtmp */
|
|
STATIC_OVL int
|
|
spec_applies(weap, mtmp)
|
|
register const struct artifact *weap;
|
|
struct monst *mtmp;
|
|
{
|
|
struct permonst *ptr;
|
|
boolean yours;
|
|
|
|
if(!(weap->spfx & (SPFX_DBONUS | SPFX_ATTK)))
|
|
return(weap->attk.adtyp == AD_PHYS);
|
|
|
|
yours = (mtmp == &youmonst);
|
|
ptr = mtmp->data;
|
|
|
|
if (weap->spfx & SPFX_DMONS) {
|
|
return (ptr == &mons[(int)weap->mtype]);
|
|
} else if (weap->spfx & SPFX_DCLAS) {
|
|
return (weap->mtype == (unsigned long)ptr->mlet);
|
|
} else if (weap->spfx & SPFX_DFLAG1) {
|
|
return ((ptr->mflags1 & weap->mtype) != 0L);
|
|
} else if (weap->spfx & SPFX_DFLAG2) {
|
|
return ((ptr->mflags2 & weap->mtype) ||
|
|
(yours && !Upolyd && (urace.selfmask & weap->mtype)));
|
|
} else if (weap->spfx & SPFX_DALIGN) {
|
|
return yours ? (u.ualign.type != weap->alignment) :
|
|
(ptr->maligntyp == A_NONE ||
|
|
sgn(ptr->maligntyp) != weap->alignment);
|
|
} else if (weap->spfx & SPFX_ATTK) {
|
|
struct obj *defending_weapon = (yours ? uwep : MON_WEP(mtmp));
|
|
|
|
if (defending_weapon && defending_weapon->oartifact &&
|
|
defends((int)weap->attk.adtyp, defending_weapon))
|
|
return FALSE;
|
|
switch(weap->attk.adtyp) {
|
|
case AD_FIRE:
|
|
return !(yours ? Fire_resistance : resists_fire(mtmp));
|
|
case AD_COLD:
|
|
return !(yours ? Cold_resistance : resists_cold(mtmp));
|
|
case AD_ELEC:
|
|
return !(yours ? Shock_resistance : resists_elec(mtmp));
|
|
case AD_MAGM:
|
|
case AD_STUN:
|
|
return !(yours ? Antimagic : (rn2(100) < ptr->mr));
|
|
case AD_DRST:
|
|
return !(yours ? Poison_resistance : resists_poison(mtmp));
|
|
case AD_DRLI:
|
|
return !(yours ? Drain_resistance : resists_drli(mtmp));
|
|
case AD_STON:
|
|
return !(yours ? Stone_resistance : resists_ston(mtmp));
|
|
default: impossible("Weird weapon special attack.");
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/* return the M2 flags of monster that an artifact's special attacks apply against */
|
|
long
|
|
spec_m2(otmp)
|
|
struct obj *otmp;
|
|
{
|
|
register const struct artifact *artifact = get_artifact(otmp);
|
|
if (artifact)
|
|
return artifact->mtype;
|
|
return 0L;
|
|
}
|
|
|
|
/* special attack bonus */
|
|
int
|
|
spec_abon(otmp, mon)
|
|
struct obj *otmp;
|
|
struct monst *mon;
|
|
{
|
|
register const struct artifact *weap = get_artifact(otmp);
|
|
|
|
/* no need for an extra check for `NO_ATTK' because this will
|
|
always return 0 for any artifact which has that attribute */
|
|
|
|
if (weap && weap->attk.damn && spec_applies(weap, mon))
|
|
return rnd((int)weap->attk.damn);
|
|
return 0;
|
|
}
|
|
|
|
/* special damage bonus */
|
|
int
|
|
spec_dbon(otmp, mon, tmp)
|
|
struct obj *otmp;
|
|
struct monst *mon;
|
|
int tmp;
|
|
{
|
|
register const struct artifact *weap = get_artifact(otmp);
|
|
|
|
if (!weap || (weap->attk.adtyp == AD_PHYS && /* check for `NO_ATTK' */
|
|
weap->attk.damn == 0 && weap->attk.damd == 0))
|
|
spec_dbon_applies = FALSE;
|
|
else
|
|
spec_dbon_applies = spec_applies(weap, mon);
|
|
|
|
if (spec_dbon_applies)
|
|
return weap->attk.damd ? rnd((int)weap->attk.damd) : max(tmp,1);
|
|
return 0;
|
|
}
|
|
|
|
/* add identified artifact to discoveries list */
|
|
void
|
|
discover_artifact(m)
|
|
xchar m;
|
|
{
|
|
int i;
|
|
|
|
/* look for this artifact in the discoveries list;
|
|
if we hit an empty slot then it's not present, so add it */
|
|
for (i = 0; i < NROFARTIFACTS; i++)
|
|
if (artidisco[i] == 0 || artidisco[i] == m) {
|
|
artidisco[i] = m;
|
|
return;
|
|
}
|
|
/* there is one slot per artifact, so we should never reach the
|
|
end without either finding the artifact or an empty slot... */
|
|
impossible("couldn't discover artifact (%d)", (int)m);
|
|
}
|
|
|
|
/* used to decide whether an artifact has been fully identified */
|
|
boolean
|
|
undiscovered_artifact(m)
|
|
xchar m;
|
|
{
|
|
int i;
|
|
|
|
/* look for this artifact in the discoveries list;
|
|
if we hit an empty slot then it's undiscovered */
|
|
for (i = 0; i < NROFARTIFACTS; i++)
|
|
if (artidisco[i] == m)
|
|
return FALSE;
|
|
else if (artidisco[i] == 0)
|
|
break;
|
|
return TRUE;
|
|
}
|
|
|
|
/* display a list of discovered artifacts; return their count */
|
|
int
|
|
disp_artifact_discoveries(tmpwin)
|
|
winid tmpwin; /* supplied by dodiscover() */
|
|
{
|
|
int i, m, otyp;
|
|
char buf[BUFSZ];
|
|
|
|
for (i = 0; i < NROFARTIFACTS; i++) {
|
|
if (artidisco[i] == 0) break; /* empty slot implies end of list */
|
|
if (i == 0) putstr(tmpwin, ATR_INVERSE, "Artifacts");
|
|
m = artidisco[i];
|
|
otyp = artilist[m].otyp;
|
|
Sprintf(buf, " %s [%s %s]", artiname(m),
|
|
align_str(artilist[m].alignment), simple_typename(otyp));
|
|
putstr(tmpwin, 0, buf);
|
|
}
|
|
return i;
|
|
}
|
|
|
|
#endif /* OVL1 */
|
|
|
|
#ifdef OVLB
|
|
|
|
/* Function used when someone attacks someone else with an artifact
|
|
* weapon. Only adds the special (artifact) damage, and returns a 1 if it
|
|
* did something special (in which case the caller won't print the normal
|
|
* hit message). This should be called once upon every artifact attack;
|
|
* dmgval() no longer takes artifact bonuses into account. Possible
|
|
* extension: change the killer so that when an orc kills you with
|
|
* Stormbringer it's "killed by Stormbringer" instead of "killed by an orc".
|
|
*/
|
|
boolean
|
|
artifact_hit(magr, mdef, otmp, dmgptr, dieroll)
|
|
struct monst *magr, *mdef;
|
|
struct obj *otmp;
|
|
int *dmgptr;
|
|
int dieroll; /* needed for Magicbane and vorpal blades */
|
|
{
|
|
boolean youattack = (magr == &youmonst);
|
|
boolean youdefend = (mdef == &youmonst);
|
|
boolean vis = (!youattack && magr && cansee(magr->mx, magr->my))
|
|
|| (!youdefend && cansee(mdef->mx, mdef->my));
|
|
boolean realizes_damage;
|
|
|
|
static const char you[] = "you";
|
|
char hittee[BUFSZ];
|
|
|
|
Strcpy(hittee, youdefend ? you : mon_nam(mdef));
|
|
|
|
/* The following takes care of most of the damage, but not all--
|
|
* the exception being for level draining, which is specially
|
|
* handled. Messages are done in this function, however.
|
|
*/
|
|
*dmgptr += spec_dbon(otmp, mdef, *dmgptr);
|
|
|
|
if (youattack && youdefend) {
|
|
impossible("attacking yourself with weapon?");
|
|
return FALSE;
|
|
} else if (!spec_dbon_applies) {
|
|
/* since damage bonus didn't apply, nothing more to do */
|
|
return FALSE;
|
|
}
|
|
|
|
realizes_damage = (youdefend || vis);
|
|
|
|
/* the four basic attacks: fire, cold, shock and missiles */
|
|
if (attacks(AD_FIRE, otmp)) {
|
|
if (realizes_damage) {
|
|
pline_The("fiery blade %s %s!",
|
|
(mdef->data == &mons[PM_WATER_ELEMENTAL]) ?
|
|
"vaporizes part of" : "burns", hittee);
|
|
if (!rn2(4)) (void) destroy_mitem(mdef, POTION_CLASS, AD_FIRE);
|
|
if (!rn2(4)) (void) destroy_mitem(mdef, SCROLL_CLASS, AD_FIRE);
|
|
if (!rn2(7)) (void) destroy_mitem(mdef, SPBOOK_CLASS, AD_FIRE);
|
|
return TRUE;
|
|
}
|
|
}
|
|
if (attacks(AD_COLD, otmp)) {
|
|
if (realizes_damage) {
|
|
pline_The("ice-cold blade freezes %s!", hittee);
|
|
if (!rn2(4)) (void) destroy_mitem(mdef, POTION_CLASS, AD_COLD);
|
|
return TRUE;
|
|
}
|
|
}
|
|
if (attacks(AD_ELEC, otmp)) {
|
|
if (realizes_damage) {
|
|
if(youattack && otmp != uwep)
|
|
pline("%s hits %s!", The(xname(otmp)), hittee);
|
|
pline("Lightning strikes %s!", hittee);
|
|
if (!rn2(5)) (void) destroy_mitem(mdef, RING_CLASS, AD_ELEC);
|
|
if (!rn2(5)) (void) destroy_mitem(mdef, WAND_CLASS, AD_ELEC);
|
|
return TRUE;
|
|
}
|
|
}
|
|
if (attacks(AD_MAGM, otmp)) {
|
|
if (realizes_damage) {
|
|
if(youattack && otmp != uwep)
|
|
pline("%s hits %s!", The(xname(otmp)), hittee);
|
|
pline("A hail of magic missiles strikes %s!", hittee);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Magicbane's intrinsic magic is incompatible with normal
|
|
* enchantment magic. Thus, its effects have a negative
|
|
* dependence on spe. Against low mr victims, it typically
|
|
* does "double athame" damage, 2d4. Occasionally, it will
|
|
* cast unbalancing magic which effectively averages out to
|
|
* 4d4 damage (2.5d4 against high mr victims), for spe = 0.
|
|
*/
|
|
|
|
#define MB_MAX_DIEROLL 8 /* rolls above this aren't magical */
|
|
#define MB_INDEX_INIT (-1)
|
|
#define MB_INDEX_PROBE 0
|
|
#define MB_INDEX_STUN 1
|
|
#define MB_INDEX_SCARE 2
|
|
#define MB_INDEX_PURGE 3
|
|
#define MB_RESIST_ATTACK (resist_index = attack_index)
|
|
#define MB_RESISTED_ATTACK (resist_index == attack_index)
|
|
#define MB_UWEP_ATTACK (youattack && (otmp == uwep))
|
|
|
|
if (attacks(AD_STUN, otmp) && (dieroll <= MB_MAX_DIEROLL)) {
|
|
int attack_index = MB_INDEX_INIT;
|
|
int resist_index = MB_INDEX_INIT;
|
|
int scare_dieroll = MB_MAX_DIEROLL / 2;
|
|
|
|
if (otmp->spe >= 3)
|
|
scare_dieroll /= (1 << (otmp->spe / 3));
|
|
|
|
*dmgptr += rnd(4); /* 3d4 */
|
|
|
|
if (otmp->spe > rn2(10)) /* probe */
|
|
attack_index = MB_INDEX_PROBE;
|
|
else { /* stun */
|
|
attack_index = MB_INDEX_STUN;
|
|
*dmgptr += rnd(4); /* 4d4 */
|
|
|
|
if (youdefend)
|
|
make_stunned((HStun + 3), FALSE);
|
|
else
|
|
mdef->mstun = 1;
|
|
}
|
|
if (dieroll <= scare_dieroll) { /* scare */
|
|
attack_index = MB_INDEX_SCARE;
|
|
*dmgptr += rnd(4); /* 5d4 */
|
|
|
|
if (youdefend) {
|
|
if (Antimagic)
|
|
MB_RESIST_ATTACK;
|
|
else {
|
|
nomul(-3);
|
|
nomovemsg = "";
|
|
if ((magr == u.ustuck)
|
|
&& sticks(youmonst.data)) {
|
|
u.ustuck = (struct monst *)0;
|
|
You("release %s!", mon_nam(magr));
|
|
}
|
|
}
|
|
} else if (youattack) {
|
|
if (rn2(2) && resist(mdef,SPBOOK_CLASS,0,0)) {
|
|
MB_RESIST_ATTACK;
|
|
} else {
|
|
monflee(mdef, 3, FALSE, TRUE);
|
|
}
|
|
}
|
|
}
|
|
if (dieroll <= (scare_dieroll / 2)) { /* purge */
|
|
struct obj *ospell;
|
|
struct permonst *old_uasmon = youmonst.data;
|
|
|
|
attack_index = MB_INDEX_PURGE;
|
|
*dmgptr += rnd(4); /* 6d4 */
|
|
|
|
/* Create a fake spell object, ala spell.c */
|
|
ospell = mksobj(SPE_CANCELLATION, FALSE, FALSE);
|
|
ospell->blessed = ospell->cursed = 0;
|
|
ospell->quan = 20L;
|
|
|
|
cancel_monst(mdef, ospell, youattack, FALSE, FALSE);
|
|
|
|
if (youdefend) {
|
|
if (old_uasmon != youmonst.data)
|
|
/* rehumanized, no more damage */
|
|
*dmgptr = 0;
|
|
if (Antimagic)
|
|
MB_RESIST_ATTACK;
|
|
} else {
|
|
if (!mdef->mcan)
|
|
MB_RESIST_ATTACK;
|
|
|
|
/* cancelled clay golems will die ... */
|
|
else if (mdef->data == &mons[PM_CLAY_GOLEM])
|
|
mdef->mhp = 1;
|
|
}
|
|
|
|
obfree(ospell, (struct obj *)0);
|
|
}
|
|
|
|
if (youdefend || mdef->mhp > 0) { /* ??? -dkh- */
|
|
static const char *mb_verb[4] =
|
|
{"probe", "stun", "scare", "purge"};
|
|
|
|
if (youattack || youdefend || vis) {
|
|
pline_The("magic-absorbing blade %ss %s!",
|
|
mb_verb[attack_index], hittee);
|
|
|
|
if (MB_RESISTED_ATTACK) {
|
|
pline("%s resist%s!",
|
|
youdefend ? "You" : Monnam(mdef),
|
|
youdefend ? "" : "s");
|
|
|
|
shieldeff(youdefend ? u.ux : mdef->mx,
|
|
youdefend ? u.uy : mdef->my);
|
|
}
|
|
}
|
|
|
|
/* Much ado about nothing. More magic fanfare! */
|
|
if (MB_UWEP_ATTACK) {
|
|
if (attack_index == MB_INDEX_PURGE) {
|
|
if (!MB_RESISTED_ATTACK &&
|
|
attacktype(mdef->data, AT_MAGC)) {
|
|
You("absorb magical energy!");
|
|
u.uenmax++;
|
|
u.uen++;
|
|
flags.botl = 1;
|
|
}
|
|
} else if (attack_index == MB_INDEX_PROBE) {
|
|
if (!rn2(4 * otmp->spe)) {
|
|
pline_The("probe is insightful!");
|
|
if (!canspotmon(mdef))
|
|
map_invisible(u.ux+u.dx,u.uy+u.dy);
|
|
/* pre-damage status */
|
|
probe_monster(mdef);
|
|
}
|
|
}
|
|
} else if (youdefend && !MB_RESISTED_ATTACK
|
|
&& (attack_index == MB_INDEX_PURGE)) {
|
|
You("lose magical energy!");
|
|
if (u.uenmax > 0) u.uenmax--;
|
|
if (u.uen > 0) u.uen--;
|
|
flags.botl = 1;
|
|
}
|
|
|
|
/* all this magic is confusing ... */
|
|
if (!rn2(12)) {
|
|
if (youdefend)
|
|
make_confused((HConfusion + 4), FALSE);
|
|
else
|
|
mdef->mconf = 1;
|
|
|
|
if (youattack || youdefend || vis)
|
|
pline("%s %s confused.",
|
|
youdefend ? "You" : Monnam(mdef),
|
|
youdefend ? "are" : "is");
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
/* end of Magicbane code */
|
|
|
|
/* We really want "on a natural 20" but Nethack does it in */
|
|
/* reverse from AD&D. */
|
|
if (spec_ability(otmp, SPFX_BEHEAD)) {
|
|
if (otmp->oartifact == ART_TSURUGI_OF_MURAMASA && dieroll == 1) {
|
|
/* not really beheading, but so close, why add another SPFX */
|
|
if (youattack && u.uswallow && mdef == u.ustuck) {
|
|
You("slice %s wide open!", mon_nam(mdef));
|
|
*dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
|
|
return TRUE;
|
|
}
|
|
if (!youdefend) {
|
|
/* allow normal cutworm() call to add extra damage */
|
|
if(notonhead)
|
|
return FALSE;
|
|
|
|
if (bigmonst(mdef->data)) {
|
|
if (youattack)
|
|
You("slice deeply into %s!",
|
|
mon_nam(mdef));
|
|
else if (vis)
|
|
pline("%s cuts deeply into %s!",
|
|
Monnam(magr), hittee);
|
|
*dmgptr *= 2;
|
|
return TRUE;
|
|
}
|
|
*dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
|
|
pline_The("razor-sharp blade cuts %s in half!",
|
|
mon_nam(mdef));
|
|
otmp->dknown = TRUE;
|
|
return TRUE;
|
|
} else {
|
|
if (bigmonst(youmonst.data)) {
|
|
pline("%s cuts deeply into you!",
|
|
Monnam(magr));
|
|
*dmgptr *= 2;
|
|
return TRUE;
|
|
}
|
|
|
|
/* Players with negative AC's take less damage instead
|
|
* of just not getting hit. We must add a large enough
|
|
* value to the damage so that this reduction in
|
|
* damage does not prevent death.
|
|
*/
|
|
*dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE_MODIFIER;
|
|
pline_The("razor-sharp blade cuts you in half!");
|
|
otmp->dknown = TRUE;
|
|
return TRUE;
|
|
}
|
|
} else if (otmp->oartifact == ART_VORPAL_BLADE &&
|
|
(dieroll == 1 || mdef->data == &mons[PM_JABBERWOCK])) {
|
|
static const char *behead_msg[2] = {
|
|
"%s beheads %s!",
|
|
"%s decapitates %s!"
|
|
};
|
|
|
|
if (youattack && u.uswallow && mdef == u.ustuck)
|
|
return FALSE;
|
|
if (!youdefend) {
|
|
if (!has_head(mdef->data) || notonhead || u.uswallow) {
|
|
if (youattack)
|
|
pline("Somehow, you miss %s wildly.",
|
|
mon_nam(mdef));
|
|
else if (vis)
|
|
pline("Somehow, %s misses wildly.",
|
|
mon_nam(magr));
|
|
*dmgptr = 0;
|
|
return ((boolean)(youattack || vis));
|
|
}
|
|
if (noncorporeal(mdef->data) || amorphous(mdef->data)) {
|
|
pline("%s slices through %s %s.",
|
|
artilist[ART_VORPAL_BLADE].name,
|
|
s_suffix(mon_nam(mdef)), mbodypart(mdef,NECK));
|
|
return TRUE;
|
|
}
|
|
*dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
|
|
pline(behead_msg[rn2(SIZE(behead_msg))],
|
|
artilist[ART_VORPAL_BLADE].name,
|
|
mon_nam(mdef));
|
|
otmp->dknown = TRUE;
|
|
return TRUE;
|
|
} else {
|
|
if (!has_head(youmonst.data)) {
|
|
pline("Somehow, %s misses you wildly.",
|
|
mon_nam(magr));
|
|
*dmgptr = 0;
|
|
return TRUE;
|
|
}
|
|
if (noncorporeal(youmonst.data) || amorphous(youmonst.data)) {
|
|
pline("%s slices through your %s.",
|
|
artilist[ART_VORPAL_BLADE].name, body_part(NECK));
|
|
return TRUE;
|
|
}
|
|
*dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE_MODIFIER;
|
|
pline(behead_msg[rn2(SIZE(behead_msg))],
|
|
artilist[ART_VORPAL_BLADE].name, "you");
|
|
otmp->dknown = TRUE;
|
|
/* Should amulets fall off? */
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (spec_ability(otmp, SPFX_DRLI)) {
|
|
if (!youdefend) {
|
|
if (vis) {
|
|
if(otmp->oartifact == ART_STORMBRINGER)
|
|
pline_The("%s blade draws the life from %s!",
|
|
hcolor(Black),
|
|
mon_nam(mdef));
|
|
else
|
|
pline("%s draws the life from %s!",
|
|
The(distant_name(otmp, xname)),
|
|
mon_nam(mdef));
|
|
}
|
|
if (mdef->m_lev == 0) {
|
|
*dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
|
|
} else {
|
|
int drain = rnd(8);
|
|
*dmgptr += drain;
|
|
mdef->mhpmax -= drain;
|
|
mdef->m_lev--;
|
|
drain /= 2;
|
|
if (drain) healup(drain, 0, FALSE, FALSE);
|
|
}
|
|
return vis;
|
|
} else { /* youdefend */
|
|
int oldhpmax = u.uhpmax;
|
|
|
|
if (Blind)
|
|
You_feel("an %s drain your life!",
|
|
otmp->oartifact == ART_STORMBRINGER ?
|
|
"unholy blade" : "object");
|
|
else if (otmp->oartifact == ART_STORMBRINGER)
|
|
pline_The("%s blade drains your life!",
|
|
hcolor(Black));
|
|
else
|
|
pline("%s drains your life!",
|
|
The(distant_name(otmp, xname)));
|
|
losexp("life drainage");
|
|
if (magr->mhp < magr->mhpmax) {
|
|
magr->mhp += (u.uhpmax - oldhpmax)/2;
|
|
if (magr->mhp > magr->mhpmax) magr->mhp = magr->mhpmax;
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static NEARDATA const char recharge_type[] = { ALLOW_COUNT, ALL_CLASSES, 0 };
|
|
static NEARDATA const char invoke_types[] = { ALL_CLASSES, 0 };
|
|
/* #invoke: an "ugly check" filters out most objects */
|
|
|
|
int
|
|
doinvoke()
|
|
{
|
|
register struct obj *obj;
|
|
|
|
obj = getobj(invoke_types, "invoke");
|
|
if(!obj) return 0;
|
|
return arti_invoke(obj);
|
|
}
|
|
|
|
STATIC_OVL int
|
|
arti_invoke(obj)
|
|
register struct obj *obj;
|
|
{
|
|
register const struct artifact *oart = get_artifact(obj);
|
|
|
|
if(!oart || !oart->inv_prop) {
|
|
if(obj->otyp == CRYSTAL_BALL)
|
|
use_crystal_ball(obj);
|
|
else
|
|
pline(nothing_happens);
|
|
return 1;
|
|
}
|
|
|
|
if(oart->inv_prop > LAST_PROP) {
|
|
/* It's a special power, not "just" a property */
|
|
if(obj->age > monstermoves) {
|
|
/* the artifact is tired :-) */
|
|
You_feel("that %s is ignoring you.", the(xname(obj)));
|
|
/* and just got more so; patience is essential... */
|
|
obj->age += (long) d(3,10);
|
|
return 1;
|
|
}
|
|
obj->age = monstermoves + rnz(100);
|
|
|
|
switch(oart->inv_prop) {
|
|
case TAMING: {
|
|
struct obj pseudo;
|
|
|
|
pseudo = zeroobj; /* neither cursed nor blessed */
|
|
pseudo.otyp = SCR_TAMING;
|
|
(void) seffects(&pseudo);
|
|
break;
|
|
}
|
|
case HEALING: {
|
|
int healamt = (u.uhpmax + 1 - u.uhp) / 2;
|
|
long creamed = (long)u.ucreamed;
|
|
|
|
if (Upolyd) healamt = (u.mhmax + 1 - u.mh) / 2;
|
|
if (healamt || Sick || Blinded > creamed)
|
|
You_feel("better.");
|
|
else
|
|
goto nothing_special;
|
|
if (healamt > 0) {
|
|
if (Upolyd) u.mh += healamt;
|
|
else u.uhp += healamt;
|
|
}
|
|
if(Sick) make_sick(0L,(char *)0,FALSE,SICK_ALL);
|
|
if(Slimed) Slimed = 0L;
|
|
if (Blinded > creamed) make_blinded(creamed, FALSE);
|
|
flags.botl = 1;
|
|
break;
|
|
}
|
|
case ENERGY_BOOST: {
|
|
int epboost = (u.uenmax + 1 - u.uen) / 2;
|
|
if (epboost > 120) epboost = 120; /* arbitrary */
|
|
else if (epboost < 12) epboost = u.uenmax - u.uen;
|
|
if(epboost) {
|
|
You_feel("re-energized.");
|
|
u.uen += epboost;
|
|
flags.botl = 1;
|
|
} else
|
|
goto nothing_special;
|
|
break;
|
|
}
|
|
case UNTRAP: {
|
|
if(!untrap(TRUE)) {
|
|
obj->age = 0; /* don't charge for changing their mind */
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case CHARGE_OBJ: {
|
|
struct obj *otmp = getobj(recharge_type, "charge");
|
|
boolean b_effect;
|
|
|
|
if (!otmp) {
|
|
obj->age = 0;
|
|
return 0;
|
|
}
|
|
b_effect = obj->blessed && (Role_switch == oart->role || !oart->role);
|
|
recharge(otmp, b_effect ? 1 : obj->cursed ? -1 : 0);
|
|
break;
|
|
}
|
|
case LEV_TELE:
|
|
level_tele();
|
|
break;
|
|
case CREATE_PORTAL: {
|
|
int i, num_ok_dungeons, last_ok_dungeon = 0;
|
|
d_level newlev;
|
|
extern int n_dgns; /* from dungeon.c */
|
|
winid tmpwin = create_nhwindow(NHW_MENU);
|
|
anything any;
|
|
|
|
any.a_void = 0; /* set all bits to zero */
|
|
start_menu(tmpwin);
|
|
/* use index+1 (cant use 0) as identifier */
|
|
for (i = num_ok_dungeons = 0; i < n_dgns; i++) {
|
|
if (!dungeons[i].dunlev_ureached) continue;
|
|
any.a_int = i+1;
|
|
add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
|
|
dungeons[i].dname, MENU_UNSELECTED);
|
|
num_ok_dungeons++;
|
|
last_ok_dungeon = i;
|
|
}
|
|
end_menu(tmpwin, "Open a portal to which dungeon?");
|
|
if (num_ok_dungeons > 1) {
|
|
/* more than one entry; display menu for choices */
|
|
menu_item *selected;
|
|
int n;
|
|
|
|
n = select_menu(tmpwin, PICK_ONE, &selected);
|
|
if (n <= 0) {
|
|
destroy_nhwindow(tmpwin);
|
|
goto nothing_special;
|
|
}
|
|
i = selected[0].item.a_int - 1;
|
|
free((genericptr_t)selected);
|
|
} else
|
|
i = last_ok_dungeon; /* also first & only OK dungeon */
|
|
destroy_nhwindow(tmpwin);
|
|
|
|
/*
|
|
* i is now index into dungeon structure for the new dungeon.
|
|
* Find the closest level in the given dungeon, open
|
|
* a use-once portal to that dungeon and go there.
|
|
* The closest level is either the entry or dunlev_ureached.
|
|
*/
|
|
newlev.dnum = i;
|
|
if(dungeons[i].depth_start >= depth(&u.uz))
|
|
newlev.dlevel = dungeons[i].entry_lev;
|
|
else
|
|
newlev.dlevel = dungeons[i].dunlev_ureached;
|
|
if(u.uhave.amulet || In_endgame(&u.uz) || In_endgame(&newlev) ||
|
|
newlev.dnum == u.uz.dnum) {
|
|
You_feel("very disoriented for a moment.");
|
|
} else {
|
|
if(!Blind) You("are surrounded by a shimmering sphere!");
|
|
else You_feel("weightless for a moment.");
|
|
goto_level(&newlev, FALSE, FALSE, FALSE);
|
|
}
|
|
break;
|
|
}
|
|
case ENLIGHTENING:
|
|
enlightenment(0);
|
|
break;
|
|
case CREATE_AMMO: {
|
|
struct obj *otmp = mksobj(ARROW, TRUE, FALSE);
|
|
|
|
if (!otmp) goto nothing_special;
|
|
otmp->blessed = obj->blessed;
|
|
otmp->cursed = obj->cursed;
|
|
otmp->bknown = obj->bknown;
|
|
if (obj->blessed) {
|
|
if (otmp->spe < 0) otmp->spe = 0;
|
|
otmp->quan += rnd(10);
|
|
} else if (obj->cursed) {
|
|
if (otmp->spe > 0) otmp->spe = 0;
|
|
} else
|
|
otmp->quan += rnd(5);
|
|
otmp->owt = weight(otmp);
|
|
otmp = hold_another_object(otmp, "Suddenly %s out.",
|
|
aobjnam(otmp, "fall"), (const char *)0);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
long cprop = (u.uprops[oart->inv_prop].extrinsic ^= W_ARTI);
|
|
boolean on = (cprop & W_ARTI) != 0; /* true if invoked prop just set */
|
|
|
|
if(on && obj->age > monstermoves) {
|
|
/* the artifact is tired :-) */
|
|
u.uprops[oart->inv_prop].extrinsic ^= W_ARTI;
|
|
You_feel("that %s is ignoring you.", the(xname(obj)));
|
|
return 1;
|
|
} else if(!on) {
|
|
/* when turning off property, determine downtime */
|
|
/* arbitrary for now until we can tune this -dlc */
|
|
obj->age = monstermoves + rnz(100);
|
|
}
|
|
|
|
if(cprop & ~W_ARTI) {
|
|
nothing_special:
|
|
/* you had the property from some other source too */
|
|
if (carried(obj))
|
|
You_feel("a surge of power, but nothing seems to happen.");
|
|
return 1;
|
|
}
|
|
switch(oart->inv_prop) {
|
|
case CONFLICT:
|
|
if(on) You_feel("like a rabble-rouser.");
|
|
else You_feel("the tension decrease around you.");
|
|
break;
|
|
case LEVITATION:
|
|
if(on) {
|
|
float_up();
|
|
spoteffects(FALSE);
|
|
} else (void) float_down(I_SPECIAL|TIMEOUT, W_ARTI);
|
|
break;
|
|
case INVIS:
|
|
if (!See_invisible && !Blind) {
|
|
newsym(u.ux,u.uy);
|
|
if (on) {
|
|
Your("body takes on a %s transparency...",
|
|
Hallucination ? "normal" : "strange");
|
|
} else {
|
|
Your("body seems to unfade...");
|
|
}
|
|
} else goto nothing_special;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* WAC return TRUE if artifact is always lit */
|
|
boolean
|
|
artifact_light(obj)
|
|
struct obj *obj;
|
|
{
|
|
return (get_artifact(obj) && obj->oartifact == ART_SUNSWORD);
|
|
}
|
|
|
|
/* KMH -- Talking artifacts are finally implemented */
|
|
void arti_speak(obj)
|
|
struct obj *obj;
|
|
{
|
|
register const struct artifact *oart = get_artifact(obj);
|
|
const char *line;
|
|
char buf[BUFSZ];
|
|
|
|
|
|
/* Is this a speaking artifact? */
|
|
if (!oart || !(oart->spfx & SPFX_SPEAK))
|
|
return;
|
|
|
|
line = getrumor(bcsign(obj), buf, TRUE);
|
|
if (!*line)
|
|
line = "NetHack rumors file closed for renovation.";
|
|
pline("%s whispers:", The(xname(obj)));
|
|
verbalize("%s", line);
|
|
return;
|
|
}
|
|
|
|
|
|
#endif /* OVLB */
|
|
|
|
/*artifact.c*/
|