static int arti_invoke(struct obj *obj) { const struct artifact *oart = get_artifact(obj); if (!oart || !oart->inv_prop) { if (obj->oclass == WAND_CLASS) return do_break_wand(obj); else if (obj->oclass == GEM_CLASS || obj->oclass == TOOL_CLASS) return dorub(obj); else 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 > moves) { /* the artifact is tired :-) */ pline("You feel that %s %s ignoring you.", the(xname(obj)), otense(obj, "are")); /* and just got more so; patience is essential... */ obj->age += (long) dice(3,10); return 1; } obj->age = moves + rnz(100); switch(oart->inv_prop) { case TAMING: { struct obj pseudo; boolean unused_known; pseudo = zeroobj; /* neither cursed nor blessed */ pseudo.otyp = SCR_TAMING; seffects(&pseudo, &unused_known); 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) pline("You feel better."); else goto nothing_special; if (healamt > 0) { if (Upolyd) u.mh += healamt; else u.uhp += healamt; } if (Sick) make_sick(0L,NULL,FALSE,SICK_ALL); if (Slimed) Slimed = 0L; if (Blinded > creamed) make_blinded(creamed, FALSE); iflags.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) { pline("You feel re-energized."); u.uen += epboost; iflags.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); update_inventory(); 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 */ struct nh_menuitem *items; items = malloc(n_dgns * sizeof(struct nh_menuitem)); num_ok_dungeons = 0; for (i = 0; i < n_dgns; i++) { if (!dungeons[i].dunlev_ureached) continue; items[num_ok_dungeons].id = i+1; items[num_ok_dungeons].accel = 0; items[num_ok_dungeons].role = MI_NORMAL; items[num_ok_dungeons].selected = FALSE; strcpy(items[num_ok_dungeons].caption, dungeons[i].dname); num_ok_dungeons++; last_ok_dungeon = i; } if (num_ok_dungeons > 1) { /* more than one entry; display menu for choices */ int n; int selected[1]; n = display_menu(items, num_ok_dungeons, "Open a portal to which dungeon?", PICK_ONE, selected); free(items); if (n <= 0) goto nothing_special; i = selected[0] - 1; } else { free(items); i = last_ok_dungeon; /* also first & only OK dungeon */ } /* * 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) { pline("You feel very disoriented for a moment."); } else { if (!Blind) pline("You are surrounded by a shimmering sphere!"); else pline("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(level, 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); hold_another_object(otmp, "Suddenly %s out.", aobjnam(otmp, "fall"), NULL); break; } } } else { long eprop = (u.uprops[oart->inv_prop].extrinsic ^= W_ARTI), iprop = u.uprops[oart->inv_prop].intrinsic; boolean on = (eprop & W_ARTI) != 0; /* true if invoked prop just set */ if (on && obj->age > moves) { /* the artifact is tired :-) */ u.uprops[oart->inv_prop].extrinsic ^= W_ARTI; pline("You feel that %s %s ignoring you.", the(xname(obj)), otense(obj, "are")); /* can't just keep repeatedly trying */ obj->age += (long) dice(3,10); return 1; } else if (!on) { /* when turning off property, determine downtime */ /* arbitrary for now until we can tune this -dlc */ obj->age = moves + rnz(100); } if ((eprop & ~W_ARTI) || iprop) { nothing_special: /* you had the property from some other source too */ if (carried(obj)) pline("You feel a surge of power, but nothing seems to happen."); return 1; } switch(oart->inv_prop) { case CONFLICT: if (on) pline("You feel like a rabble-rouser."); else pline("You feel the tension decrease around you."); break; case LEVITATION: if (on) { float_up(); spoteffects(FALSE); } else float_down(I_SPECIAL|TIMEOUT, W_ARTI); break; case INVIS: if (BInvis || Blind) goto nothing_special; newsym(u.ux, u.uy); if (on) pline("Your body takes on a %s transparency...", Hallucination ? "normal" : "strange"); else pline("Your body seems to unfade..."); break; } } return 1; }
int spelleffects(int spell, boolean atme, const struct nh_cmd_arg *arg) { int energy, damage, chance, n, intell; int skill, role_skill; boolean confused = (Confusion != 0); struct obj *pseudo; boolean dummy; coord cc; schar dx = 0, dy = 0, dz = 0; if (!SPELL_IS_FROM_SPELLBOOK(spell)) { /* At the moment, we implement this via calling the code for the shortcut command. Eventually, it would make sense to invert this (and make the shortcut commands wrappers for spelleffects). */ switch (spellid(spell)) { case SPID_PRAY: return dopray(arg); case SPID_TURN: return doturn(arg); case SPID_RLOC: return dotele(arg); case SPID_JUMP: return dojump(arg); case SPID_MONS: return domonability(arg); default: impossible("Unknown spell number %d?", spellid(spell)); return 0; } } /* * Find the skill the hero has in a spell type category. * See spell_skilltype for categories. */ skill = spell_skilltype(spellid(spell)); role_skill = P_SKILL(skill); /* Get the direction or target, if applicable. We want to do this *before* determining spell success, both for interface consistency and to cut down on needless mksobj calls. */ switch (spellid(spell)) { /* These spells ask the user to target a specific space. */ case SPE_CONE_OF_COLD: case SPE_FIREBALL: /* If Skilled or better, get a specific space. */ if (role_skill >= P_SKILLED) { if (throwspell(&dx, &dy, arg)) { dz = 0; break; } else { /* Decided not to target anything. Abort the spell. */ pline("Spell canceled."); return 0; } } /* If not Skilled, fall through. */ /* These spells ask the user to target a direction. */ case SPE_FORCE_BOLT: case SPE_SLEEP: case SPE_MAGIC_MISSILE: case SPE_KNOCK: case SPE_SLOW_MONSTER: case SPE_WIZARD_LOCK: case SPE_DIG: case SPE_TURN_UNDEAD: case SPE_POLYMORPH: case SPE_TELEPORT_AWAY: case SPE_CANCELLATION: case SPE_FINGER_OF_DEATH: case SPE_HEALING: case SPE_EXTRA_HEALING: case SPE_DRAIN_LIFE: case SPE_STONE_TO_FLESH: if (atme) dx = dy = dz = 0; else if (!getargdir(arg, NULL, &dx, &dy, &dz)) { /* getdir cancelled, abort */ pline("Spell canceled."); return 0; } break; case SPE_JUMPING: if(!get_jump_coords(arg, &cc, max(role_skill, 1))) { /* No jumping after all, I guess. */ pline("Spell canceled."); return 0; } break; /* The rest of the spells don't have targeting. */ default: break; } /* Spell casting no longer affects knowledge of the spell. A decrement of spell knowledge is done every turn. */ if (spellknow(spell) <= 0) { pline("Your knowledge of this spell is twisted."); pline("It invokes nightmarish images in your mind..."); spell_backfire(spell); return 0; } else if (spellknow(spell) <= 200) { /* 1% */ pline("You strain to recall the spell."); } else if (spellknow(spell) <= 1000) { /* 5% */ pline("Your knowledge of this spell is growing faint."); } energy = (spellev(spell) * 5); /* 5 <= energy <= 35 */ if (u.uhunger <= 10 && spellid(spell) != SPE_DETECT_FOOD) { pline("You are too hungry to cast that spell."); return 0; } else if (ACURR(A_STR) < 4) { pline("You lack the strength to cast spells."); return 0; } else if (check_capacity ("Your concentration falters while carrying so much stuff.")) { return 1; } else if (!freehand()) { pline("Your arms are not free to cast!"); return 0; } if (Uhave_amulet) { pline("You feel the amulet draining your energy away."); energy += rnd(2 * energy); } if (energy > u.uen) { pline("You don't have enough energy to cast that spell."); return 0; } else { if (spellid(spell) != SPE_DETECT_FOOD) { int hungr = energy * 2; /* * If hero is a wizard, their current intelligence * (bonuses + temporary + current) * affects hunger reduction in casting a spell. * 1. int = 17-18 no reduction * 2. int = 16 1/4 hungr * 3. int = 15 1/2 hungr * 4. int = 1-14 normal reduction * The reason for this is: * a) Intelligence affects the amount of exertion * in thinking. * b) Wizards have spent their life at magic and * understand quite well how to cast spells. */ intell = acurr(A_INT); if (!Role_if(PM_WIZARD)) intell = 10; if (intell >= 17) hungr = 0; else if (intell == 16) hungr /= 4; else if (intell == 15) hungr /= 2; /* don't put player (quite) into fainting from casting a spell, particularly since they might not even be hungry at the beginning; however, this is low enough that they must eat before casting anything else except detect food */ if (hungr > u.uhunger - 3) hungr = u.uhunger - 3; morehungry(hungr); } } chance = percent_success(spell); if (confused || (rnd(100) > chance)) { pline("You fail to cast the spell correctly."); u.uen -= energy / 2; return 1; } u.uen -= energy; /* pseudo is a temporary "false" object containing the spell stats */ pseudo = mktemp_sobj(level, spellid(spell)); pseudo->blessed = pseudo->cursed = 0; pseudo->quan = 20L; /* do not let useup get it */ switch (pseudo->otyp) { /* * At first spells act as expected. As the hero increases in skill * with the appropriate spell type, some spells increase in their * effects, e.g. more damage, further distance, and so on, without * additional cost to the spellcaster. */ case SPE_CONE_OF_COLD: case SPE_FIREBALL: if (role_skill >= P_SKILLED) { cc.x = dx; cc.y = dy; n = rnd(8) + 1; while (n--) { if (!dx && !dy && !dz) { if ((damage = zapyourself(pseudo, TRUE)) != 0) losehp(damage, msgprintf( "zapped %sself with an exploding spell", uhim())); } else { explode(dx, dy, pseudo->otyp - SPE_MAGIC_MISSILE + 10, u.ulevel / 2 + 1 + spell_damage_bonus(), 0, (pseudo->otyp == SPE_CONE_OF_COLD) ? EXPL_FROSTY : EXPL_FIERY, NULL, 0); } dx = cc.x + rnd(3) - 2; dy = cc.y + rnd(3) - 2; if (!isok(dx, dy) || !cansee(dx, dy) || IS_STWALL(level->locations[dx][dy].typ) || Engulfed) { /* Spell is reflected back to center */ dx = cc.x; dy = cc.y; } } break; } /* else fall through... */ /* these spells are all duplicates of wand effects */ case SPE_FORCE_BOLT: case SPE_SLEEP: case SPE_MAGIC_MISSILE: case SPE_KNOCK: case SPE_SLOW_MONSTER: case SPE_WIZARD_LOCK: case SPE_DIG: case SPE_TURN_UNDEAD: case SPE_POLYMORPH: case SPE_TELEPORT_AWAY: case SPE_CANCELLATION: case SPE_FINGER_OF_DEATH: case SPE_LIGHT: case SPE_DETECT_UNSEEN: case SPE_HEALING: case SPE_EXTRA_HEALING: case SPE_DRAIN_LIFE: case SPE_STONE_TO_FLESH: if (objects[pseudo->otyp].oc_dir != NODIR) { if (!dx && !dy && !dz) { if ((damage = zapyourself(pseudo, TRUE)) != 0) { losehp(damage, msgprintf("zapped %sself with a spell", uhim())); } } else weffects(pseudo, dx, dy, dz); } else weffects(pseudo, 0, 0, 0); update_inventory(); /* spell may modify inventory */ break; /* these are all duplicates of scroll effects */ case SPE_REMOVE_CURSE: case SPE_CONFUSE_MONSTER: case SPE_DETECT_FOOD: case SPE_CAUSE_FEAR: /* high skill yields effect equivalent to blessed scroll */ if (role_skill >= P_SKILLED) pseudo->blessed = 1; /* fall through */ case SPE_CHARM_MONSTER: case SPE_MAGIC_MAPPING: case SPE_CREATE_MONSTER: case SPE_IDENTIFY: seffects(pseudo, &dummy); break; /* these are all duplicates of potion effects */ case SPE_HASTE_SELF: case SPE_DETECT_TREASURE: case SPE_DETECT_MONSTERS: case SPE_LEVITATION: case SPE_RESTORE_ABILITY: /* high skill yields effect equivalent to blessed potion */ if (role_skill >= P_SKILLED) pseudo->blessed = 1; /* fall through */ case SPE_INVISIBILITY: peffects(pseudo); break; case SPE_CURE_BLINDNESS: healup(0, 0, FALSE, TRUE); break; case SPE_CURE_SICKNESS: if (Sick) pline("You are no longer ill."); if (Slimed) { pline("The slime disappears!"); Slimed = 0; } healup(0, 0, TRUE, FALSE); break; case SPE_CREATE_FAMILIAR: make_familiar(NULL, u.ux, u.uy, FALSE); break; case SPE_CLAIRVOYANCE: if (!BClairvoyant) do_vicinity_map(); /* at present, only one thing blocks clairvoyance */ else if (uarmh && uarmh->otyp == CORNUTHAUM) pline("You sense a pointy hat on top of your %s.", body_part(HEAD)); break; case SPE_PROTECTION: cast_protection(); break; case SPE_JUMPING: jump_to_coords(&cc); break; default: impossible("Unknown spell %d attempted.", spell); obfree(pseudo, NULL); return 0; } /* gain skill for successful cast */ use_skill(skill, spellev(spell)); obfree(pseudo, NULL); /* now, get rid of it */ return 1; }
int doread() { register struct obj *scroll; register boolean confused = (Confusion != 0); known = FALSE; scroll = getobj(readable, "read"); /* "#-" added by GAN 10/22/86 */ if(!scroll) return(0); /* below added to allow reading of fortune cookies */ if(scroll->otyp == FORTUNE_COOKIE) { if(flags.verbose) You("break up the cookie and throw away the pieces."); outrumor(bcsign(scroll), TRUE); useup(scroll); return(1); } else if(scroll->olet != SCROLL_SYM #ifdef SPELLS && scroll->olet != SPBOOK_SYM #endif ) { pline("That is a silly thing to read."); return(0); } if(Blind) { #ifdef SPELLS if (scroll->olet == SPBOOK_SYM) { pline("Being blind, you cannot read the mystic runes."); return(0); } else #endif if (!scroll->dknown) { pline("Being blind, you cannot read the formula on the scroll."); return(0); } } #ifndef NO_SIGNAL scroll->in_use = TRUE; /* now being read */ #endif #ifdef SPELLS if(scroll->olet == SPBOOK_SYM) { if(confused) { You("cannot grasp the meaning of this tome."); useup(scroll); return(0); } else return(study_book(scroll)); } #endif if(scroll->otyp != SCR_BLANK_PAPER) { if(Blind) pline("As you pronounce the formula on it, the scroll disappears."); else pline("As you read the scroll, it disappears."); if(confused) { if (Hallucination) pline("Being so trippy, you screw up..."); else pline("Being confused, you mispronounce the magic words..."); } } if(!seffects(scroll)) { if(!objects[scroll->otyp].oc_name_known) { if(known && !confused) { makeknown(scroll->otyp); more_experienced(0,10); } else if(!objects[scroll->otyp].oc_uname) docall(scroll); } if(!(scroll->otyp == SCR_BLANK_PAPER) || confused) useup(scroll); #ifndef NO_SIGNAL else scroll->in_use = FALSE; #endif } return(1); }