Mercurial > hg > early-roguelike
diff urogue/sticks.c @ 256:c495a4f288c6
Import UltraRogue from the Roguelike Restoration Project (r1490)
author | John "Elwin" Edwards |
---|---|
date | Tue, 31 Jan 2017 19:56:04 -0500 |
parents | |
children | e52a8a7ad4c5 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/sticks.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1796 @@ +/* + sticks.c - Functions to implement the various sticks one might find + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include <limits.h> +#include <string.h> +#include <ctype.h> +#include "rogue.h" + +/* for WS_HIT, WS_WEB, etc */ + +static struct object null_stick = +{ + {0,0},NULL,NULL,"",0,"0d0",0,0,'X',0,0,0,0,0,0,0,0,0,{0} +}; + +/* + * Mask for cancelling special abilities The flags listed here will be the + * ones left on after the cancellation takes place + */ + +#define CANC0MASK ( ISBLIND | ISINWALL | ISRUN | \ + ISFLEE | ISMEAN | ISGREED | \ + CANSHOOT | ISHELD | ISHUH | \ + ISSLOW | ISHASTE | ISCLEAR | \ + ISUNIQUE) + +#define CANC1MASK ( HASDISEASE | DIDSUFFOCATE | CARRYGOLD | \ + HASITCH | CANSELL | CANBBURN | \ + CANSPEAK | CANFLY | ISFRIENDLY) + +#define CANC2MASK ( HASINFEST | NOMOVE | ISSCAVENGE | \ + DOROT | HASSTINK | DIDHOLD) + +#define CANC3MASK ( ISUNDEAD | CANBREATHE | CANCAST | \ + HASOXYGEN) + +#define CANC4MASK ( CANTRAMPLE | CANSWIM | CANWIELD | \ + ISFAST | CANBARGAIN | CANSPORE | \ + ISLARGE | ISSMALL | ISFLOCK | \ + ISSWARM | CANSTICK | CANTANGLE | \ + SHOOTNEEDLE | CANZAP | HASARMOR | \ + CANTELEPORT | ISBERSERK | ISFAMILIAR | \ + HASFAMILIAR | SUMMONING) + +#define CANC5MASK ( CANREFLECT | MAGICATTRACT | HASSHIELD | HASMSHIELD) + +#define CANC6MASK ( 0 ) +#define CANC7MASK ( 0 ) +#define CANC8MASK ( 0 ) +#define CANC9MASK ( 0 ) +#define CANCAMASK ( 0 ) +#define CANCBMASK ( 0 ) +#define CANCCMASK ( 0 ) +#define CANCDMASK ( 0 ) +#define CANCEMASK ( 0 ) +#define CANCFMASK ( 0 ) + +void +fix_stick(struct object *cur) +{ + if (strcmp(ws_type[cur->o_which], "staff") == 0) + { + cur->o_weight = 100; + cur->o_charges = 5 + rnd(10); + cur->o_damage = "2d3"; + + switch (cur->o_which) + { + case WS_HIT: + cur->o_hplus = 3; + cur->o_dplus = 3; + cur->o_damage = "2d8"; + break; + + case WS_LIGHT: + cur->o_charges = 20 + rnd(10); + break; + } + } + else /* A wand */ + { + cur->o_damage = "1d3"; + cur->o_weight = 60; + cur->o_charges = 3 + rnd(5); + + switch (cur->o_which) + { + case WS_HIT: + cur->o_hplus = 3; + cur->o_dplus = 3; + cur->o_damage = "1d8"; + break; + + case WS_LIGHT: + cur->o_charges = 10 + rnd(10); + break; + } + } + + cur->o_hurldmg = "1d1"; +} + +/* + do_zap() + zap a stick (or effect a stick-like spell) + zapper: who does it + got_dir: need to ask for direction? + which: which WS_STICK (-1 means ask from pack) + flags: ISBLESSED, ISCURSED +*/ + +void +do_zap(struct thing *zapper, int which, unsigned long flags) +{ + struct linked_list *item = NULL, *nitem; + struct object *obj, *nobj; + struct room *rp; + struct thing *tp = NULL; + char *mname = NULL; + int y, x; + int blessed = flags & ISBLESSED; + int cursed = flags & ISCURSED; + int is_stick = (which < 0 ? TRUE : FALSE); + int got_one = TRUE; + + if (zapper != &player) + { + monster_do_zap(zapper, which, flags); + return; + } + + if (is_stick) + { + if ((obj = get_object(pack, "zap with", 0, bff_zappable)) == NULL) + return; + + if (obj->o_type != STICK && !(obj->o_flags & ISZAPPED)) + { + msg("You can't zap with that!"); + return; + } + + if (obj->o_type != STICK) /* an electrified weapon */ + which = WS_ELECT; + else + which = obj->o_which; + + if (obj->o_charges < 1) + { + nothing_message(flags); + return; + } + + obj->o_charges--; + + if (delta.y == 0 && delta.x == 0) + do + { + delta.y = rnd(3) - 1; + delta.x = rnd(3) - 1; + } + while (delta.y == 0 && delta.x == 0); + + flags = obj->o_flags; + cursed = obj->o_flags & ISCURSED; + blessed = obj->o_flags & ISBLESSED; + } + else + obj = &null_stick; + + /* Find out who the target is */ + + y = hero.y; + x = hero.x; + + while(shoot_ok(CCHAR(winat(y, x)))) + { + y += delta.y; + x += delta.x; + } + + if (x >= 0 && x < COLS && y >= 1 && y < LINES - 2 && + isalpha(mvwinch(mw, y, x))) + { + item = find_mons(y, x); + tp = THINGPTR(item); + mname = on(player, ISBLIND) ? "monster" : + monsters[tp->t_index].m_name; + + if (on(*tp, CANSELL)) + { + luck++; + aggravate(); + } + } + else + got_one = FALSE; + + debug("Zapping with %d", which); + + switch(which) + { + case WS_LIGHT: + /* Reddy Kilowat wand. Light up the room */ + if (blue_light(flags) && is_stick) + if (is_stick) + know_items[TYP_STICK][WS_LIGHT] = TRUE; + break; + + case WS_DRAIN: + + /* + * Take away 1/2 of hero's hit points, then take it away + * evenly from the monsters in the room or next to hero if he + * is in a passage. Leave the monsters alone if the stick is + * cursed. Drain 1/3rd of hero's hit points if blessed. + */ + + if (pstats.s_hpt < 2) + { + death(D_DRAINLIFE); + return; + } + + if (cursed) + pstats.s_hpt /= 2; + else if ((rp = roomin(hero)) == NULL) + drain(hero.y - 1, hero.y + 1, hero.x - 1, hero.x + 1); + else + drain(rp->r_pos.y, rp->r_pos.y + rp->r_max.y, + rp->r_pos.x, rp->r_pos.x + rp->r_max.x); + + if (blessed) + pstats.s_hpt = (int) (pstats.s_hpt * 2.0 / 3.0); + + break; + + case WS_POLYMORPH: + case WS_MONSTELEP: + case WS_CANCEL: + { + char oldch; + int rm; + int save_adj = 0; + + if (got_one) + { + /* if the monster gets the saving throw, leave the case */ + + if (blessed) + save_adj = -5; + + if (cursed) + if (which == WS_POLYMORPH) + save_adj = -5; /* not save vs becoming tougher */ + else + save_adj = 5; + + if (save_throw(VS_MAGIC - save_adj, tp)) + { + nothing_message(flags); + break; + } + else if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + /* Unhold player */ + + if (on(*tp, DIDHOLD)) + { + turn_off(*tp, DIDHOLD); + + if (--hold_count == 0) + turn_off(player, ISHELD); + } + + /* unsuffocate player */ + + if (on(*tp, DIDSUFFOCATE)) + { + turn_off(*tp, DIDSUFFOCATE); + extinguish_fuse(FUSE_SUFFOCATE); + } + + if (which == WS_POLYMORPH) + { + int which_new; + int charmed; + + detach(mlist, item); + charmed = on(*tp, ISCHARMED); + oldch = tp->t_oldch; + delta.y = y; + delta.x = x; + + if (!blessed && !cursed) + which_new = randmonster(WANDER, GRAB); + else + { + /* duplicate randmonster() for now */ + /* Eventually fix to take level */ + + int cur_level = 0, range, i; + + if (blessed) + cur_level = level / 2; + + if (cursed) + cur_level = level * 2; + + range = 4 * NLEVMONS; + i = 0; + + do + { + if (i++ > range * 10) /* just in case all have */ + { /* been genocided */ + i = 0; + + if (--cur_level <= 0) + fatal("Rogue could not find a monster to make"); + } + + which_new = NLEVMONS * (cur_level - 1) + (rnd(range) - (range - 1 - NLEVMONS)); + + if (which_new < 1) + which_new = rnd(NLEVMONS) + 1; + + if (which_new > nummonst - NUMSUMMON - 1) + { + if (blessed) + which_new = rnd(range) + (nummonst - NUMSUMMON - 1) - (range - 1); + else if (which_new > nummonst - 1) + which_new = rnd(range + NUMSUMMON) + (nummonst - 1) - (range + NUMSUMMON - 1); + } + } + while (!monsters[which_new].m_normal); + } + + new_monster(item, which_new, &delta, NOMAXSTATS); + mname = on(player, ISBLIND) ? "monster" : monsters[tp->t_index].m_name; + + if (!cursed && charmed) + turn_on(*tp, ISCHARMED); + + if (off(*tp, ISRUN)) + chase_it(&delta, &player); + + if (isalpha(mvwinch(cw, y, x))) + mvwaddch(cw, y, x, tp->t_type); + + tp->t_oldch = oldch; + seemsg("You have created a new %s!", mname); + } + else if (which == WS_CANCEL) + { + tp->t_flags[0] &= CANC0MASK; + tp->t_flags[1] &= CANC1MASK; + tp->t_flags[2] &= CANC2MASK; + tp->t_flags[3] &= CANC3MASK; + tp->t_flags[4] &= CANC4MASK; + tp->t_flags[5] &= CANC5MASK; + tp->t_flags[6] &= CANC5MASK; + tp->t_flags[7] &= CANC7MASK; + tp->t_flags[8] &= CANC8MASK; + tp->t_flags[9] &= CANC9MASK; + tp->t_flags[10] &= CANCAMASK; + tp->t_flags[11] &= CANCBMASK; + tp->t_flags[12] &= CANCCMASK; + tp->t_flags[13] &= CANCDMASK; + tp->t_flags[14] &= CANCEMASK; + tp->t_flags[15] &= CANCFMASK; + } + else /* A teleport stick */ + { + if (cursed) /* Teleport monster to */ + { /* player */ + if ((y == (hero.y + delta.y)) && + (x == (hero.x + delta.x))) + nothing_message(flags); + else + { + tp->t_pos.y = hero.y + delta.y; + tp->t_pos.x = hero.x + delta.x; + } + } + else if (blessed) /* Get rid of monster */ + { + killed(NULL, item, NOMESSAGE, NOPOINTS); + return; + } + else + { + int i = 0; + + do /* Move monster to */ + { /* another room */ + rm = rnd_room(); + rnd_pos(&rooms[rm], &tp->t_pos); + } + while (winat(tp->t_pos.y, tp->t_pos.x) != + FLOOR && i++ < 500); + } + + /* Now move the monster */ + + if (isalpha(mvwinch(cw, y, x))) + mvwaddch(cw, y, x, tp->t_oldch); + + chase_it(&tp->t_pos, &player); + mvwaddch(mw, y, x, ' '); + mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch); + + if (tp->t_pos.y != y || tp->t_pos.x != x) + tp->t_oldch = CCHAR(mvwinch(cw, tp->t_pos.y, tp->t_pos.x)); + } + } + } + break; + + case WS_MISSILE: + { + int damage; + int nsides = 4; + int ch; + coord pos; + + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + /* Magic Missiles *always* hit, no saving throw */ + + pos = do_motion('*', delta.y, delta.x, &player); + ch = winat(pos.y, pos.x); + + if (cursed) + nsides /= 2; + else if (blessed) + nsides *= 2; + + damage = roll(pstats.s_lvl, nsides); + + if (isalpha(ch)) + { + debug("Missiled %s for %d (%d)", + mname, damage, tp->t_stats.s_hpt - damage); + + if ((tp->t_stats.s_hpt -= damage) <= 0) + { + seemsg("The missile kills the %s.", mname); + killed(&player, item, NOMESSAGE, POINTS); + } + else + { + seemsg("The missile hits the %s", mname); + chase_it(&pos, &player); + summon_help(tp, NOFORCE); + } + } + + if (pos.y >= 0 && pos.x >= 0 && + pos.y < LINES && pos.x < COLS) + mvwaddch(cw, pos.y, pos.x, show(pos.y, pos.x)); + } + break; + + case WS_HIT: + { + coord pos; + int ch; + struct object strike; /* don't want to change sticks attributes */ + + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + pos = do_motion('@', delta.y, delta.x, &player); + ch = winat(pos.y, pos.x); + + if (isalpha(ch)) + { + static char buf[20]; + int nsides, ndice; + + strike = *obj; + + if (blessed) + strike.o_hplus = 12; + else if (cursed) + strike.o_hplus = 3; + else + strike.o_hplus = 6; + + if (!is_stick || strcmp(ws_type[which], "staff") == 0) + ndice = 3; + else + ndice = 2; + + if (blessed) + nsides = 16; + else if (cursed) + nsides = 4; + else + nsides = 8; + + sprintf(buf, "%dd%d", ndice, nsides); + + strike.o_damage = buf; + fight(&tp->t_pos, &strike, NOTHROWN); + } + } + break; + + case WS_SLOW_M: + if (got_one) + { + if (cursed) + { + if (off(*tp, ISSLOW)) + turn_on(*tp, ISHASTE); + else + turn_off(*tp, ISSLOW); + } + else if (blessed) + { + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + turn_off(*tp, ISRUN); + turn_on(*tp, ISHELD); + return; + } + else + { + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + if (off(*tp, ISHASTE)) + turn_on(*tp, ISSLOW); + else + turn_off(*tp, ISHASTE); + + tp->t_turn = TRUE; + } + + delta.y = y; + delta.x = x; + chase_it(&delta, &player); + } + break; + + case WS_CHARGE: + if (know_items[TYP_STICK][which] != TRUE && is_stick) + { + msg("This is a wand of charging."); + know_items[TYP_STICK][which] = TRUE; + } + + if ((nitem = get_item("charge", STICK)) != NULL) + { + nobj = OBJPTR(nitem); + + if ((nobj->o_charges == 0) && (nobj->o_which != WS_CHARGE)) + { + fix_stick(nobj); + + if (blessed) + nobj->o_charges *= 2; + else if (cursed) + nobj->o_charges /= 2; + } + else + { + if (blessed) + nobj->o_charges += 2; + else if (cursed) + nobj->o_charges -= 1; + else + nobj->o_charges += 1; + } + } + break; + + case WS_ELECT: + case WS_FIRE: + case WS_COLD: + { + char *name = NULL; + int damage; + int ndice = 3 + pstats.s_lvl; + int nsides; + + if (is_stick) + { + know_items[TYP_STICK][which] = TRUE; + + if (strcmp(ws_type[which], "staff") == 0) + nsides = 8; + else + nsides = 4; + } + else + nsides = 6; + + if (cursed) + nsides /= 2; + else if (blessed) + nsides *= 2; + + switch (which) + { + case WS_ELECT: + name = "lightning bolt"; + + if (rnd(2)) + ndice += rnd(obj->o_charges / 10); + else + nsides += rnd(obj->o_charges / 10); + + break; + + case WS_FIRE: + name = "flame"; + break; + + case WS_COLD: + name = "ice"; + break; + } + + damage = roll(ndice, nsides); + shoot_bolt(&player, hero, delta, POINTS, D_BOLT, name, damage); + } + break; + + case WS_ANTIMATTER: + { + int m1, m2, x1, y1; + char ch; + struct linked_list *ll; + struct thing *lt; + + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + y1 = hero.y; + x1 = hero.x; + + do + { + y1 += delta.y; + x1 += delta.x; + ch = winat(y1, x1); + } + while (ch == PASSAGE || ch == FLOOR); + + for (m1 = x1 - 1; m1 <= x1 + 1; m1++) + { + for (m2 = y1 - 1; m2 <= y1 + 1; m2++) + { + ch = winat(m2, m1); + + if (m1 == hero.x && m2 == hero.y) + continue; + + if (ch != ' ') + { + ll = find_obj(m2, m1); + + while (ll != NULL) + { + rem_obj(ll, TRUE); + ll = find_obj(m2, m1); + } + + ll = find_mons(m2, m1); + + if (ll != NULL) + { + lt = THINGPTR(ll); + if (on(*lt, CANSELL)) + { + luck++; + aggravate(); + } + + if (off(*lt, CANINWALL)) + { + check_residue(lt); + detach(mlist, ll); + discard(ll); + mvwaddch(mw, m2, m1, ' '); + } + } + + mvaddch(m2, m1, ' '); + mvwaddch(cw, m2, m1, ' '); + } + } + } + + touchwin(cw); + touchwin(mw); + } + break; + + case WS_CONFMON: + case WS_PARALYZE: + if (got_one) + { + if (save_throw(VS_MAGIC - (blessed ? 5 : (cursed ? -5 : 0)), tp)) + nothing_message(flags); + else + { + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + switch(which) + { + case WS_CONFMON: + seemsg("The %s looks bewildered.", mname); + turn_on(*tp, ISHUH); + break; + + case WS_PARALYZE: + seemsg("The %s looks stunned.", mname); + tp->t_no_move = FREEZETIME; + break; + } + } + + delta.y = y; + delta.x = x; + chase_it(&delta, &player); + } + else + nothing_message(flags); + + break; + + case WS_XENOHEALING: + { + int hpt_gain = roll(zapper->t_stats.s_lvl, (blessed ? 8 : 4)); + int mons_hpt = tp->t_stats.s_hpt; + + if (got_one) + { + if (cursed) + { + if (!save_throw(VS_MAGIC, tp)) + { + if ((mons_hpt -= hpt_gain) <= 0) { + seemsg("The %s shrivels up and dies!", mname); + killed(&player, item, NOMESSAGE, POINTS); + } + else + seemsg("Wounds appear all over the %s.", mname); + } + else + nothing_message(flags); + + delta.y = y; + delta.x = x; + chase_it(&delta, &player); + } + else + { + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + mons_hpt = min(mons_hpt + hpt_gain, tp->maxstats.s_hpt); + seemsg("The %s appears less wounded!", mname); + } + } + else + nothing_message(flags); + } + break; + + case WS_DISINTEGRATE: + if (got_one) + { + if (cursed) + { + if (on(*tp, ISUNIQUE)) + { + if (on(*tp, CANSUMMON)) + summon_help(tp, FORCE); + else + msg("The %s is very upset.", mname); + + turn_on(*tp, ISHASTE); + } + else + { + int m1, m2; + coord mp; + struct linked_list *titem; + int ch; + struct thing *th; + + for (m1 = tp->t_pos.x - 1; m1 <= tp->t_pos.x + 1; m1++) + { + for (m2 = tp->t_pos.y - 1; m2 <= tp->t_pos.y + 1; m2++) + { + ch = winat(m2, m1); + + if (shoot_ok(ch) && ch != PLAYER) + { + mp.x = m1; /* create it */ + mp.y = m2; + titem = new_item(sizeof(struct thing)); + new_monster(titem, (short) tp->t_index, &mp, NOMAXSTATS); + th = THINGPTR(titem); + turn_on(*th, ISMEAN); + chase_it(&mp, &player); + } + } + } + } + + delta.y = y; + delta.x = x; + turn_on(*tp, ISMEAN); + chase_it(&delta, &player); + } + else /* if its a UNIQUE it might still live */ + { + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + if (on(*tp, ISUNIQUE) && + save_throw(VS_MAGIC - (blessed ? -5 : 0), tp)) + { + tp->t_stats.s_hpt = tp->t_stats.s_hpt / 2 - 1; + + if (tp->t_stats.s_hpt < 1) + { + killed(&player, item, NOMESSAGE, POINTS); + seemsg("The %s fades away.", mname); + } + else + { + delta.y = y; + delta.x = x; + + chase_it(&delta, &player); + + if (on(*tp, CANSUMMON)) + summon_help(tp, FORCE); + else + seemsg("The %s is very upset.", mname); + } + } + else + { + if (on(*tp, CANSELL)) + { + luck++; + aggravate(); + } + seemsg("The %s turns to dust and blows away.", mname); + killed(&player, item, NOMESSAGE, POINTS); + } + } + } + break; + + case WS_NOTHING: + nothing_message(flags); + break; + + case WS_INVIS: + { + if (cursed) + { + int x1, y1, x2, y2; + int zapped = FALSE; + + if ((rp = roomin(hero)) == NULL) + { + x1 = max(hero.x - 1, 0); + y1 = max(hero.y - 1, 0); + x2 = min(hero.x + 1, COLS - 1); + y2 = min(hero.y + 1, LINES - 3); + } + else + { + x1 = rp->r_pos.x; + y1 = rp->r_pos.y; + x2 = rp->r_pos.x + rp->r_max.x; + y2 = rp->r_pos.y + rp->r_max.y; + } + + for (item = mlist; item != NULL; item = next(item)) + { + tp = THINGPTR(item); + + if (tp->t_pos.x >= x1 && tp->t_pos.x <= x2 && + tp->t_pos.y >= y1 && tp->t_pos.y <= y2) + { + turn_on(*tp, ISINVIS); + turn_on(*tp, ISRUN); + turn_off(*tp, ISDISGUISE); + chase_it(&tp->t_pos, &player); + zapped = TRUE; + } + } + + if (zapped) + seemsg("The monsters seem to have all disappeared."); + else + nothing_message(flags); + } + else + { + if (got_one) + { + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + if (blessed) + { + turn_off(*tp, ISINVIS); + turn_off(*tp, ISSHADOW); + seemsg("The %s appears.", mname); + } + else + { + turn_on(*tp, ISINVIS); + seemsg("The %s disappears.", mname); + } + } + else + nothing_message(flags); + } + light(&hero); + } + break; + + case WS_BLAST: + { + int ch; + struct linked_list *itm, *ip; + struct object *ob; + struct trap *trp; + + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + itm = spec_item(WEAPON, GRENADE, 0, 0); + ob = OBJPTR(itm); + ob->o_count = 1; + ob->o_flags |= ISKNOW; + hearmsg("BOOOM!"); + aggravate(); + ob->o_pos.x = hero.x; + ob->o_pos.y = hero.y; + + for (;;) + { + ob->o_pos.y += delta.y; + ob->o_pos.x += delta.x; + + if (!ce(ob->o_pos, hero) && + cansee(ob->o_pos.y, ob->o_pos.x) && + mvwinch(cw, ob->o_pos.y, ob->o_pos.x) != ' ') + { + mvwaddch(cw, ob->o_pos.y, ob->o_pos.x, + show(ob->o_pos.y, ob->o_pos.x)); + } + + if (shoot_ok(ch = winat(ob->o_pos.y, ob->o_pos.x)) + && ch != DOOR && !ce(ob->o_pos, hero)) + { + if (cansee(ob->o_pos.y, ob->o_pos.x) && + ntraps + 1 < 2 * MAXTRAPS && + mvwinch(cw, ob->o_pos.y, ob->o_pos.x) != ' ') + { + mvwaddch(cw, ob->o_pos.y, ob->o_pos.x,TRAPDOOR); + wrefresh(cw); + } + + if (isatrap(ch)) + { + trp = trap_at(ob->o_pos.y, ob->o_pos.x); + + if (trp != NULL) + { + trp->tr_type = TRAPDOOR; + trp->tr_flags |= ISFOUND; + trp->tr_show = TRAPDOOR; + } + } + else if (isalpha(ch)) + hit_monster(ob->o_pos.y, ob->o_pos.x, ob, &player); + else if ((ch == FLOOR || ch == PASSAGE) + && ntraps + 1 < 2 * MAXTRAPS) + { + mvaddch(ob->o_pos.y, ob->o_pos.x, TRAPDOOR); + traps[ntraps].tr_type = TRAPDOOR; + traps[ntraps].tr_flags = ISFOUND; + traps[ntraps].tr_show = TRAPDOOR; + traps[ntraps].tr_pos.y = ob->o_pos.y; + traps[ntraps++].tr_pos.x = ob->o_pos.x; + } + else if (ch == POTION || ch == SCROLL || ch == FOOD + || ch == WEAPON || ch == RING || ch == ARMOR + || ch == STICK || ch == ARTIFACT || ch == GOLD) + { + ip = find_obj(ob->o_pos.y, ob->o_pos.x); + + while (ip) /* BOOM destroys all stacked objects */ + { + rem_obj(ip, TRUE); + ip = find_obj(ob->o_pos.y, ob->o_pos.x); + } + + mvaddch(ob->o_pos.y, ob->o_pos.x, + (roomin(ob->o_pos) == NULL ? PASSAGE : FLOOR) ); + } + continue; + } + break; + } + discard(itm); + } + break; + + case WS_WEB: + { + int ch; + coord pos; + + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + pos = do_motion('#', delta.y, delta.x, &player); + ch = winat(pos.y, pos.x); + + if (isalpha(ch)) + { + if (save_throw(VS_MAGIC, tp)) + seemsg("The %s evades your web.", mname); + else + { + seemsg("The %s is webbed.", mname); + turn_off(*tp, ISRUN); + turn_on(*tp, ISHELD); + } + } + + if (pos.y >= 0 && pos.x >= 0 && + pos.y < LINES && pos.x < COLS) + mvwaddch(cw, pos.y, pos.x, show(pos.y, pos.x)); + } + break; + + case WS_KNOCK: + case WS_CLOSE: + if (blessed) /* Do all doors in room */ + { + char new_door = (which == WS_KNOCK ? DOOR : SECRETDOOR); + struct room *rmp = roomin(hero); + + if (rmp != NULL) + for (x = 0; x < rmp->r_nexits; x++) + { + move(rmp->r_exit[x].y, rmp->r_exit[x].x); + addch(new_door); + } + else /* Do all adjacent doors */ + for (x = hero.x - 1; x <= hero.x + 1; x++) + for (y = hero.y - 1; y <= hero.y + 1; y++) + switch(CCHAR( winat(y, x) )) + { + case DOOR: + case SECRETDOOR: + addch(new_door); + break; + } + light(&hero); + } + else if (cursed)/* do opposite spell */ + do_zap(zapper, (which == WS_KNOCK ? WS_CLOSE : WS_KNOCK), ISBLESSED); + else + { + coord zap_loc; + char loc; + + zap_loc.y = hero.y + delta.y; + zap_loc.x = hero.x + delta.x; + loc = winat(zap_loc.y, zap_loc.x); + + switch (loc) + { + case SECRETDOOR: + if (which == WS_KNOCK) + { + mvaddch(zap_loc.y, zap_loc.x, DOOR); + seemsg("A secret door stands revealed before you!"); + + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + } + else + nothing_message(flags); + break; + + case DOOR: + + if (which == WS_CLOSE) + { + mvaddch(zap_loc.y, zap_loc.x, SECRETDOOR); + msg("You sucessfully block off the door.") +; + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + } + else + nothing_message(flags); + + break; + + default: + nothing_message(flags); + } + } + + break; + default: + msg("What a bizarre schtick!"); + } +} + +/* + drain() + Do drain hit points from player shtick +*/ + +void +drain(int ymin, int ymax, int xmin, int xmax) +{ + int i, j, cnt; + struct thing *ick; + struct linked_list *item; + + /* First count how many things we need to spread the hit points among */ + + for (cnt = 0, i = ymin; i <= ymax; i++) + for (j = xmin; j <= xmax; j++) + if (isalpha(mvwinch(mw, i, j))) + cnt++; + + if (cnt == 0) + { + msg("You have a tingling feeling."); + return; + } + else + msg("You feel weaker."); + + cnt = pstats.s_hpt / cnt; + pstats.s_hpt /= 2; + + /* Now zot all of the monsters */ + + for (i = ymin; i <= ymax; i++) + for (j = xmin; j <= xmax; j++) + if (isalpha(mvwinch(mw, i, j)) && ((item = find_mons(i, j)) != NULL)) + { + ick = THINGPTR(item); + + if (on(*ick, CANSELL)) + { + luck++; + aggravate(); + } + + if ((ick->t_stats.s_hpt -= cnt) < 1) + killed(&player, item, + cansee(i, j) && !on(*ick, ISINVIS), POINTS); + } +} + +/* + charge_str() + charge a wand for wizards. +*/ + +char * +charge_str(struct object *obj, char *buf) +{ + if (buf == NULL) + return( "[UltraRogue Bug #103]" ); + + if (!(obj->o_flags & ISKNOW)) + buf[0] = '\0'; + else if (obj->o_charges == 1) + sprintf(buf, " [%d charge]", obj->o_charges); + else + sprintf(buf, " [%d charges]", obj->o_charges); + + return(buf); +} + + +/* + shoot_bolt() + fires a bolt from the given starting point in the given direction + struct thing *shooter; + coord start, dir; + int get_points; should player get exp points? + short reason; reason for dying + char *name; fire, nerve, cold, etc + int damage; make zapee suffer +*/ + +void +shoot_bolt(struct thing *shooter, coord start, coord dir, int get_points, int reason, char *name, int damage) +{ + int ch; + char dirch; + int change; + short y, x; + coord pos; + coord spotpos[BOLT_LENGTH + 1]; + int ret_val = FALSE;/* True if breathing monster gets killed */ + struct linked_list *item; + struct thing *tp; + char *mname; + int bounced; /* where along BOLT_LENGTH it hit a wall */ + int player_damage; /* damage if player saved */ + int no_effect; /* zap does not effect zappee */ + int take_that[BOLT_LENGTH + 1]; /* damage to each monster */ + + /* last spot for player */ + + debug("%s does %d damage", name, damage); + + switch (dir.y + dir.x) + { + case 0: + dirch = '/'; + break; + + case 1: + case -1: + dirch = (dir.y == 0 ? '-' : '|'); + break; + + case 2: + case -2: + dirch = '\\'; + break; + } + + pos.y = start.y + dir.y; + pos.x = start.x + dir.x; + change = FALSE; + + for (y = 0; y < BOLT_LENGTH + 1; y++) + take_that[y] = 0; + + bounced = 0; + + for (y = 0; y < BOLT_LENGTH; y++) + { + no_effect = FALSE; + ch = winat(pos.y, pos.x); + spotpos[y] = pos; + + /* Bolt damage dimishes over space */ + + damage = max(1, damage - (y / 3)); + + /* Are we at hero? */ + + if (ce(pos, hero)) + goto at_hero; + + switch(ch) + { + case SECRETDOOR: + case '|': + case '-': + case ' ': + bounced = y; + if (dirch == '-' || dirch == '|') + { + dir.y = -dir.y; + dir.x = -dir.x; + } + else + switch (ch) + { + case '|': + case '-': + case SECRETDOOR: + { + struct room *rp; + + rp = roomin(pos); + + if (pos.y == rp->r_pos.y || + pos.y == rp->r_pos.y + rp->r_max.y - 1) + { + dir.y = -dir.y; + change ^= TRUE; + } + + if (pos.x == rp->r_pos.x || + pos.x == rp->r_pos.x + rp->r_max.x - 1) + { + dir.x = -dir.x; + change ^= TRUE; + } + } + default: /* A wall */ + { + char chy = CCHAR( mvinch(pos.y - dir.y, pos.x + dir.x)), chx = CCHAR( mvinch(pos.y + dir.y, pos.x - dir.x) ); + + if (chy != WALL && chy != SECRETDOOR && + chy != '-' && chy != '|') + { + dir.y = -dir.y; + change = TRUE; + } + else if (chx != WALL && chx != SECRETDOOR && + chx != '-' && chx != '|') + { + dir.x = -dir.x; + change = TRUE; + } + else + { + dir.y = -dir.y; + dir.x = -dir.x; + } + } + } + + /* Do we change how the bolt looks? */ + + if (change) + { + change = FALSE; + + if (dirch == '\\') + dirch = '/'; + else if (dirch == '/') + dirch = '\\'; + } + + break; + + default: + if (isalpha(ch)) /* hit a monster */ + { + item = find_mons(pos.y, pos.x); + + if (item == NULL) + { + debug("Can't find monster %c @ %d %d.", + ch, pos.y, pos.x); + + continue; + } + + tp = THINGPTR(item); + mname = on(player, ISBLIND) ? "monster" : monsters[tp->t_index].m_name; + + /* Disguised monsters stay hidden if they save */ + + if (on(*tp, ISDISGUISE) && save_throw(VS_MAGIC, tp) && + (tp->t_type != tp->t_disguise) && off(player, ISBLIND)) + { + msg("Wait! That's a %s!", mname); + turn_off(*tp, ISDISGUISE); + } + + tp->t_wasshot = TRUE; + + if (on(*tp, CANSELL)) + { + luck++; + aggravate(); + } + + /* Hit the monster -- does it do damage? */ + + if (strcmp(name, "ice") == 0) + { + if (on(*tp, NOCOLD) || on(*tp, ISUNDEAD)) + no_effect = TRUE; + } + else if (strcmp(name, "flame") == 0) + { + if (on(*tp, NOFIRE)) + no_effect = TRUE; + + if (on(*tp, CANBBURN)) + { + seemsg("The %s is burned to death by the flame.", + mname); + + take_that[y] += tp->t_stats.s_hpt + 1; + ret_val = TRUE; + } + } + else if (strcmp(name, "lightning bolt") == 0) + { + if (on(*tp, NOBOLT)) + no_effect = TRUE; + + if (on(*tp, BOLTDIVIDE)) + { + no_effect = TRUE; + + if (creat_mons(tp, tp->t_index, NOMESSAGE)) + seemsg("The %s divides the %s.", name, mname); + } + } + + if (no_effect == TRUE) + { + msg("The %s has no effect on the %s.", name, + on(player, ISBLIND) ? "monster" : mname); + } + else + { + take_that[(bounced ? bounced-- : y)] += + save_throw(VS_MAGIC, tp) ? damage / 2 : damage; + } + } + else if (pos.y == hero.y && pos.x == hero.x) + { + at_hero: + player_damage = damage; + running = fighting = FALSE; + bounced = 0; + + if (cur_armor != NULL && + cur_armor->o_which == CRYSTAL_ARMOR && + (strcmp(name, "acid") == 0)) + { + player_damage = 0; + msg("The acid splashes harmlessly against your armor!"); + } + else if (((cur_armor != NULL && + cur_armor->o_which == CRYSTAL_ARMOR) || + (on(player, ISELECTRIC)) || + is_wearing(R_ELECTRESIST)) && + (strcmp(name, "lightning bolt") == 0)) + { + player_damage = 0; + + if (rnd(100) < 75 + && cur_weapon != NULL + && shooter != &player) + { + cur_weapon->o_flags |= ISZAPPED; + cur_weapon->o_charges += (10 + rnd(15)); + } + + if (cur_weapon != NULL && cur_armor != NULL) + msg("Your armor and %s are covered with dancing blue lights!", weaps[cur_weapon->o_which].w_name); + else if (cur_armor != NULL) + msg("Your armor is covered with dancing blue lights!"); + else if (cur_weapon != NULL) + msg("Your %s is covered with dancing blue lights!", weaps[cur_weapon->o_which].w_name); + else + msg("You are momentarily covered with dancing blue lights."); + } + else if ((is_wearing(R_FIRERESIST) || on(player, NOFIRE)) && + (strcmp(name, "flame") == 0)) + { + player_damage = 0; + msg("The flame bathes you harmlessly."); + } + else if ((is_wearing(R_COLDRESIST) || on(player, NOCOLD)) && + (strcmp(name, "ice") == 0)) + { + player_damage = 0; + msg("The ice cracks and quickly melts around you."); + } + else if (save(VS_MAGIC)) + { + take_that[BOLT_LENGTH] += player_damage / 2; + debug("Player dodges %s for %d.", name, player_damage / 2); + } + else + { + debug("Player zapped by %s for %d.", name, player_damage); + take_that[BOLT_LENGTH] += player_damage; + } + + /* Check for gas with special effects */ + + if (off(player, HASOXYGEN) && !is_wearing(R_BREATHE) && !save(VS_BREATH)) + { + if (strcmp(name, "nerve gas") == 0) + { + if (no_command == 0) + { + msg("The nerve gas paralyzes you."); + no_command = FREEZETIME; + } + } + else if (strcmp(name, "sleeping gas") == 0) + { + if (no_command == 0) + { + msg("The sleeping gas puts you to sleep."); + no_command = SLEEPTIME; + } + } + else if (strcmp(name, "slow gas") == 0 + && !is_wearing(R_FREEDOM)) + { + msg("You feel yourself moving %sslower.", + on(player, ISSLOW) ? "even " : ""); + + if (on(player, ISSLOW)) + lengthen_fuse(FUSE_NOSLOW, rnd(10) + 4); + else + { + turn_on(player, ISSLOW); + player.t_turn = TRUE; + light_fuse(FUSE_NOSLOW, 0, rnd(10) + 4, AFTER); + } + } + else if (strcmp(name, "fear gas") == 0) + { + if (!(on(player, ISFLEE) && + (player.t_chasee==shooter)) && + (shooter != &player)) + { + if (off(player, SUPERHERO) + && player.t_ctype != C_PALADIN) + { + turn_on(player, ISFLEE); + player.t_chasee = shooter; + player.t_ischasing = FALSE; + msg("The fear gas terrifies you."); + } + else + msg("The fear gas has no effect."); + } + } + } + } + mvwaddch(cw, pos.y, pos.x, dirch); + wrefresh(cw); + } + pos.y += dir.y; + pos.x += dir.x; + } + + /* + * Now that we have determined the damage for the length of the bolt, + * apply it to each monster (and then the player) in turn + */ + + for (x = 0; x < BOLT_LENGTH; x++) + { + ch = winat(spotpos[x].y, spotpos[x].x); + + if (take_that[x] != 0 && isalpha(ch)) + { + if ((item = find_mons(spotpos[x].y, spotpos[x].x)) == NULL) + { + debug("Can't find monster %c @ %d %d.", + ch, spotpos[x].y, spotpos[x].x); + + continue; + } + else + tp = THINGPTR(item); + + mname = on(player, ISBLIND) ? "monster" : monsters[tp->t_index].m_name; + + debug("Zapped %s for %d (%d)", + mname, take_that[x], tp->t_stats.s_hpt - take_that[x]); + + if ((tp->t_stats.s_hpt -= take_that[x]) <= 0) + { + if (cansee(tp->t_pos.y, tp->t_pos.x)) + msg("The %s kills the %s.", name, + on(player, ISBLIND) ? "monster" : mname); + + killed(shooter, item, NOMESSAGE, get_points); + ret_val = TRUE; + } + else if (get_points) + { + chase_it(&spotpos[x], &player); + summon_help(tp, NOFORCE); + } + } + + if (spotpos[x].y >= 0 && spotpos[x].x >= 0 && + spotpos[x].y < LINES && spotpos[x].x < COLS) + mvwaddch(cw, spotpos[x].y, spotpos[x].x, + show(spotpos[x].y, spotpos[x].x)); + } + + if (take_that[BOLT_LENGTH] != 0) + { + if (tp != THINGPTR(fam_ptr)) + { + msg("You are hit by the %s.", name); + + if ((pstats.s_hpt -= take_that[BOLT_LENGTH]) <= 0) + { + death(reason); + return; + } + } + } + + return; +} + +/* + monster_do_zap() + monster gets the effect +*/ + +void +monster_do_zap(struct thing *zapper, int which, int flags) +{ + struct stats *curp = &(zapper->t_stats); + int blessed = flags & ISBLESSED; + int cursed = flags & ISCURSED; + int shoot = FALSE; + int damage; + int ndice, nsides; + int ch; + char *bolt_name = NULL; + coord pos; + + switch(which) + { + case WS_MISSILE: + bolt_name = "magic missile"; + pos = do_motion('*', delta.y, delta.x, zapper); + ch = winat(pos.y, pos.x); + ndice = curp->s_lvl; + nsides = 4; + + if (cursed) + nsides /= 2; + else if (blessed) + nsides *= 2; + + damage = roll(ndice, nsides); + + if (ch == PLAYER) + { + if (save(VS_MAGIC)) /* help the player */ + msg("You evade the %s.", bolt_name); + else + { + msg("You are hit by the %s.", bolt_name); + + if ((pstats.s_hpt -= damage) <= 0) + death(D_BOLT); + } + } + else if (isalpha(ch)) + { + struct linked_list *item = find_mons(pos.y, pos.x); + + if (item != NULL) + { + struct thing *tp = THINGPTR(item); + int see_it = cansee(pos.y, pos.x); + + if ((tp->t_stats.s_hpt -= damage) <= 0) + killed(zapper, item, see_it, (zapper == THINGPTR(fam_ptr))); + else if (see_it) + msg("The %s hits the monster.", bolt_name); + } + } + + if (pos.y >= 0 && pos.x >= 0 && + pos.y < LINES && pos.x < COLS) + mvwaddch(cw, pos.y, pos.x, show(pos.y, pos.x)); + + break; + + case WS_CANCEL: + cancel_player(off(*zapper, ISUNIQUE)); + break; + + case WS_COLD: + shoot = TRUE; + bolt_name = "cold"; + break; + + case WS_FIRE: + shoot = TRUE; + bolt_name = "fire"; + break; + + case WS_ELECT: + shoot = TRUE; + bolt_name = "lightning"; + break; + + default: + debug("'%s' is a strange stick for a monster to zap!", + ws_magic[which].mi_name); + break; + } + + if (shoot) + { + ndice = curp->s_lvl / 2 + 1; + nsides = 6; + + if (cursed) + nsides /= 2; + else if (blessed) + nsides *= 2; + + damage = roll(ndice, nsides); + shoot_bolt(zapper, zapper->t_pos, delta, + (zapper == THINGPTR(fam_ptr)), D_BOLT, bolt_name, damage); + } +} + +struct powers +{ + unsigned long p_flag; + int fuse_id; +}; + +/* Order in which powers will attempt to be cancelled */ + +struct powers player_powers[] = +{ + { ISHASTE, FUSE_NOHASTE }, + { ISELECTRIC, FUSE_UNELECTRIFY }, + { CANINWALL, FUSE_UNPHASE }, + { CANFLY, FUSE_UNFLY }, + { ISINVIS, FUSE_APPEAR }, + { CANREFLECT, FUSE_UNGAZE }, + { ISDISGUISE, FUSE_UNDISGUISE }, + { HASMSHIELD, FUSE_UNMSHIELD }, + { ISREGEN, FUSE_UNREGEN }, + { CANSEE, FUSE_UNSEE }, + { NOCOLD, FUSE_UNCOLD }, + { NOFIRE, FUSE_UNHOT }, + { HASOXYGEN, FUSE_UNBREATHE }, + { HASSHIELD, FUSE_UNSHIELD }, + { ULONG_MAX, FUSE_NULL } +}; + +void +cancel_player(int not_unique) +{ + struct powers *pp; + int no_effect = TRUE; + + for(pp = player_powers; pp->p_flag != ULONG_MAX; pp++) + { + if (on(player, pp->p_flag) && !save(VS_MAGIC)) + { + fuses[pp->fuse_id].func(NULL); + extinguish_fuse(pp->fuse_id); + no_effect = FALSE; + + if (not_unique) /* Gods might cancel everything */ + break; + } + } + + if (no_effect) + nothing_message(ISNORMAL); +}