diff --git a/src/artifact.c b/src/artifact.c index dfb436c3d..cd0c37bdd 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -27,6 +27,17 @@ staticfn int spec_applies(const struct artifact *, struct monst *) NONNULLARG12; staticfn int invoke_ok(struct obj *); staticfn void nothing_special(struct obj *) NONNULLARG1; +staticfn int invoke_taming(struct obj *) NONNULLARG1; +staticfn int invoke_healing(struct obj *) NONNULLARG1; +staticfn int invoke_energy_boost(struct obj *) NONNULLARG1; +staticfn int invoke_untrap(struct obj *) NONNULLARG1; +staticfn int invoke_charge_obj(struct obj *) NONNULLARG1; +staticfn int invoke_create_portal(struct obj *) NONNULLARG1; +staticfn int invoke_create_ammo(struct obj *) NONNULLARG1; +staticfn int invoke_banish(struct obj *) NONNULLARG1; +staticfn int invoke_fling_poison(struct obj *) NONNULLARG1; +staticfn int invoke_storm_spell(struct obj *) NONNULLARG1; +staticfn int invoke_blinding_ray(struct obj *) NONNULLARG1; staticfn int arti_invoke(struct obj *); staticfn boolean Mb_hit(struct monst * magr, struct monst *mdef, struct obj *, int *, int, boolean, char *); @@ -1750,10 +1761,331 @@ nothing_special(struct obj *obj) You_feel("a surge of power, but nothing seems to happen."); } +staticfn int +invoke_taming(struct obj *obj UNUSED) +{ + struct obj pseudo; + + pseudo = cg.zeroobj; /* neither cursed nor blessed, zero oextra too */ + pseudo.otyp = SCR_TAMING; + (void) seffects(&pseudo); + return ECMD_TIME; +} + +staticfn int +invoke_healing(struct obj *obj) +{ + 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 || Slimed || Blinded > creamed) + You_feel("better."); + if (healamt || Sick || Slimed || BlindedTimeout > creamed) + You_feel("%sbetter.", + (!healamt && !Sick && !Slimed + /* when healing temporary blindness (aside from + goop covering face), might still be blind + due to PermaBlind or eyeless polymorph; + vary the message in that situation */ + && (HBlinded & ~TIMEOUT) != 0L) ? "slightly " : ""); + else { + nothing_special(obj); + return ECMD_TIME; + } + if (healamt > 0) { + if (Upolyd) + u.mh += healamt; + else + u.uhp += healamt; + } + if (Sick) + make_sick(0L, (char *) 0, FALSE, SICK_ALL); + if (Slimed) + make_slimed(0L, (char *) 0); + if (BlindedTimeout > creamed) + make_blinded(creamed, FALSE); + disp.botl = TRUE; + return ECMD_TIME; +} + +staticfn int +invoke_energy_boost(struct obj *obj) +{ + 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) { + u.uen += epboost; + disp.botl = TRUE; + You_feel("re-energized."); + } else { + nothing_special(obj); + return ECMD_TIME; + } + return ECMD_TIME; +} + +staticfn int +invoke_untrap(struct obj *obj) +{ + if (!untrap(TRUE, 0, 0, (struct obj *) 0)) { + obj->age = 0; /* don't charge for changing their mind */ + return ECMD_CANCEL; + } + return ECMD_TIME; +} + +staticfn int +invoke_charge_obj(struct obj *obj) +{ + const struct artifact *oart = get_artifact(obj); + struct obj *otmp = getobj("charge", charge_ok, + GETOBJ_PROMPT | GETOBJ_ALLOWCNT); + boolean b_effect; + + if (!otmp) { + obj->age = 0; + return ECMD_CANCEL; + } + b_effect = (obj->blessed && (oart->role == Role_switch + || oart->role == NON_PM)); + recharge(otmp, b_effect ? 1 : obj->cursed ? -1 : 0); + update_inventory(); + return ECMD_TIME; +} + +staticfn int +invoke_create_portal(struct obj *obj) +{ + int i, num_ok_dungeons, last_ok_dungeon = 0; + d_level newlev; + winid tmpwin = create_nhwindow(NHW_MENU); + anything any; + int clr = NO_COLOR; + + any = cg.zeroany; /* set all bits to zero */ + start_menu(tmpwin, MENU_BEHAVE_STANDARD); + /* use index+1 (can't use 0) as identifier */ + for (i = num_ok_dungeons = 0; i < svn.n_dgns; i++) { + if (!svd.dungeons[i].dunlev_ureached) + continue; + if (i == tutorial_dnum) /* can't portal into tutorial */ + continue; + any.a_int = i + 1; + add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, + ATR_NONE, clr, + svd.dungeons[i].dname, MENU_ITEMFLAGS_NONE); + 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); + nothing_special(obj); + return ECMD_TIME; + } + 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 (svd.dungeons[i].depth_start >= depth(&u.uz)) + newlev.dlevel = svd.dungeons[i].entry_lev; + else + newlev.dlevel = svd.dungeons[i].dunlev_ureached; + + if (u.uhave.amulet || In_endgame(&u.uz) || In_endgame(&newlev) + || newlev.dnum == u.uz.dnum || !next_to_u()) { + 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); + } + return ECMD_TIME; +} + +staticfn int +invoke_create_ammo(struct obj *obj) +{ + struct obj *otmp = mksobj(ARROW, TRUE, FALSE); + + if (!otmp) { + nothing_special(obj); + return ECMD_TIME; + } + otmp->blessed = obj->blessed; + otmp->cursed = obj->cursed; + otmp->bknown = obj->bknown; + otmp->oeroded = otmp->oeroded2 = 0; + 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"), (char *) 0); + nhUse(otmp); + return ECMD_TIME; +} + +staticfn int +invoke_banish(struct obj *obj UNUSED) +{ + int nvanished = 0, nstayed = 0; + struct monst *mtmp, *mtmp2; + d_level dest; + + find_hell(&dest); + + for (mtmp = fmon; mtmp; mtmp = mtmp2) { + int chance = 1; + + mtmp2 = mtmp->nmon; + if (DEADMONSTER(mtmp) || !isok(mtmp->mx, mtmp->my)) + continue; + if (!is_demon(mtmp->data) && mtmp->data->mlet != S_IMP) + continue; + if (!couldsee(mtmp->mx, mtmp->my)) + continue; + if (mtmp->data->msound == MS_NEMESIS) + continue; + + if (In_quest(&u.uz) && !svq.quest_status.killed_nemesis) + chance += 10; + if (is_dprince(mtmp->data)) + chance += 2; + if (is_dlord(mtmp->data)) + chance++; + + mtmp->msleeping = mtmp->mtame = mtmp->mpeaceful = 0; + if (chance <= 1 || !rn2(chance)) { + if (!Inhell) { + nvanished++; + /* banish to a random level in Gehennom */ + dest.dlevel = rn2(dunlevs_in_dungeon(&dest)); + migrate_mon(mtmp, ledger_no(&dest), MIGR_RANDOM); + } else { + u_teleport_mon(mtmp, FALSE); + } + } else { + nstayed++; + } + } + + if (nvanished) { + char subject[] = "demons"; + + if (nvanished == 1) + *(eos(subject) - 1) = '\0'; /* remove 's' */ + pline("%s %s %s in a cloud of brimstone!", + nstayed ? ((nvanished > nstayed) + ? "Most of the" + : "Some of the") + : "The", + subject, vtense(subject, "disappear")); + } + return ECMD_TIME; +} + +staticfn int +invoke_fling_poison(struct obj *obj) +{ + if (getdir((char *) 0)) { + int venom = rn2(2) ? BLINDING_VENOM : ACID_VENOM; + struct obj *otmp = mksobj(venom, TRUE, FALSE); + + otmp->spe = 1; /* the poison is yours */ + throwit(otmp, 0L, FALSE, (struct obj *) 0); + } else { + /* no direction picked */ + pline("%s", Never_mind); + obj->age = svm.moves; + return ECMD_CANCEL; + } + return ECMD_TIME; +} + +staticfn int +invoke_storm_spell(struct obj *obj) +{ + const struct artifact *oart = get_artifact(obj); + int storm = oart->inv_prop == SNOWSTORM ? SPE_CONE_OF_COLD : SPE_FIREBALL; + int skill = spell_skilltype(storm); + int expertise = P_SKILL(skill); + + P_SKILL(skill) = P_EXPERT; + (void) spelleffects(storm, FALSE, TRUE); + P_SKILL(skill) = expertise; + return ECMD_TIME; +} + +staticfn int +invoke_blinding_ray(struct obj *obj) +{ + if (getdir((char *) 0)) { + if (u.dx || u.dy) { + do_blinding_ray(obj); + } else if (u.dz) { + /* up or down => light this map spot; litroom() uses + radius 0 for Sunsword, except on Rogue level where + whole room gets lit and corridor spots remain unlit */ + litroom(TRUE, obj); + pline("%s", ((!Blind && levl[u.ux][u.uy].lit + && !levl[u.ux][u.uy].waslit) + ? "It is lit here now." + : nothing_seems_to_happen)); + } else { /* zapyourself() */ + boolean vulnerable = (u.umonnum == PM_GREMLIN); + int damg = obj->blessed ? 15 : !obj->cursed ? 10 : 5; + + if (vulnerable) /* could be fatal if Unchanging */ + (void) lightdamage(obj, TRUE, 2 * damg); + + if (!flashburn((long) (damg + rnd(damg)), FALSE) + && !vulnerable) + pline("%s", nothing_seems_to_happen); + } + } else { + /* no direction picked */ + pline("%s", Never_mind); + obj->age = svm.moves; + return ECMD_CANCEL; + } + return ECMD_TIME; +} + staticfn int arti_invoke(struct obj *obj) { const struct artifact *oart; + int res = ECMD_OK; if (!obj) { impossible("arti_invoke without obj"); @@ -1781,300 +2113,29 @@ arti_invoke(struct obj *obj) obj->age = svm.moves + rnz(100); switch (oart->inv_prop) { - case TAMING: { - struct obj pseudo; - - pseudo = - cg.zeroobj; /* neither cursed nor blessed, zero oextra too */ - 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 || Slimed || Blinded > creamed) - You_feel("better."); - if (healamt || Sick || Slimed || BlindedTimeout > creamed) - You_feel("%sbetter.", - (!healamt && !Sick && !Slimed - /* when healing temporary blindness (aside from - goop covering face), might still be blind - due to PermaBlind or eyeless polymorph; - vary the message in that situation */ - && (HBlinded & ~TIMEOUT) != 0L) ? "slightly " : ""); - else { - nothing_special(obj); - return ECMD_TIME; - } - if (healamt > 0) { - if (Upolyd) - u.mh += healamt; - else - u.uhp += healamt; - } - if (Sick) - make_sick(0L, (char *) 0, FALSE, SICK_ALL); - if (Slimed) - make_slimed(0L, (char *) 0); - if (BlindedTimeout > creamed) - make_blinded(creamed, FALSE); - disp.botl = TRUE; - 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) { - u.uen += epboost; - disp.botl = TRUE; - You_feel("re-energized."); - } else { - nothing_special(obj); - return ECMD_TIME; - } - break; - } - case UNTRAP: { - if (!untrap(TRUE, 0, 0, (struct obj *) 0)) { - obj->age = 0; /* don't charge for changing their mind */ - return ECMD_OK; - } - break; - } - case CHARGE_OBJ: { - struct obj *otmp = getobj("charge", charge_ok, - GETOBJ_PROMPT | GETOBJ_ALLOWCNT); - boolean b_effect; - - if (!otmp) { - obj->age = 0; - return ECMD_CANCEL; - } - b_effect = (obj->blessed && (oart->role == Role_switch - || oart->role == NON_PM)); - recharge(otmp, b_effect ? 1 : obj->cursed ? -1 : 0); - update_inventory(); - break; - } - case LEV_TELE: - level_tele(); - break; - case CREATE_PORTAL: { - int i, num_ok_dungeons, last_ok_dungeon = 0; - d_level newlev; - winid tmpwin = create_nhwindow(NHW_MENU); - anything any; - int clr = NO_COLOR; - - any = cg.zeroany; /* set all bits to zero */ - start_menu(tmpwin, MENU_BEHAVE_STANDARD); - /* use index+1 (can't use 0) as identifier */ - for (i = num_ok_dungeons = 0; i < svn.n_dgns; i++) { - if (!svd.dungeons[i].dunlev_ureached) - continue; - if (i == tutorial_dnum) /* can't portal into tutorial */ - continue; - any.a_int = i + 1; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, - svd.dungeons[i].dname, MENU_ITEMFLAGS_NONE); - 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); - nothing_special(obj); - return ECMD_TIME; - } - 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 (svd.dungeons[i].depth_start >= depth(&u.uz)) - newlev.dlevel = svd.dungeons[i].entry_lev; - else - newlev.dlevel = svd.dungeons[i].dunlev_ureached; - - if (u.uhave.amulet || In_endgame(&u.uz) || In_endgame(&newlev) - || newlev.dnum == u.uz.dnum || !next_to_u()) { - 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 TAMING: res = invoke_taming(obj); break; + case HEALING: res = invoke_healing(obj); break; + case ENERGY_BOOST: res = invoke_energy_boost(obj); break; + case UNTRAP: res = invoke_untrap(obj); break; + case CHARGE_OBJ: res = invoke_charge_obj(obj); break; + case LEV_TELE: level_tele(); res = ECMD_TIME; break; + case CREATE_PORTAL: res = invoke_create_portal(obj); break; case ENLIGHTENING: enlightenment(MAGICENLIGHTENMENT, ENL_GAMEINPROGRESS); + res = ECMD_TIME; break; - case CREATE_AMMO: { - struct obj *otmp = mksobj(ARROW, TRUE, FALSE); - - if (!otmp) { - nothing_special(obj); - return ECMD_TIME; - } - otmp->blessed = obj->blessed; - otmp->cursed = obj->cursed; - otmp->bknown = obj->bknown; - otmp->oeroded = otmp->oeroded2 = 0; - 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"), (char *) 0); - nhUse(otmp); - break; - } - case BANISH: { - int nvanished = 0, nstayed = 0; - struct monst *mtmp, *mtmp2; - d_level dest; - - find_hell(&dest); - - for (mtmp = fmon; mtmp; mtmp = mtmp2) { - int chance = 1; - - mtmp2 = mtmp->nmon; - if (DEADMONSTER(mtmp) || !isok(mtmp->mx, mtmp->my)) - continue; - if (!is_demon(mtmp->data) && mtmp->data->mlet != S_IMP) - continue; - if (!couldsee(mtmp->mx, mtmp->my)) - continue; - if (mtmp->data->msound == MS_NEMESIS) - continue; - - if (In_quest(&u.uz) && !svq.quest_status.killed_nemesis) - chance += 10; - if (is_dprince(mtmp->data)) - chance += 2; - if (is_dlord(mtmp->data)) - chance++; - - mtmp->msleeping = mtmp->mtame = mtmp->mpeaceful = 0; - if (chance <= 1 || !rn2(chance)) { - if (!Inhell) { - nvanished++; - /* banish to a random level in Gehennom */ - dest.dlevel = rn2(dunlevs_in_dungeon(&dest)); - migrate_mon(mtmp, ledger_no(&dest), MIGR_RANDOM); - } else { - u_teleport_mon(mtmp, FALSE); - } - } else { - nstayed++; - } - } - - if (nvanished) { - char subject[] = "demons"; - - if (nvanished == 1) - *(eos(subject) - 1) = '\0'; /* remove 's' */ - pline("%s %s %s in a cloud of brimstone!", - nstayed ? ((nvanished > nstayed) - ? "Most of the" - : "Some of the") - : "The", - subject, vtense(subject, "disappear")); - } - break; - } - case FLING_POISON: - if (getdir((char *) 0)) { - int venom = rn2(2) ? BLINDING_VENOM : ACID_VENOM; - struct obj *otmp = mksobj(venom, TRUE, FALSE); - - otmp->spe = 1; /* the poison is yours */ - throwit(otmp, 0L, FALSE, (struct obj *) 0); - } else { - /* no direction picked */ - pline("%s", Never_mind); - obj->age = svm.moves; - } - break; + case CREATE_AMMO: res = invoke_create_ammo(obj); break; + case BANISH: res = invoke_banish(obj); break; + case FLING_POISON: res = invoke_fling_poison(obj); break; case SNOWSTORM: - case FIRESTORM: - { - int storm = oart->inv_prop == SNOWSTORM ? SPE_CONE_OF_COLD : SPE_FIREBALL; - int skill = spell_skilltype(storm); - int expertise = P_SKILL(skill); - - P_SKILL(skill) = P_EXPERT; - (void) spelleffects(storm, FALSE, TRUE); - P_SKILL(skill) = expertise; - } - break; - case BLINDING_RAY: - if (getdir((char *) 0)) { - if (u.dx || u.dy) { - do_blinding_ray(obj); - } else if (u.dz) { - /* up or down => light this map spot; litroom() uses - radius 0 for Sunsword, except on Rogue level where - whole room gets lit and corridor spots remain unlit */ - litroom(TRUE, obj); - pline("%s", ((!Blind && levl[u.ux][u.uy].lit - && !levl[u.ux][u.uy].waslit) - ? "It is lit here now." - : nothing_seems_to_happen)); - } else { /* zapyourself() */ - boolean vulnerable = (u.umonnum == PM_GREMLIN); - int damg = obj->blessed ? 15 : !obj->cursed ? 10 : 5; - - if (vulnerable) /* could be fatal if Unchanging */ - (void) lightdamage(obj, TRUE, 2 * damg); - - if (!flashburn((long) (damg + rnd(damg)), FALSE) - && !vulnerable) - pline("%s", nothing_seems_to_happen); - } - } else { - /* no direction picked */ - pline("%s", Never_mind); - obj->age = svm.moves; - } - break; + /*FALLTHRU*/ + case FIRESTORM: res = invoke_storm_spell(obj); break; + case BLINDING_RAY: res = invoke_blinding_ray(obj); break; default: impossible("Unknown invoke power %d.", oart->inv_prop); break; } + return res; } else { long eprop = (u.uprops[oart->inv_prop].extrinsic ^= W_ARTI), iprop = u.uprops[oart->inv_prop].intrinsic;