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: