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: