static boolean artifact_hit_drainlife(struct monst *magr, struct monst *mdef, struct obj *otmp, int *dmgptr) { boolean youattack = (magr == &youmonst); boolean youdefend = (mdef == &youmonst); boolean vis = (!youattack && magr && cansee(magr->mx, magr->my)) || (!youdefend && cansee(mdef->mx, mdef->my)) || (youattack && u.uswallow && mdef == u.ustuck && !Blind); 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) pline("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 && magr->mhp < magr->mhpmax) { magr->mhp += (oldhpmax - u.uhpmax)/2; if (magr->mhp > magr->mhpmax) magr->mhp = magr->mhpmax; } return TRUE; } return FALSE; }
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; }