1 | /* SCCS Id: @(#)artifact.c 3.3 2000/01/11 */ 2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3 | /* NetHack may be freely redistributed. See license for details. */ 4 | 5 | #include "hack.h" 6 | #include "artifact.h" 7 | #ifdef OVLB 8 | #include "artilist.h" 9 | #else 10 | STATIC_DCL struct artifact artilist[]; 11 | #endif 12 | /* 13 | * Note: both artilist[] and artiexist[] have a dummy element #0, 14 | * so loops over them should normally start at #1. The primary 15 | * exception is the save & restore code, which doesn't care about 16 | * the contents, just the total size. 17 | */ 18 | 19 | extern boolean notonhead; /* for long worms */ 20 | 21 | #define get_artifact(o) \ 22 | (((o)&&(o)->oartifact) ? &artilist[(int) (o)->oartifact] : 0) 23 | STATIC_DCL int FDECL(spec_applies, (const struct artifact *,struct monst *)); 24 | STATIC_DCL int FDECL(arti_invoke, (struct obj*)); 25 | 26 | /* The amount added to normal damage which should guarantee that the 27 | victim will be killed even after damage bonus/penalty adjustments. 28 | It needs to be big enough so that halving will still kill, but not 29 | so big that doubling ends up overflowing 15 bits. This value used 30 | to be 1234, but it was possible for players to accumulate enough 31 | hit points so that taking (uhp + 1234)/2 damage was survivable. */ 32 | #define FATAL_DAMAGE 9999 33 | 34 | #ifndef OVLB 35 | STATIC_DCL int spec_dbon_applies; 36 | STATIC_DCL xchar artidisco[NROFARTIFACTS]; 37 | #else /* OVLB */ 38 | /* coordinate effects from spec_dbon() with messages in artifact_hit() */ 39 | STATIC_OVL int spec_dbon_applies = 0; 40 | 41 | /* flags including which artifacts have already been created */ 42 | static boolean artiexist[1+NROFARTIFACTS+1]; 43 | /* and a discovery list for them (no dummy first entry here) */ 44 | STATIC_OVL xchar artidisco[NROFARTIFACTS]; 45 | 46 | STATIC_DCL void NDECL(hack_artifacts); 47 | STATIC_DCL boolean FDECL(attacks, (int,struct obj *)); 48 | 49 | /* handle some special cases; must be called after u_init() */ 50 | STATIC_OVL void 51 | hack_artifacts() 52 | { 53 | struct artifact *art; 54 | int alignmnt = aligns[flags.initalign].value; 55 | 56 | /* Fix up the alignments of "gift" artifacts */ 57 | for (art = artilist+1; art->otyp; art++) 58 | if (art->role == Role_switch && art->alignment != A_NONE) 59 | art->alignment = alignmnt; 60 | 61 | /* Excalibur can be used by any lawful character, not just knights */ 62 | if (!Role_if(PM_KNIGHT)) 63 | artilist[ART_EXCALIBUR].role = 0; /****FIXME****/ 64 | 65 | /* Fix up the quest artifact */ 66 | if (urole.questarti) { 67 | artilist[urole.questarti].alignment = alignmnt; 68 | artilist[urole.questarti].role = Role_switch; 69 | } 70 | return; 71 | } 72 | 73 | /* zero out the artifact existence list */ 74 | void 75 | init_artifacts() 76 | { 77 | (void) memset((genericptr_t) artiexist, 0, sizeof artiexist); 78 | (void) memset((genericptr_t) artidisco, 0, sizeof artidisco); 79 | hack_artifacts(); 80 | } 81 | 82 | void 83 | save_artifacts(fd) 84 | int fd; 85 | { 86 | bwrite(fd, (genericptr_t) artiexist, sizeof artiexist); 87 | bwrite(fd, (genericptr_t) artidisco, sizeof artidisco); 88 | } 89 | 90 | void 91 | restore_artifacts(fd) 92 | int fd; 93 | { 94 | mread(fd, (genericptr_t) artiexist, sizeof artiexist); 95 | mread(fd, (genericptr_t) artidisco, sizeof artidisco); 96 | hack_artifacts(); /* redo non-saved special cases */ 97 | } 98 | 99 | const char * 100 | artiname(artinum) 101 | int artinum; 102 | { 103 | if (artinum <= 0 || artinum > NROFARTIFACTS) return(""); 104 | return(artilist[artinum].name); 105 | } 106 | 107 | /* 108 | Make an artifact. If a specific alignment is specified, then an object of 109 | the appropriate alignment is created from scratch, or 0 is returned if 110 | none is available. (If at least one aligned artifact has already been 111 | given, then unaligned ones also become eligible for this.) 112 | If no alignment is given, then 'otmp' is converted 113 | into an artifact of matching type, or returned as-is if that's not possible. 114 | For the 2nd case, caller should use ``obj = mk_artifact(obj, A_NONE);'' 115 | for the 1st, ``obj = mk_artifact((struct obj *)0, some_alignment);''. 116 | */ 117 | struct obj * 118 | mk_artifact(otmp, alignment) 119 | struct obj *otmp; /* existing object; ignored if alignment specified */ 120 | aligntyp alignment; /* target alignment, or A_NONE */ 121 | { 122 | const struct artifact *a; 123 | int n, m; 124 | boolean by_align = (alignment != A_NONE); 125 | short o_typ = (by_align || !otmp) ? 0 : otmp->otyp; 126 | boolean unique = !by_align && otmp && objects[o_typ].oc_unique; 127 | short eligible[NROFARTIFACTS]; 128 | 129 | /* gather eligible artifacts */ 130 | for (n = 0, a = artilist+1, m = 1; a->otyp; a++, m++) 131 | if ((!by_align ? a->otyp == o_typ : 132 | (a->alignment == alignment || 133 | (a->alignment == A_NONE && u.ugifts > 0))) && 134 | (!(a->spfx & SPFX_NOGEN) || unique) && !artiexist[m]) { 135 | if (by_align && a->race != NON_PM && race_hostile(&mons[a->race])) 136 | continue; /* skip enemies' equipment */ 137 | else if (by_align && Role_if(a->role)) 138 | goto make_artif; /* 'a' points to the desired one */ 139 | else 140 | eligible[n++] = m; 141 | } 142 | 143 | if (n) { /* found at least one candidate */ 144 | m = eligible[rn2(n)]; /* [0..n-1] */ 145 | a = &artilist[m]; 146 | 147 | /* make an appropriate object if necessary, then christen it */ 148 | make_artif: if (by_align) otmp = mksobj((int)a->otyp, TRUE, FALSE); 149 | otmp = oname(otmp, a->name); 150 | otmp->oartifact = m; 151 | artiexist[m] = TRUE; 152 | } else { 153 | /* nothing appropriate could be found; return the original object */ 154 | if (by_align) otmp = 0; /* (there was no original object) */ 155 | } 156 | return otmp; 157 | } 158 | 159 | /* 160 | * Returns the full name (with articles and correct capitalization) of an 161 | * artifact named "name" if one exists, or NULL, it not. 162 | * The given name must be rather close to the real name for it to match. 163 | * The object type of the artifact is returned in otyp if the return value 164 | * is non-NULL. 165 | */ 166 | const char* 167 | artifact_name(name, otyp) 168 | const char *name; 169 | short *otyp; 170 | { 171 | register const struct artifact *a; 172 | register const char *aname; 173 | 174 | if(!strncmpi(name, "the ", 4)) name += 4; 175 | 176 | for (a = artilist+1; a->otyp; a++) { 177 | aname = a->name; 178 | if(!strncmpi(aname, "the ", 4)) aname += 4; 179 | if(!strcmpi(name, aname)) { 180 | *otyp = a->otyp; 181 | return a->name; 182 | } 183 | } 184 | 185 | return (char *)0; 186 | } 187 | 188 | boolean 189 | exist_artifact(otyp, name) 190 | register int otyp; 191 | register const char *name; 192 | { 193 | register const struct artifact *a; 194 | register boolean *arex; 195 | 196 | if (otyp && *name) 197 | for (a = artilist+1,arex = artiexist+1; a->otyp; a++,arex++) 198 | if ((int) a->otyp == otyp && !strcmp(a->name, name)) 199 | return *arex; 200 | return FALSE; 201 | } 202 | 203 | void 204 | artifact_exists(otmp, name, mod) 205 | register struct obj *otmp; 206 | register const char *name; 207 | register boolean mod; 208 | { 209 | register const struct artifact *a; 210 | 211 | if (otmp && *name) 212 | for (a = artilist+1; a->otyp; a++) 213 | if (a->otyp == otmp->otyp && !strcmp(a->name, name)) { 214 | register int m = a - artilist; 215 | otmp->oartifact = (char)(mod ? m : 0); 216 | otmp->age = 0; 217 | if(otmp->otyp == RIN_INCREASE_DAMAGE) 218 | otmp->spe = 0; 219 | artiexist[m] = mod; 220 | break; 221 | } 222 | return; 223 | } 224 | 225 | int 226 | nartifact_exist() 227 | { 228 | int a = 0; 229 | int n = SIZE(artiexist); 230 | 231 | while(n > 1) 232 | if(artiexist[--n]) a++; 233 | 234 | return a; 235 | } 236 | #endif /* OVLB */ 237 | #ifdef OVL0 238 | 239 | boolean 240 | spec_ability(otmp, abil) 241 | struct obj *otmp; 242 | unsigned long abil; 243 | { 244 | const struct artifact *arti = get_artifact(otmp); 245 | 246 | return((boolean)(arti && (arti->spfx & abil))); 247 | } 248 | 249 | #endif /* OVL0 */ 250 | #ifdef OVLB 251 | 252 | boolean 253 | restrict_name(otmp, name) /* returns 1 if name is restricted for otmp->otyp */ 254 | register struct obj *otmp; 255 | register const char *name; 256 | { 257 | register const struct artifact *a; 258 | register const char *aname; 259 | 260 | if (!*name) return FALSE; 261 | if (!strncmpi(name, "the ", 4)) name += 4; 262 | 263 | /* Since almost every artifact is SPFX_RESTR, it doesn't cost 264 | us much to do the string comparison before the spfx check. 265 | Bug fix: don't name multiple elven daggers "Sting". 266 | */ 267 | for (a = artilist+1; a->otyp; a++) { 268 | if (a->otyp != otmp->otyp) continue; 269 | aname = a->name; 270 | if (!strncmpi(aname, "the ", 4)) aname += 4; 271 | if (!strcmp(aname, name)) 272 | return ((boolean)((a->spfx & (SPFX_NOGEN|SPFX_RESTR)) != 0 || 273 | otmp->quan > 1L)); 274 | } 275 | 276 | return FALSE; 277 | } 278 | 279 | STATIC_OVL boolean 280 | attacks(adtyp, otmp) 281 | register int adtyp; 282 | register struct obj *otmp; 283 | { 284 | register const struct artifact *weap; 285 | 286 | if ((weap = get_artifact(otmp)) != 0) 287 | return((boolean)(weap->attk.adtyp == adtyp)); 288 | return FALSE; 289 | } 290 | 291 | boolean 292 | defends(adtyp, otmp) 293 | register int adtyp; 294 | register struct obj *otmp; 295 | { 296 | register const struct artifact *weap; 297 | 298 | if ((weap = get_artifact(otmp)) != 0) 299 | return((boolean)(weap->defn.adtyp == adtyp)); 300 | return FALSE; 301 | } 302 | 303 | /* used for monsters */ 304 | boolean 305 | protects(adtyp, otmp) 306 | int adtyp; 307 | struct obj *otmp; 308 | { 309 | register const struct artifact *weap; 310 | 311 | if ((weap = get_artifact(otmp)) != 0) 312 | return (boolean)(weap->cary.adtyp == adtyp); 313 | return FALSE; 314 | } 315 | 316 | /* 317 | * a potential artifact has just been worn/wielded/picked-up or 318 | * unworn/unwielded/dropped. Pickup/drop only set/reset the W_ART mask. 319 | */ 320 | void 321 | set_artifact_intrinsic(otmp,on,wp_mask) 322 | register struct obj *otmp; 323 | boolean on; 324 | long wp_mask; 325 | { 326 | long *mask = 0; 327 | register const struct artifact *oart = get_artifact(otmp); 328 | uchar dtyp; 329 | long spfx; 330 | 331 | if (!oart) return; 332 | 333 | /* effects from the defn field */ 334 | dtyp = (wp_mask != W_ART) ? oart->defn.adtyp : oart->cary.adtyp; 335 | 336 | if (dtyp == AD_FIRE) 337 | mask = &EFire_resistance; 338 | else if (dtyp == AD_COLD) 339 | mask = &ECold_resistance; 340 | else if (dtyp == AD_ELEC) 341 | mask = &EShock_resistance; 342 | else if (dtyp == AD_MAGM) 343 | mask = &EAntimagic; 344 | else if (dtyp == AD_DISN) 345 | mask = &EDisint_resistance; 346 | else if (dtyp == AD_DRST) 347 | mask = &EPoison_resistance; 348 | 349 | if (mask && wp_mask == W_ART && !on) { 350 | /* find out if some other artifact also confers this intrinsic */ 351 | /* if so, leave the mask alone */ 352 | register struct obj* obj; 353 | for(obj = invent; obj; obj = obj->nobj) 354 | if(obj != otmp && obj->oartifact) { 355 | register const struct artifact *art = get_artifact(obj); 356 | if(art->cary.adtyp == dtyp) { 357 | mask = (long *) 0; 358 | break; 359 | } 360 | } 361 | } 362 | if (mask) { 363 | if (on) *mask |= wp_mask; 364 | else *mask &= ~wp_mask; 365 | } 366 | 367 | /* intrinsics from the spfx field; there could be more than one */ 368 | spfx = (wp_mask != W_ART) ? oart->spfx : oart->cspfx; 369 | if(spfx && wp_mask == W_ART && !on) { 370 | /* don't change any spfx also conferred by other artifacts */ 371 | register struct obj* obj; 372 | for(obj = invent; obj; obj = obj->nobj) 373 | if(obj != otmp && obj->oartifact) { 374 | register const struct artifact *art = get_artifact(obj); 375 | spfx &= ~art->cspfx; 376 | } 377 | } 378 | 379 | if (spfx & SPFX_SEARCH) { 380 | if(on) ESearching |= wp_mask; 381 | else ESearching &= ~wp_mask; 382 | } 383 | if (spfx & SPFX_HALRES) { 384 | /* make_hallucinated must (re)set the mask itself to get 385 | * the display right */ 386 | /* restoring needed because this is the only artifact intrinsic 387 | * that can print a message--need to guard against being printed 388 | * when restoring a game 389 | */ 390 | make_hallucinated((long)!on, restoring ? FALSE : TRUE, wp_mask); 391 | } 392 | if (spfx & SPFX_ESP) { 393 | if(on) ETelepat |= wp_mask; 394 | else ETelepat &= ~wp_mask; 395 | see_monsters(); 396 | } 397 | if (spfx & SPFX_STLTH) { 398 | if (on) EStealth |= wp_mask; 399 | else EStealth &= ~wp_mask; 400 | } 401 | if (spfx & SPFX_REGEN) { 402 | if (on) ERegeneration |= wp_mask; 403 | else ERegeneration &= ~wp_mask; 404 | } 405 | if (spfx & SPFX_TCTRL) { 406 | if (on) ETeleport_control |= wp_mask; 407 | else ETeleport_control &= ~wp_mask; 408 | } 409 | if (spfx & SPFX_WARN) { 410 | if (spec_m2(otmp)) { 411 | if (on) { 412 | EWarn_of_mon |= wp_mask; 413 | flags.warntype |= spec_m2(otmp); 414 | } else { 415 | EWarn_of_mon &= ~wp_mask; 416 | flags.warntype &= ~spec_m2(otmp); 417 | } 418 | see_monsters(); 419 | } else { 420 | if (on) EWarning |= wp_mask; 421 | else EWarning &= ~wp_mask; 422 | } 423 | } 424 | if (spfx & SPFX_EREGEN) { 425 | if (on) EEnergy_regeneration |= wp_mask; 426 | else EEnergy_regeneration &= ~wp_mask; 427 | } 428 | if (spfx & SPFX_HSPDAM) { 429 | if (on) EHalf_spell_damage |= wp_mask; 430 | else EHalf_spell_damage &= ~wp_mask; 431 | } 432 | if (spfx & SPFX_HPHDAM) { 433 | if (on) EHalf_physical_damage |= wp_mask; 434 | else EHalf_physical_damage &= ~wp_mask; 435 | } 436 | if (spfx & SPFX_XRAY) { 437 | /* this assumes that no one else is using xray_range */ 438 | if (on) u.xray_range = 3; 439 | else u.xray_range = -1; 440 | } 441 | if ((spfx & SPFX_REFLECT) && (wp_mask & W_WEP)) { 442 | if (on) EReflecting |= wp_mask; 443 | else EReflecting &= ~wp_mask; 444 | } 445 | 446 | if(wp_mask == W_ART && !on && oart->inv_prop) { 447 | /* might have to turn off invoked power too */ 448 | if (oart->inv_prop <= LAST_PROP && 449 | (u.uprops[oart->inv_prop].extrinsic & W_ARTI)) 450 | (void) arti_invoke(otmp); 451 | } 452 | } 453 | 454 | /* 455 | * creature (usually player) tries to touch (pick up or wield) an artifact obj. 456 | * Returns 0 if the object refuses to be touched. 457 | * This routine does not change any object chains. 458 | * Ignores such things as gauntlets, assuming the artifact is not 459 | * fooled by such trappings. 460 | */ 461 | int 462 | touch_artifact(obj,mon) 463 | struct obj *obj; 464 | struct monst *mon; 465 | { 466 | register const struct artifact *oart = get_artifact(obj); 467 | boolean badclass, badalign, self_willed, yours; 468 | 469 | if(!oart) return 1; 470 | 471 | yours = (mon == &youmonst); 472 | /* all quest artifacts are self-willed; it this ever changes, `badclass' 473 | will have to be extended to explicitly include quest artifacts */ 474 | self_willed = ((oart->spfx & SPFX_INTEL) != 0); 475 | if (yours || !(mon->data->mflags3 & M3_WANTSALL)) { 476 | badclass = (self_willed && (!yours || 477 | (oart->role != NON_PM && !Role_if(oart->role)) || 478 | (oart->race != NON_PM && !Race_if(oart->race)))); 479 | badalign = (oart->spfx & SPFX_RESTR) && 480 | oart->alignment != A_NONE && 481 | ((oart->alignment != 482 | (yours ? u.ualign.type : sgn(mon->data->maligntyp))) || 483 | (yours && u.ualign.record < 0)); 484 | } else { /* an M3_WANTSxxx monster */ 485 | /* special monsters trying to take the Amulet, invocation tools or 486 | quest item can touch anything except for `spec_applies' artifacts */ 487 | badclass = badalign = FALSE; 488 | } 489 | /* weapons which attack specific categories of monsters are 490 | bad for them even if their alignments happen to match */ 491 | if (!badalign && (oart->spfx & SPFX_DBONUS) != 0) { 492 | struct artifact tmp; 493 | 494 | tmp = *oart; 495 | tmp.spfx &= SPFX_DBONUS; 496 | badalign = !!spec_applies(&tmp, mon); 497 | } 498 | 499 | if (((badclass || badalign) && self_willed) || 500 | (badalign && (!yours || !rn2(4)))) { 501 | int dmg; 502 | char buf[BUFSZ]; 503 | 504 | if (!yours) return 0; 505 | You("are blasted by %s power!", s_suffix(the(xname(obj)))); 506 | dmg = d((Antimagic ? 2 : 4), (self_willed ? 10 : 4)); 507 | Sprintf(buf, "touching %s", oart->name); 508 | losehp(dmg, buf, KILLED_BY); 509 | exercise(A_WIS, FALSE); 510 | } 511 | 512 | /* can pick it up unless you're totally non-synch'd with the artifact */ 513 | if (badclass && badalign && self_willed) { 514 | if (yours) pline("%s evades your grasp!", The(xname(obj))); 515 | return 0; 516 | } 517 | 518 | return 1; 519 | } 520 | 521 | #endif /* OVLB */ 522 | #ifdef OVL1 523 | 524 | /* decide whether an artifact's special attacks apply against mtmp */ 525 | STATIC_OVL int 526 | spec_applies(weap, mtmp) 527 | register const struct artifact *weap; 528 | struct monst *mtmp; 529 | { 530 | struct permonst *ptr; 531 | boolean yours; 532 | 533 | if(!(weap->spfx & (SPFX_DBONUS | SPFX_ATTK))) 534 | return(weap->attk.adtyp == AD_PHYS); 535 | 536 | yours = (mtmp == &youmonst); 537 | ptr = mtmp->data; 538 | 539 | if (weap->spfx & SPFX_DMONS) { 540 | return (ptr == &mons[(int)weap->mtype]); 541 | } else if (weap->spfx & SPFX_DCLAS) { 542 | return (weap->mtype == (unsigned long)ptr->mlet); 543 | } else if (weap->spfx & SPFX_DFLAG1) { 544 | return ((ptr->mflags1 & weap->mtype) != 0L); 545 | } else if (weap->spfx & SPFX_DFLAG2) { 546 | return ((ptr->mflags2 & weap->mtype) || 547 | (yours && !Upolyd && (urace.selfmask & weap->mtype))); 548 | } else if (weap->spfx & SPFX_DALIGN) { 549 | return yours ? (u.ualign.type != weap->alignment) : 550 | (ptr->maligntyp == A_NONE || 551 | sgn(ptr->maligntyp) != weap->alignment); 552 | } else if (weap->spfx & SPFX_ATTK) { 553 | struct obj *defending_weapon = (yours ? uwep : MON_WEP(mtmp)); 554 | 555 | if (defending_weapon && defending_weapon->oartifact && 556 | defends((int)weap->attk.adtyp, defending_weapon)) 557 | return FALSE; 558 | switch(weap->attk.adtyp) { 559 | case AD_FIRE: 560 | return !(yours ? Fire_resistance : resists_fire(mtmp)); 561 | case AD_COLD: 562 | return !(yours ? Cold_resistance : resists_cold(mtmp)); 563 | case AD_ELEC: 564 | return !(yours ? Shock_resistance : resists_elec(mtmp)); 565 | case AD_MAGM: 566 | case AD_STUN: 567 | return !(yours ? Antimagic : (rn2(100) < ptr->mr)); 568 | case AD_DRST: 569 | return !(yours ? Poison_resistance : resists_poison(mtmp)); 570 | case AD_DRLI: 571 | return !(yours ? Drain_resistance : resists_drli(mtmp)); 572 | case AD_STON: 573 | return !(yours ? Stone_resistance : resists_ston(mtmp)); 574 | default: impossible("Weird weapon special attack."); 575 | } 576 | } 577 | return(0); 578 | } 579 | 580 | /* return the M2 flags of monster that an artifact's special attacks apply against */ 581 | long 582 | spec_m2(otmp) 583 | struct obj *otmp; 584 | { 585 | register const struct artifact *artifact = get_artifact(otmp); 586 | if (artifact) 587 | return artifact->mtype; 588 | return 0L; 589 | } 590 | 591 | /* special attack bonus */ 592 | int 593 | spec_abon(otmp, mon) 594 | struct obj *otmp; 595 | struct monst *mon; 596 | { 597 | register const struct artifact *weap = get_artifact(otmp); 598 | 599 | if (weap && spec_applies(weap, mon)) 600 | return weap->attk.damn ? rnd((int)weap->attk.damn) : 0; 601 | return 0; 602 | } 603 | 604 | /* special damage bonus */ 605 | int 606 | spec_dbon(otmp, mon, tmp) 607 | struct obj *otmp; 608 | struct monst *mon; 609 | int tmp; 610 | { 611 | register const struct artifact *weap = get_artifact(otmp); 612 | 613 | if ((spec_dbon_applies = (weap && spec_applies(weap, mon))) != 0) 614 | return weap->attk.damd ? rnd((int)weap->attk.damd) : max(tmp,1); 615 | return 0; 616 | } 617 | 618 | /* add identified artifact to discoveries list */ 619 | void 620 | discover_artifact(m) 621 | xchar m; 622 | { 623 | int i; 624 | 625 | /* look for this artifact in the discoveries list; 626 | if we hit an empty slot then it's not present, so add it */ 627 | for (i = 0; i < NROFARTIFACTS; i++) 628 | if (artidisco[i] == 0 || artidisco[i] == m) { 629 | artidisco[i] = m; 630 | return; 631 | } 632 | /* there is one slot per artifact, so we should never reach the 633 | end without either finding the artifact or an empty slot... */ 634 | impossible("couldn't discover artifact (%d)", (int)m); 635 | } 636 | 637 | /* used to decide whether an artifact has been fully identified */ 638 | boolean 639 | undiscovered_artifact(m) 640 | xchar m; 641 | { 642 | int i; 643 | 644 | /* look for this artifact in the discoveries list; 645 | if we hit an empty slot then it's undiscovered */ 646 | for (i = 0; i < NROFARTIFACTS; i++) 647 | if (artidisco[i] == m) 648 | return FALSE; 649 | else if (artidisco[i] == 0) 650 | break; 651 | return TRUE; 652 | } 653 | 654 | /* display a list of discovered artifacts; return their count */ 655 | int 656 | disp_artifact_discoveries(tmpwin) 657 | winid tmpwin; /* supplied by dodiscover() */ 658 | { 659 | int i, m, otyp; 660 | char buf[BUFSZ]; 661 | 662 | for (i = 0; i < NROFARTIFACTS; i++) { 663 | if (artidisco[i] == 0) break; /* empty slot implies end of list */ 664 | if (i == 0) putstr(tmpwin, ATR_INVERSE, "Artifacts"); 665 | m = artidisco[i]; 666 | otyp = artilist[m].otyp; 667 | Sprintf(buf, " %s [%s %s]", artiname(m), 668 | align_str(artilist[m].alignment), simple_typename(otyp)); 669 | putstr(tmpwin, 0, buf); 670 | } 671 | return i; 672 | } 673 | 674 | #endif /* OVL1 */ 675 | 676 | #ifdef OVLB 677 | 678 | /* Function used when someone attacks someone else with an artifact 679 | * weapon. Only adds the special (artifact) damage, and returns a 1 if it 680 | * did something special (in which case the caller won't print the normal 681 | * hit message). This should be called once upon every artifact attack; 682 | * dmgval() no longer takes artifact bonuses into account. Possible 683 | * extension: change the killer so that when an orc kills you with 684 | * Stormbringer it's "killed by Stormbringer" instead of "killed by an orc". 685 | */ 686 | boolean 687 | artifact_hit(magr, mdef, otmp, dmgptr, dieroll) 688 | struct monst *magr, *mdef; 689 | struct obj *otmp; 690 | int *dmgptr; 691 | int dieroll; /* needed for Magicbane and vorpal blades */ 692 | { 693 | boolean youattack = (magr == &youmonst); 694 | boolean youdefend = (mdef == &youmonst); 695 | boolean vis = (!youattack && magr && cansee(magr->mx, magr->my)) 696 | || (!youdefend && cansee(mdef->mx, mdef->my)); 697 | boolean realizes_damage; 698 | 699 | static const char you[] = "you"; 700 | const char *hittee = youdefend ? you : mon_nam(mdef); 701 | 702 | /* The following takes care of most of the damage, but not all-- 703 | * the exception being for level draining, which is specially 704 | * handled. Messages are done in this function, however. 705 | */ 706 | *dmgptr += spec_dbon(otmp, mdef, *dmgptr); 707 | 708 | if (youattack && youdefend) { 709 | impossible("attacking yourself with weapon?"); 710 | return FALSE; 711 | } else if (!spec_dbon_applies) { 712 | /* since damage bonus didn't apply, nothing more to do */ 713 | return FALSE; 714 | } 715 | 716 | realizes_damage = (youdefend || vis); 717 | 718 | /* the four basic attacks: fire, cold, shock and missiles */ 719 | if (attacks(AD_FIRE, otmp)) { 720 | if (realizes_damage) { 721 | pline_The("fiery blade %s %s!", 722 | (mdef->data == &mons[PM_WATER_ELEMENTAL]) ? 723 | "vaporizes part of" : "burns", hittee); 724 | if (!rn2(4)) (void) destroy_mitem(mdef, POTION_CLASS, AD_FIRE); 725 | if (!rn2(4)) (void) destroy_mitem(mdef, SCROLL_CLASS, AD_FIRE); 726 | if (!rn2(7)) (void) destroy_mitem(mdef, SPBOOK_CLASS, AD_FIRE); 727 | return TRUE; 728 | } 729 | } 730 | if (attacks(AD_COLD, otmp)) { 731 | if (realizes_damage) { 732 | pline_The("ice-cold blade freezes %s!", hittee); 733 | if (!rn2(4)) (void) destroy_mitem(mdef, POTION_CLASS, AD_COLD); 734 | return TRUE; 735 | } 736 | } 737 | if (attacks(AD_ELEC, otmp)) { 738 | if (realizes_damage) { 739 | if(youattack && otmp != uwep) 740 | pline("%s hits %s!", The(xname(otmp)), hittee); 741 | pline("Lightning strikes %s!", hittee); 742 | if (!rn2(5)) (void) destroy_mitem(mdef, RING_CLASS, AD_ELEC); 743 | if (!rn2(5)) (void) destroy_mitem(mdef, WAND_CLASS, AD_ELEC); 744 | return TRUE; 745 | } 746 | } 747 | if (attacks(AD_MAGM, otmp)) { 748 | if (realizes_damage) { 749 | if(youattack && otmp != uwep) 750 | pline("%s hits %s!", The(xname(otmp)), hittee); 751 | pline("A hail of magic missiles strikes %s!", hittee); 752 | return TRUE; 753 | } 754 | } 755 | 756 | /* 757 | * Magicbane's intrinsic magic is incompatible with normal 758 | * enchantment magic. Thus, its effects have a negative 759 | * dependence on spe. Against low mr victims, it typically 760 | * does "double athame" damage, 2d4. Occasionally, it will 761 | * cast unbalancing magic which effectively averages out to 762 | * 4d4 damage (2.5d4 against high mr victims), for spe = 0. 763 | */ 764 | 765 | #define MB_MAX_DIEROLL 8 /* rolls above this aren't magical */ 766 | #define MB_INDEX_INIT (-1) 767 | #define MB_INDEX_PROBE 0 768 | #define MB_INDEX_STUN 1 769 | #define MB_INDEX_SCARE 2 770 | #define MB_INDEX_PURGE 3 771 | #define MB_RESIST_ATTACK (resist_index = attack_index) 772 | #define MB_RESISTED_ATTACK (resist_index == attack_index) 773 | #define MB_UWEP_ATTACK (youattack && (otmp == uwep)) 774 | 775 | if (attacks(AD_STUN, otmp) && (dieroll <= MB_MAX_DIEROLL)) { 776 | int attack_index = MB_INDEX_INIT; 777 | int resist_index = MB_INDEX_INIT; 778 | int scare_dieroll = MB_MAX_DIEROLL / 2; 779 | 780 | if (otmp->spe >= 3) 781 | scare_dieroll /= (1 << (otmp->spe / 3)); 782 | 783 | *dmgptr += rnd(4); /* 3d4 */ 784 | 785 | if (otmp->spe > rn2(10)) /* probe */ 786 | attack_index = MB_INDEX_PROBE; 787 | else { /* stun */ 788 | attack_index = MB_INDEX_STUN; 789 | *dmgptr += rnd(4); /* 4d4 */ 790 | 791 | if (youdefend) 792 | make_stunned((HStun + 3), FALSE); 793 | else 794 | mdef->mstun = 1; 795 | } 796 | if (dieroll <= scare_dieroll) { /* scare */ 797 | attack_index = MB_INDEX_SCARE; 798 | *dmgptr += rnd(4); /* 5d4 */ 799 | 800 | if (youdefend) { 801 | if (Antimagic) 802 | MB_RESIST_ATTACK; 803 | else { 804 | nomul(-3); 805 | nomovemsg = ""; 806 | if ((magr == u.ustuck) 807 | && sticks(youmonst.data)) { 808 | u.ustuck = (struct monst *)0; 809 | You("release %s!", mon_nam(magr)); 810 | } 811 | } 812 | } else if (youattack) { 813 | if (rn2(2) && resist(mdef,SPBOOK_CLASS,0,0)) { 814 | MB_RESIST_ATTACK; 815 | } else { 816 | if (mdef == u.ustuck) { 817 | if (u.uswallow) 818 | expels(mdef,mdef->data,TRUE); 819 | else { 820 | if (!sticks(youmonst.data)) { 821 | u.ustuck = (struct monst *)0; 822 | You("get released!"); 823 | } 824 | } 825 | } 826 | mdef->mflee = 1; 827 | mdef->mfleetim += 3; 828 | } 829 | } 830 | } 831 | if (dieroll <= (scare_dieroll / 2)) { /* purge */ 832 | struct obj *ospell; 833 | struct permonst *old_uasmon = youmonst.data; 834 | 835 | attack_index = MB_INDEX_PURGE; 836 | *dmgptr += rnd(4); /* 6d4 */ 837 | 838 | /* Create a fake spell object, ala spell.c */ 839 | ospell = mksobj(SPE_CANCELLATION, FALSE, FALSE); 840 | ospell->blessed = ospell->cursed = 0; 841 | ospell->quan = 20L; 842 | 843 | cancel_monst(mdef, ospell, youattack, FALSE, FALSE); 844 | 845 | if (youdefend) { 846 | if (old_uasmon != youmonst.data) 847 | /* rehumanized, no more damage */ 848 | *dmgptr = 0; 849 | if (Antimagic) 850 | MB_RESIST_ATTACK; 851 | } else { 852 | if (!mdef->mcan) 853 | MB_RESIST_ATTACK; 854 | 855 | /* cancelled clay golems will die ... */ 856 | else if (mdef->data == &mons[PM_CLAY_GOLEM]) 857 | mdef->mhp = 1; 858 | } 859 | 860 | obfree(ospell, (struct obj *)0); 861 | } 862 | 863 | if (youdefend || mdef->mhp > 0) { /* ??? -dkh- */ 864 | static const char *mb_verb[4] = 865 | {"probe", "stun", "scare", "purge"}; 866 | 867 | if (youattack || youdefend || vis) { 868 | pline_The("magic-absorbing blade %ss %s!", 869 | mb_verb[attack_index], hittee); 870 | 871 | if (MB_RESISTED_ATTACK) { 872 | pline("%s resist%s!", 873 | youdefend ? "You" : Monnam(mdef), 874 | youdefend ? "" : "s"); 875 | 876 | shieldeff(youdefend ? u.ux : mdef->mx, 877 | youdefend ? u.uy : mdef->my); 878 | } 879 | } 880 | 881 | /* Much ado about nothing. More magic fanfare! */ 882 | if (MB_UWEP_ATTACK) { 883 | if (attack_index == MB_INDEX_PURGE) { 884 | if (!MB_RESISTED_ATTACK && 885 | attacktype(mdef->data, AT_MAGC)) { 886 | You("absorb magical energy!"); 887 | u.uenmax++; 888 | u.uen++; 889 | flags.botl = 1; 890 | } 891 | } else if (attack_index == MB_INDEX_PROBE) { 892 | if (!rn2(4 * otmp->spe)) { 893 | pline_The("probe is insightful!"); 894 | if (!canspotmon(mdef)) 895 | map_invisible(u.ux+u.dx,u.uy+u.dy); 896 | /* pre-damage status */ 897 | probe_monster(mdef); 898 | } 899 | } 900 | } else if (youdefend && !MB_RESISTED_ATTACK 901 | && (attack_index == MB_INDEX_PURGE)) { 902 | You("lose magical energy!"); 903 | if (u.uenmax > 0) u.uenmax--; 904 | if (u.uen > 0) u.uen--; 905 | flags.botl = 1; 906 | } 907 | 908 | /* all this magic is confusing ... */ 909 | if (!rn2(12)) { 910 | if (youdefend) 911 | make_confused((HConfusion + 4), FALSE); 912 | else 913 | mdef->mconf = 1; 914 | 915 | if (youattack || youdefend || vis) 916 | pline("%s %s confused.", 917 | youdefend ? "You" : Monnam(mdef), 918 | youdefend ? "are" : "is"); 919 | } 920 | } 921 | return TRUE; 922 | } 923 | /* end of Magicbane code */ 924 | 925 | /* We really want "on a natural 20" but Nethack does it in */ 926 | /* reverse from AD&D. */ 927 | if (spec_ability(otmp, SPFX_BEHEAD)) { 928 | if (otmp->oartifact == ART_TSURUGI_OF_MURAMASA && dieroll == 1) { 929 | /* not really beheading, but so close, why add another SPFX */ 930 | if (youattack && u.uswallow && mdef == u.ustuck) { 931 | You("slice %s wide open!", mon_nam(mdef)); 932 | *dmgptr = mdef->mhp + FATAL_DAMAGE; 933 | return TRUE; 934 | } 935 | if (!youdefend) { 936 | /* allow normal cutworm() call to add extra damage */ 937 | if(notonhead) 938 | return FALSE; 939 | 940 | if (bigmonst(mdef->data)) { 941 | if (youattack) 942 | You("slice deeply into %s!", 943 | mon_nam(mdef)); 944 | else if (vis) 945 | pline("%s cuts deeply into %s!", 946 | Monnam(magr), mon_nam(mdef)); 947 | *dmgptr *= 2; 948 | return TRUE; 949 | } 950 | *dmgptr = mdef->mhp + FATAL_DAMAGE; 951 | pline_The("razor-sharp blade cuts %s in half!", 952 | mon_nam(mdef)); 953 | otmp->dknown = TRUE; 954 | return TRUE; 955 | } else { 956 | if (bigmonst(youmonst.data)) { 957 | pline("%s cuts deeply into you!", 958 | Monnam(magr)); 959 | *dmgptr *= 2; 960 | return TRUE; 961 | } 962 | 963 | /* Players with negative AC's take less damage instead 964 | * of just not getting hit. We must add a large enough 965 | * value to the damage so that this reduction in 966 | * damage does not prevent death. 967 | */ 968 | *dmgptr = (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE; 969 | pline_The("razor-sharp blade cuts you in half!"); 970 | otmp->dknown = TRUE; 971 | return TRUE; 972 | } 973 | } else if (otmp->oartifact == ART_VORPAL_BLADE && 974 | (dieroll == 1 || mdef->data == &mons[PM_JABBERWOCK])) { 975 | static const char *behead_msg[2] = { 976 | "%s beheads %s!", 977 | "%s decapitates %s!" 978 | }; 979 | 980 | if (youattack && u.uswallow && mdef == u.ustuck) 981 | return FALSE; 982 | if (!youdefend) { 983 | if (!has_head(mdef->data) || notonhead || u.uswallow) { 984 | if (youattack) 985 | pline("Somehow, you miss %s wildly.", 986 | mon_nam(mdef)); 987 | else if (vis) 988 | pline("Somehow, %s misses wildly.", 989 | mon_nam(magr)); 990 | *dmgptr = 0; 991 | return ((boolean)(youattack || vis)); 992 | } 993 | if (noncorporeal(mdef->data) || amorphous(mdef->data)) { 994 | pline("%s slices through %s %s.", 995 | artilist[ART_VORPAL_BLADE].name, 996 | s_suffix(mon_nam(mdef)), mbodypart(mdef,NECK)); 997 | return TRUE; 998 | } 999 | *dmgptr = mdef->mhp + FATAL_DAMAGE; 1000 | pline(behead_msg[rn2(SIZE(behead_msg))], 1001 | artilist[ART_VORPAL_BLADE].name, 1002 | mon_nam(mdef)); 1003 | otmp->dknown = TRUE; 1004 | return TRUE; 1005 | } else { 1006 | if (!has_head(youmonst.data)) { 1007 | pline("Somehow, %s misses you wildly.", 1008 | mon_nam(magr)); 1009 | *dmgptr = 0; 1010 | return TRUE; 1011 | } 1012 | if (noncorporeal(youmonst.data) || amorphous(youmonst.data)) { 1013 | pline("%s slices through your %s.", 1014 | artilist[ART_VORPAL_BLADE].name, body_part(NECK)); 1015 | return TRUE; 1016 | } 1017 | *dmgptr = (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE; 1018 | pline(behead_msg[rn2(SIZE(behead_msg))], 1019 | artilist[ART_VORPAL_BLADE].name, "you"); 1020 | otmp->dknown = TRUE; 1021 | /* Should amulets fall off? */ 1022 | return TRUE; 1023 | } 1024 | } 1025 | } 1026 | if (spec_ability(otmp, SPFX_DRLI)) { 1027 | if (!youdefend) { 1028 | if (vis) { 1029 | if(otmp->oartifact == ART_STORMBRINGER) 1030 | pline_The("%s blade draws the life from %s!", 1031 | hcolor(Black), 1032 | mon_nam(mdef)); 1033 | else 1034 | pline("%s draws the life from %s!", 1035 | The(distant_name(otmp, xname)), 1036 | mon_nam(mdef)); 1037 | } 1038 | if (mdef->m_lev == 0) { 1039 | *dmgptr = mdef->mhp + FATAL_DAMAGE; 1040 | } else { 1041 | int drain = rnd(8); 1042 | *dmgptr += drain; 1043 | mdef->mhpmax -= drain; 1044 | mdef->m_lev--; 1045 | drain /= 2; 1046 | if (drain) healup(drain, 0, FALSE, FALSE); 1047 | } 1048 | return vis; 1049 | } else { /* youdefend */ 1050 | int oldhpmax = u.uhpmax; 1051 | 1052 | if (Blind) 1053 | You_feel("an %s drain your life!", 1054 | otmp->oartifact == ART_STORMBRINGER ? 1055 | "unholy blade" : "object"); 1056 | else if (otmp->oartifact == ART_STORMBRINGER) 1057 | pline_The("%s blade drains your life!", 1058 | hcolor(Black)); 1059 | else 1060 | pline("%s drains your life!", 1061 | The(distant_name(otmp, xname))); 1062 | losexp("life drainage"); 1063 | if (magr->mhp < magr->mhpmax) { 1064 | magr->mhp += (u.uhpmax - oldhpmax)/2; 1065 | if (magr->mhp > magr->mhpmax) magr->mhp = magr->mhpmax; 1066 | } 1067 | return TRUE; 1068 | } 1069 | } 1070 | return FALSE; 1071 | } 1072 | 1073 | static NEARDATA const char recharge_type[] = { ALLOW_COUNT, ALL_CLASSES, 0 }; 1074 | static NEARDATA const char invoke_types[] = { ALL_CLASSES, 0 }; 1075 | /* #invoke: an "ugly check" filters out most objects */ 1076 | 1077 | int 1078 | doinvoke() 1079 | { 1080 | register struct obj *obj; 1081 | 1082 | obj = getobj(invoke_types, "invoke"); 1083 | if(!obj) return 0; 1084 | return arti_invoke(obj); 1085 | } 1086 | 1087 | STATIC_OVL int 1088 | arti_invoke(obj) 1089 | register struct obj *obj; 1090 | { 1091 | register const struct artifact *oart = get_artifact(obj); 1092 | 1093 | if(!oart || !oart->inv_prop) { 1094 | if(obj->otyp == CRYSTAL_BALL) 1095 | use_crystal_ball(obj); 1096 | else 1097 | pline(nothing_happens); 1098 | return 1; 1099 | } 1100 | 1101 | if(oart->inv_prop > LAST_PROP) { 1102 | /* It's a special power, not "just" a property */ 1103 | if(obj->age > monstermoves) { 1104 | /* the artifact is tired :-) */ 1105 | You_feel("that %s is ignoring you.", the(xname(obj))); 1106 | /* and just got more so; patience is essential... */ 1107 | obj->age += (long) d(3,10); 1108 | return 1; 1109 | } 1110 | obj->age = monstermoves + rnz(100); 1111 | 1112 | switch(oart->inv_prop) { 1113 | case TAMING: { 1114 | struct obj *pseudo = mksobj(SPE_CHARM_MONSTER, FALSE, FALSE); 1115 | pseudo->blessed = pseudo->cursed = 0; 1116 | pseudo->quan = 20L; /* do not let useup get it */ 1117 | (void) seffects(pseudo); 1118 | obfree(pseudo, (struct obj *)0); /* now, get rid of it */ 1119 | break; 1120 | } 1121 | case HEALING: { 1122 | int healamt = (u.uhpmax + 1 - u.uhp) / 2; 1123 | if (Upolyd) healamt = (u.mhmax + 1 - u.mh) / 2; 1124 | if(healamt || Sick || (Blinded > 1)) 1125 | You_feel("better."); 1126 | else 1127 | goto nothing_special; 1128 | if (healamt > 0) { 1129 | if (Upolyd) u.mh += healamt; 1130 | else u.uhp += healamt; 1131 | } 1132 | if(Sick) make_sick(0L,(char *)0,FALSE,SICK_ALL); 1133 | if(Slimed) Slimed = 0L; 1134 | if(Blinded > 1) make_blinded(0L,FALSE); 1135 | flags.botl = 1; 1136 | break; 1137 | } 1138 | case ENERGY_BOOST: { 1139 | int epboost = (u.uenmax + 1 - u.uen) / 2; 1140 | if (epboost > 120) epboost = 120; /* arbitrary */ 1141 | else if (epboost < 12) epboost = u.uenmax - u.uen; 1142 | if(epboost) { 1143 | You_feel("re-energized."); 1144 | u.uen += epboost; 1145 | flags.botl = 1; 1146 | } else 1147 | goto nothing_special; 1148 | break; 1149 | } 1150 | case UNTRAP: { 1151 | if(!untrap(TRUE)) { 1152 | obj->age = 0; /* don't charge for changing their mind */ 1153 | return 0; 1154 | } 1155 | break; 1156 | } 1157 | case CHARGE_OBJ: { 1158 | struct obj *otmp = getobj(recharge_type, "charge"); 1159 | boolean b_effect; 1160 | 1161 | if (!otmp) { 1162 | obj->age = 0; 1163 | return 0; 1164 | } 1165 | b_effect = obj->blessed && (Role_switch == oart->role || !oart->role); 1166 | recharge(otmp, b_effect ? 1 : obj->cursed ? -1 : 0); 1167 | break; 1168 | } 1169 | case LEV_TELE: 1170 | level_tele(); 1171 | break; 1172 | case CREATE_PORTAL: { 1173 | int i, num_ok_dungeons, last_ok_dungeon = 0; 1174 | d_level newlev; 1175 | extern int n_dgns; /* from dungeon.c */ 1176 | winid tmpwin = create_nhwindow(NHW_MENU); 1177 | anything any; 1178 | 1179 | any.a_void = 0; /* set all bits to zero */ 1180 | start_menu(tmpwin); 1181 | /* use index+1 (cant use 0) as identifier */ 1182 | for (i = num_ok_dungeons = 0; i < n_dgns; i++) { 1183 | if (!dungeons[i].dunlev_ureached) continue; 1184 | any.a_int = i+1; 1185 | add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, 1186 | dungeons[i].dname, MENU_UNSELECTED); 1187 | num_ok_dungeons++; 1188 | last_ok_dungeon = i; 1189 | } 1190 | end_menu(tmpwin, "Open a portal to which dungeon?"); 1191 | if (num_ok_dungeons > 1) { 1192 | /* more than one entry; display menu for choices */ 1193 | menu_item *selected; 1194 | int n; 1195 | 1196 | n = select_menu(tmpwin, PICK_ONE, &selected); 1197 | if (n <= 0) { 1198 | destroy_nhwindow(tmpwin); 1199 | goto nothing_special; 1200 | } 1201 | i = selected[0].item.a_int - 1; 1202 | free((genericptr_t)selected); 1203 | } else 1204 | i = last_ok_dungeon; /* also first & only OK dungeon */ 1205 | destroy_nhwindow(tmpwin); 1206 | 1207 | /* 1208 | * i is now index into dungeon structure for the new dungeon. 1209 | * Find the closest level in the given dungeon, open 1210 | * a use-once portal to that dungeon and go there. 1211 | * The closest level is either the entry or dunlev_ureached. 1212 | */ 1213 | newlev.dnum = i; 1214 | if(dungeons[i].depth_start >= depth(&u.uz)) 1215 | newlev.dlevel = dungeons[i].entry_lev; 1216 | else 1217 | newlev.dlevel = dungeons[i].dunlev_ureached; 1218 | if(u.uhave.amulet || In_endgame(&u.uz) || In_endgame(&newlev) || 1219 | newlev.dnum == u.uz.dnum) { 1220 | You_feel("very disoriented for a moment."); 1221 | } else { 1222 | if(!Blind) You("are surrounded by a shimmering sphere!"); 1223 | else You_feel("weightless for a moment."); 1224 | goto_level(&newlev, FALSE, FALSE, FALSE); 1225 | } 1226 | break; 1227 | } 1228 | case ENLIGHTENING: 1229 | enlightenment(0); 1230 | break; 1231 | case CREATE_AMMO: { 1232 | struct obj *otmp = mksobj(ARROW, TRUE, FALSE); 1233 | 1234 | if (!otmp) goto nothing_special; 1235 | otmp->blessed = obj->blessed; 1236 | otmp->cursed = obj->cursed; 1237 | otmp->bknown = obj->bknown; 1238 | if (obj->blessed) { 1239 | if (otmp->spe < 0) otmp->spe = 0; 1240 | otmp->quan += rnd(10); 1241 | } else if (obj->cursed) { 1242 | if (otmp->spe > 0) otmp->spe = 0; 1243 | } else 1244 | otmp->quan += rnd(5); 1245 | otmp->owt = weight(otmp); 1246 | otmp = hold_another_object(otmp, "Suddenly %s out.", 1247 | aobjnam(otmp, "fall"), (const char *)0); 1248 | break; 1249 | } 1250 | } 1251 | } else { 1252 | long cprop = (u.uprops[oart->inv_prop].extrinsic ^= W_ARTI); 1253 | boolean on = (cprop & W_ARTI) != 0; /* true if invoked prop just set */ 1254 | 1255 | if(on && obj->age > monstermoves) { 1256 | /* the artifact is tired :-) */ 1257 | u.uprops[oart->inv_prop].extrinsic ^= W_ARTI; 1258 | You_feel("that %s is ignoring you.", the(xname(obj))); 1259 | return 1; 1260 | } else if(!on) { 1261 | /* when turning off property, determine downtime */ 1262 | /* arbitrary for now until we can tune this -dlc */ 1263 | obj->age = monstermoves + rnz(100); 1264 | } 1265 | 1266 | if(cprop & ~W_ARTI) { 1267 | nothing_special: 1268 | /* you had the property from some other source too */ 1269 | if (carried(obj)) 1270 | You_feel("a surge of power, but nothing seems to happen."); 1271 | return 1; 1272 | } 1273 | switch(oart->inv_prop) { 1274 | case CONFLICT: 1275 | if(on) You_feel("like a rabble-rouser."); 1276 | else You_feel("the tension decrease around you."); 1277 | break; 1278 | case LEVITATION: 1279 | if(on) float_up(); 1280 | else (void) float_down(I_SPECIAL|TIMEOUT, W_ARTI); 1281 | break; 1282 | case INVIS: 1283 | if (!See_invisible && !Blind) { 1284 | newsym(u.ux,u.uy); 1285 | if (on) { 1286 | Your("body takes on a %s transparency...", 1287 | Hallucination ? "normal" : "strange"); 1288 | } else { 1289 | Your("body seems to unfade..."); 1290 | } 1291 | } else goto nothing_special; 1292 | break; 1293 | } 1294 | } 1295 | 1296 | return 1; 1297 | } 1298 | 1299 | 1300 | /* KMH -- Talking artifacts are finally implemented */ 1301 | void arti_speak(obj) 1302 | struct obj *obj; 1303 | { 1304 | register const struct artifact *oart = get_artifact(obj); 1305 | const char *line; 1306 | char buf[BUFSZ]; 1307 | 1308 | 1309 | /* Is this a speaking artifact? */ 1310 | if (!oart || !(oart->spfx & SPFX_SPEAK)) 1311 | return; 1312 | 1313 | line = getrumor(bcsign(obj), buf, TRUE); 1314 | if (!*line) 1315 | line = "NetHack rumors file closed for renovation."; 1316 | pline("%s whispers:", The(xname(obj))); 1317 | verbalize("%s", line); 1318 | return; 1319 | } 1320 | 1321 | 1322 | #endif /* OVLB */ 1323 | 1324 | /*artifact.c*/