Mercurial > hg > early-roguelike
view 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 source
/* 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);