boolean can_reach_floor(void) { return (boolean)(!u.uswallow && /* Restricted/unskilled riders can't reach the floor */ !(u.usteed && P_SKILL(P_RIDING) < P_BASIC) && (!Levitation || Is_airlevel(&u.uz) || Is_waterlevel(&u.uz))); }
static void cast_protection(void) { int loglev = 0; int l = u.ulevel; int natac = get_player_ac() + u.uspellprot; int gain; /* loglev=log2(u.ulevel)+1 (1..5) */ while (l) { loglev++; l /= 2; } /* * The more u.uspellprot you already have, the less you get, * and the better your natural ac, the less you get. * * LEVEL AC SPELLPROT from sucessive SPE_PROTECTION casts * 1 10 0, 1, 2, 3, 4 * 1 0 0, 1, 2, 3 * 1 -10 0, 1, 2 * 2-3 10 0, 2, 4, 5, 6, 7, 8 * 2-3 0 0, 2, 4, 5, 6 * 2-3 -10 0, 2, 3, 4 * 4-7 10 0, 3, 6, 8, 9, 10, 11, 12 * 4-7 0 0, 3, 5, 7, 8, 9 * 4-7 -10 0, 3, 5, 6 * 7-15 -10 0, 3, 5, 6 * 8-15 10 0, 4, 7, 10, 12, 13, 14, 15, 16 * 8-15 0 0, 4, 7, 9, 10, 11, 12 * 8-15 -10 0, 4, 6, 7, 8 * 16-30 10 0, 5, 9, 12, 14, 16, 17, 18, 19, 20 * 16-30 0 0, 5, 9, 11, 13, 14, 15 * 16-30 -10 0, 5, 8, 9, 10 */ gain = loglev - (int)u.uspellprot / (4 - min(3, (10 - natac) / 10)); if (gain > 0) { if (!Blind) { const char *hgolden = hcolor("golden"); if (u.uspellprot) pline("The %s haze around you becomes more dense.", hgolden); else pline("The %s around you begins to shimmer with %s haze.", (Underwater || Is_waterlevel(&u.uz)) ? "water" : Engulfed ? mbodypart(u.ustuck, STOMACH) : IS_STWALL(level->locations[u.ux][u.uy].typ) ? "stone" : "air", an(hgolden)); } u.uspellprot += gain; u.uspmtime = P_SKILL(spell_skilltype(SPE_PROTECTION)) == P_EXPERT ? 20 : 10; if (!u.usptime) u.usptime = u.uspmtime; } else { pline("Your %s feels warm for a moment.", body_part(SKIN)); } }
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 use_saddle(struct obj *otmp, const struct nh_cmd_arg *arg) { struct monst *mtmp; const struct permonst *ptr; int chance; const char *s; schar dx, dy, dz; /* Can you use it? */ if (nohands(youmonst.data)) { pline("You have no hands!"); /* not `body_part(HAND)' */ return 0; } else if (!freehand()) { pline("You have no free %s.", body_part(HAND)); return 0; } /* Select an animal */ if (Engulfed || Underwater || !getargdir(arg, NULL, &dx, &dy, &dz)) { pline("Never mind."); return 0; } if (!dx && !dy) { pline("Saddle yourself? Very funny..."); return 0; } if (!isok(u.ux + dx, u.uy + dy) || !((mtmp = m_at(level, u.ux + dx, u.uy + dy))) || !canspotmon(mtmp)) { if (knownwormtail(u.ux + dx, u.uy + dy)) pline("It's hard to strap a saddle to a tail."); else pline("I see nobody there."); return 0; } /* Is this a valid monster? */ if (mtmp->misc_worn_check & W_MASK(os_saddle) || which_armor(mtmp, os_saddle)) { pline("%s doesn't need another one.", Monnam(mtmp)); return 0; } ptr = mtmp->data; if (!uarmg && touched_monster(ptr - mons)) { pline("You touch %s.", mon_nam(mtmp)); instapetrify(killer_msg(STONING, msgcat("attempting to saddle ", an(mtmp->data->mname)))); } if (ptr == &mons[PM_INCUBUS] || ptr == &mons[PM_SUCCUBUS]) { pline("Shame on you!"); exercise(A_WIS, FALSE); return 1; } if (mtmp->isminion || mtmp->isshk || mtmp->ispriest || mtmp->isgd || mtmp->iswiz) { pline("I think %s would mind.", mon_nam(mtmp)); return 0; } if (!can_saddle(mtmp)) { pline("You can't saddle such a creature."); return 0; } /* Calculate your chance */ chance = ACURR(A_DEX) + ACURR(A_CHA) / 2 + 2 * mtmp->mtame; chance += u.ulevel * (mtmp->mtame ? 20 : 5); if (!mtmp->mtame) chance -= 10 * mtmp->m_lev; if (Role_if(PM_KNIGHT)) chance += 20; switch (P_SKILL(P_RIDING)) { case P_ISRESTRICTED: case P_UNSKILLED: default: chance -= 20; break; case P_BASIC: break; case P_SKILLED: chance += 15; break; case P_EXPERT: chance += 30; break; } if (Confusion || Fumbling || Glib) chance -= 20; else if (uarmg && (s = OBJ_DESCR(objects[uarmg->otyp])) != NULL && !strncmp(s, "riding ", 7)) /* Bonus for wearing "riding" (but not fumbling) gloves */ chance += 10; else if (uarmf && (s = OBJ_DESCR(objects[uarmf->otyp])) != NULL && !strncmp(s, "riding ", 7)) /* ... or for "riding boots" */ chance += 10; if (otmp->cursed) chance -= 50; /* steed becomes alert if possible */ maybewakesteed(mtmp); /* Make the attempt */ if (rn2(100) < chance) { pline("You put the saddle on %s.", mon_nam(mtmp)); if (otmp->owornmask) remove_worn_item(otmp, FALSE); freeinv(otmp); /* mpickobj may free otmp it if merges, but we have already checked for a saddle above, so no merger should happen */ mpickobj(mtmp, otmp); mtmp->misc_worn_check |= W_MASK(os_saddle); otmp->owornmask = W_MASK(os_saddle); otmp->leashmon = mtmp->m_id; update_mon_intrinsics(mtmp, otmp, TRUE, FALSE); } else pline("%s resists!", Monnam(mtmp)); return 1; }