view arogue7/sticks.c @ 239:837044d2c362

Merge the GCC5 and build fix branches. This fixes all warnings produced by GCC 5, except the ones related to system functions. Those could be fixed by including the proper headers, but it would be better to replace the system-dependent code with functions from mdport.c.
author John "Elwin" Edwards
date Fri, 11 Mar 2016 19:47:52 -0500
parents e1cd27c5464f
children e52a8a7ad4c5
line wrap: on
line source

/*
 * sticks.c - Functions to implement the various sticks one might find
 *
 * Advanced Rogue
 * Copyright (C) 1984, 1985, 1986 Michael Morgan, Ken Dalka and AT&T
 * 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.
 */

/*
 * Functions to implement the various sticks one might find
 * while wandering around the dungeon.
 */

#include "curses.h"
#include <ctype.h>
#include <string.h>
#include "rogue.h"

void drain(int ymin, int ymax, int xmin, int xmax);

/*
 * zap a stick and see what happens
 */
void
do_zap(struct thing *zapper, struct object *obj, coord *direction, int which, 
       int flags)
{
    register struct linked_list *item = NULL;
    register struct thing *tp;
    register int y, x, bonus;
    struct linked_list *nitem;
    struct object *nobj;
    bool cursed, blessed, is_player;
    char *mname = "";

    cursed = flags & ISCURSED;
    blessed = flags & ISBLESSED;

    if (obj && obj->o_type != RELIC) { /* all relics are chargeless */
	if (obj->o_charges < 1) {
	    msg(nothing);
	    return;
	}
        obj->o_charges--;
    }
    if (which == WS_WONDER) {
	switch (rnd(14)) {
	    case  0: which = WS_ELECT;
	    when  1: which = WS_FIRE;
	    when  2: which = WS_COLD;
	    when  3: which = WS_POLYMORPH;
	    when  4: which = WS_MISSILE;
	    when  5: which = WS_SLOW_M;
	    when  6: which = WS_TELMON;
	    when  7: which = WS_CANCEL;
	    when  8: which = WS_CONFMON;
	    when  9: which = WS_DISINTEGRATE;
	    when 10: which = WS_PETRIFY;
	    when 11: which = WS_PARALYZE;
	    when 12: which = WS_MDEG;
	    when 13: which = WS_FEAR;
	}
	if(ws_magic[which].mi_curse>0 && rnd(100)<=ws_magic[which].mi_curse){
	    cursed = TRUE;
	    blessed = FALSE;
	}
    }

    tp = NULL;
    switch (which) {
	case WS_POLYMORPH:
	case WS_SLOW_M:
	case WS_TELMON:
	case WS_CANCEL:
	case WS_CONFMON:
	case WS_DISINTEGRATE:
	case WS_PETRIFY:
	case WS_PARALYZE:
	case WS_MDEG:
	case WS_FEAR:
	    y = zapper->t_pos.y;
	    x = zapper->t_pos.x;

	    do {
		y += direction->y;
		x += direction->x;
	    }
	    while (shoot_ok(winat(y, x)) && !(y == hero.y && x == hero.x));

	    if (y == hero.y && x == hero.x)
		is_player = TRUE;
	    else if (isalpha(mvwinch(mw, y, x))) {
		item = find_mons(y, x);
		tp = THINGPTR(item);
		runto(tp, &hero);
		turn_off(*tp, CANSURPRISE);
		mname = monster_name(tp);
		is_player = FALSE;

		/* The monster may not like being shot at */
		if ((zapper == &player)	&&
		    on(*tp, ISCHARMED)	&&
		    save(VS_MAGIC, tp, 0)) {
		    msg("The eyes of %s turn clear.", prname(mname, FALSE));
		    turn_off(*tp, ISCHARMED);
		    mname = monster_name(tp);
		}
	    }
	    else {
		/*
		 * if monster misses player because the player dodged then lessen
		 * the chances he will use the wand again since the player appears
		 * to be rather dextrous
		 */
		if (zapper != &player) 
		    zapper->t_wand = zapper->t_wand * 3 / 4;
	    }
    }
    switch (which) {
	case WS_LIGHT:
	    /*
	     * Reddy Kilowat wand.  Light up the room
	     */
	    blue_light(blessed, cursed);
	when 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 (but leave the monsters alone
	     * if the stick is cursed)
	     */
	    if (pstats.s_hpt < 2) {
		msg("You are too weak to use it.");
	    }
	    else if (cursed)
		pstats.s_hpt /= 2;
	    else
		drain(hero.y-1, hero.y+1, hero.x-1, hero.x+1);

	when WS_POLYMORPH:
	{
	    register char oldch;
	    register struct room *rp;
	    register struct linked_list *pitem;
	    coord delta;

	    if (tp == NULL)
		break;
	    if (save(VS_MAGIC, tp, 0)) {
		msg(nothing);
		break;
	    }
	    rp = roomin(&tp->t_pos);
	    check_residue(tp);
	    delta.x = x;
	    delta.y = y;
	    detach(mlist, item);
	    oldch = tp->t_oldch;
	    pitem = tp->t_pack; /* save his pack */
	    tp->t_pack = NULL;
	    new_monster(item,rnd(NUMMONST-NUMUNIQUE-1)+1,&delta,FALSE);
	    if (tp->t_pack != NULL) 
		o_free_list (tp->t_pack);
	    tp->t_pack = pitem;
	    if (isalpha(mvwinch(cw, y, x)))
		mvwaddch(cw, y, x, tp->t_type);
	    tp->t_oldch = oldch;
	    /*
	     * should the room light up?
	     */
	    if (on(*tp, HASFIRE)) {
		if (rp) {
		    register struct linked_list *fire_item;

		    fire_item = creat_item();
		    ldata(fire_item) = (char *) tp;
		    attach(rp->r_fires, fire_item);
		    rp->r_flags |= HASFIRE;
		    if (cansee(tp->t_pos.y,tp->t_pos.x) &&
			next(rp->r_fires) == NULL) light(&hero);
		}
	    }
	    runto(tp, &hero);
	    msg(terse ? "A new %s!" 
		      : "You have created a new %s!",
		      monster_name(tp));
        }

	when WS_PETRIFY:
	    if (tp == NULL)
		break;
	    if (save(VS_MAGIC, tp, 0)) {
		msg(nothing);
		break;
	    }
	    check_residue(tp);
	    turn_on(*tp, ISSTONE);
	    turn_on(*tp, NOSTONE);
	    turn_off(*tp, ISRUN);
	    turn_off(*tp, ISINVIS);
	    turn_off(*tp, CANSURPRISE);
	    turn_off(*tp, ISDISGUISE);
	    tp->t_action = A_NIL;
	    tp->t_no_move = 0;
	    msg("%s is turned to stone!",prname(mname, TRUE));

	when WS_TELMON:
	{
	    register int rm;
	    register struct room *rp;

	    if (tp == NULL)
		break;
	    if (save(VS_MAGIC, tp, 0)) {
		msg(nothing);
		break;
	    }
	    rp = NULL;
	    check_residue(tp);
	    tp->t_action = A_FREEZE; /* creature is disoriented */
	    tp->t_no_move = 2;
	    if (cursed) {	/* Teleport monster to player */
		if ((y == (hero.y + direction->y)) &&
		    (x == (hero.x + direction->x)))
			msg(nothing);
		else {
		    tp->t_pos.y = hero.y + direction->y;
		    tp->t_pos.x = hero.x + direction->x;
		}
	    }
	    else if (blessed) {	/* Get rid of monster */
		killed(item, FALSE, TRUE, TRUE);
		return;
	    }
	    else {
		register int i=0;

		do {	/* Move monster to another room */
		    rm = rnd_room();
		    rnd_pos(&rooms[rm], &tp->t_pos);
		}until(winat(tp->t_pos.y,tp->t_pos.x)==FLOOR ||i++>500);
		rp = &rooms[rm];
	    }

	    /* Now move the monster */
	    if (isalpha(mvwinch(cw, y, x)))
		mvwaddch(cw, y, x, tp->t_oldch);
	    mvwaddch(mw, y, x, ' ');
	    mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, tp->t_type);
	    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) );
	    /*
	     * check to see if room that creature appears in should
	     * light up
	     */
	    if (on(*tp, HASFIRE)) {
		if (rp) {
		    register struct linked_list *fire_item;

		    fire_item = creat_item();
		    ldata(fire_item) = (char *) tp;
		    attach(rp->r_fires, fire_item);
		    rp->r_flags |= HASFIRE;
		    if(cansee(tp->t_pos.y, tp->t_pos.x) && 
		       next(rp->r_fires) == NULL)
			light(&hero);
		}
	    }
	}
	when WS_CANCEL:
	    if (tp == NULL)
		break;
	    if (save(VS_MAGIC, tp, 0)) {
		msg(nothing);
		break;
	    }
	    check_residue(tp);
	    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] &= CANC6MASK;
	    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;

	when WS_MISSILE:
	{
	    int dice;
	    static struct object bolt =
	    {
		MISSILE , {0, 0}, "", 0, "", "1d4 " , NULL, 0, WS_MISSILE, 50, 1
	    };

	    if (!obj)
		dice = zapper->t_stats.s_lvl;
	    if (obj->o_type == RELIC)
		 dice = 15;
	    else if (EQUAL(ws_type[which], "staff"))
		 dice = 10;
	    else
		 dice = 6;
	    sprintf(bolt.o_hurldmg, "%dd4", dice);
	    do_motion(&bolt, direction->y, direction->x, zapper);
	    if (!hit_monster(unc(bolt.o_pos), &bolt, zapper))
	       msg("The missile vanishes with a puff of smoke");
	}
	when WS_HIT:
	{
	    register char ch;
	    struct object strike; /* don't want to change sticks attributes */

	    direction->y += hero.y;
	    direction->x += hero.x;
	    ch = CCHAR( winat(direction->y, direction->x) );
	    if (isalpha(ch))
	    {
		strike = *obj;
		strike.o_hplus  = 6;
		if (EQUAL(ws_type[which], "staff"))
		    strncpy(strike.o_damage,"3d8",sizeof(strike.o_damage));
		else
		    strncpy(strike.o_damage,"2d8",sizeof(strike.o_damage));
		fight(direction, &strike, FALSE);
	    }
	}
	when WS_SLOW_M:
	    if (is_player) {
		add_slow();
		break;
	    }
	    if (tp == NULL)
		break;
	    if (cursed) {
		if (on(*tp, ISSLOW))
		    turn_off(*tp, ISSLOW);
		else
		    turn_on(*tp, ISHASTE);
		break;
	    }
	    if ((on(*tp,ISUNIQUE) && save(VS_MAGIC,tp,0)) || on(*tp,NOSLOW)) {
		msg(nothing);
		break;
	    }
	    else if (blessed) {
		turn_off(*tp, ISRUN);
		turn_on(*tp, ISHELD);
	    }
	    /*
	     * always slow in case he breaks free of HOLD
	     */
	    if (on(*tp, ISHASTE))
		turn_off(*tp, ISHASTE);
	    else
		turn_on(*tp, ISSLOW);

	when WS_CHARGE:
	    if (ws_know[WS_CHARGE] != TRUE && obj)
		msg("This is a wand of charging.");
	    nitem = get_item(pack, "charge", STICK, FALSE, FALSE);
	    if (nitem != NULL) {
		nobj = OBJPTR(nitem);
	        if ((++(nobj->o_charges) == 1) && (nobj->o_which == WS_HIT))
		    fix_stick(nobj);
		if (blessed) ++(nobj->o_charges);
	        if (EQUAL(ws_type[nobj->o_which], "staff")) {
		    if (nobj->o_charges > 100) 
		        nobj->o_charges = 100;
		}
	        else {
		    if (nobj->o_charges > 50)
		        nobj->o_charges = 50;
		}
	    }
	when WS_ELECT:
	    shoot_bolt(	zapper, zapper->t_pos, *direction, TRUE, D_BOLT, 
			"lightning bolt", roll(zapper->t_stats.s_lvl,6));

	when WS_FIRE:
	    shoot_bolt(	zapper, zapper->t_pos, *direction, TRUE, D_BOLT, 
			"flame", roll(zapper->t_stats.s_lvl,6));

	when WS_COLD:
	    shoot_bolt(	zapper, zapper->t_pos, *direction, TRUE, D_BOLT, 
			"ice", roll(zapper->t_stats.s_lvl,6));

	when WS_CONFMON:
	    if (cursed || is_player) { 
		if (!save(VS_WAND, &player, 0)) {
		    dsrpt_player();
		    confus_player();
		}
		else {
		    if (zapper != &player) zapper->t_wand /= 2;
		    msg(nothing);
		}
	    }
	    else {
		if (tp == NULL)
		    break;
		if (save(VS_MAGIC, tp, 0) || on(*tp, ISCLEAR))
		     msg(nothing);
		else
		     turn_on (*tp, ISHUH);
	    }
	when WS_PARALYZE:
	    if (is_player || cursed) {
		if ((obj && obj->o_type==RELIC) || !save(VS_WAND, &player, 0)){
		    player.t_no_move += 2 * movement(&player) * FREEZETIME;
		    player.t_action = A_FREEZE;
		    msg("You can't move.");
		}
		else {
		    if (zapper != &player) zapper->t_wand /= 2;
		    msg(nothing);
		}
	    }
	    else {
		if (tp == NULL)
		    break;
		bonus = 0;
		if (blessed) bonus = -3;
		if (((obj && obj->o_type==RELIC) || !save(VS_WAND,tp,bonus)) &&
		    off(*tp, NOPARALYZE)) {
		    tp->t_no_move += 2 * movement(tp) * FREEZETIME;
		    tp->t_action = A_FREEZE;
		}
		else {
		    msg(nothing);
		}
	    }
	when WS_FEAR:
	    if (is_player) {
		if (!on(player, ISFLEE)		|| 
		    ISWEARING(R_HEROISM)	|| 
		    save(VS_WAND, &player, 0)) {
			msg(nothing);
			zapper->t_wand /= 2;
		}
		else {
		    turn_on(player, ISFLEE);
		    player.t_dest = &zapper->t_pos;
		    msg("The sight of %s terrifies you.", prname(mname, FALSE));
		}
		break;
	    }
	    if (tp == NULL)
		break;
	    bonus = 0;
	    if (blessed) bonus = -3;
	    if(save(VS_WAND, tp,bonus) || on(*tp,ISUNDEAD) || on(*tp,NOFEAR)){
		    msg(nothing);
		    break;
	    }
	    turn_on(*tp, ISFLEE);
	    turn_on(*tp, WASTURNED);

	    /* Stop it from attacking us */
	    dsrpt_monster(tp, TRUE, cansee(tp->t_pos.y, tp->t_pos.x));

	    /* If monster was suffocating, stop it */
	    if (on(*tp, DIDSUFFOCATE)) {
		turn_off(*tp, DIDSUFFOCATE);
		extinguish(suffocate);
	    }

	    /* If monster held us, stop it */
	    if (on(*tp, DIDHOLD) && (--hold_count == 0))
		    turn_off(player, ISHELD);
	    turn_off(*tp, DIDHOLD);

	    /* It is okay to turn tail */
	    tp->t_oldpos = tp->t_pos;

	when WS_MDEG:
	    if (is_player) {
		if (save(VS_WAND, &player, 0)) {
		    msg (nothing);
		    zapper->t_wand /= 2;
		    break;
		}
		pstats.s_hpt /= 2;
		if (pstats.s_hpt <= 0) {
		    msg("Your life has been sucked from you -- More --");
		    wait_for(' ');
		    death(zapper->t_index);
		}
		else
		    msg("You feel a great drain on your system");
	    }
	    if (tp == NULL)
		break;
	    if (cursed) {
		 tp->t_stats.s_hpt *= 2;
		 msg("%s appears to be stronger now!", prname(mname, TRUE));
	    }
	    else if (on(*tp, ISUNIQUE) && save(VS_WAND, tp, 0))
		 msg (nothing);
	    else {
		 tp->t_stats.s_hpt /= 2;
		 msg("%s appears to be weaker now", prname(mname, TRUE));
	    }
	    if (tp->t_stats.s_hpt < 1)
		 killed(item, TRUE, TRUE, TRUE);
	when WS_DISINTEGRATE:
	    if (tp == NULL)
		break;
	    if (cursed) {
		register int m1, m2;
		coord mp;
		struct linked_list *titem;
		char ch;
		struct thing *th;

		if (on(*tp, ISUNIQUE)) {
		    msg (nothing);
		    break;
		}
		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++) {
			if (m1 == hero.x && m2 == hero.y)
			    continue;
			ch = CCHAR( winat(m2,m1) );
			if (shoot_ok(ch)) {
			    mp.x = m1;	/* create it */
			    mp.y = m2;
			    titem = new_item(sizeof(struct thing));
			    new_monster(titem,(short)tp->t_index,&mp,FALSE);
			    th = THINGPTR(titem);
			    turn_on (*th, ISMEAN);
			    runto(th,&hero);
			    if (on(*th, HASFIRE)) {
				register struct room *rp;

				rp = roomin(&th->t_pos);
				if (rp) {
				    register struct linked_list *fire_item;

				    fire_item = creat_item();
				    ldata(fire_item) = (char *) th;
				    attach(rp->r_fires, fire_item);
				    rp->r_flags |= HASFIRE;
				    if (cansee(th->t_pos.y, th->t_pos.x) &&
					next(rp->r_fires) == NULL)
					light(&hero);
				}
			    }
			}
		    }
		}
	    }
	    else { /* if its a UNIQUE it might still live */
		if (on(*tp, ISUNIQUE) && save(VS_MAGIC, tp, 0)) {
		    tp->t_stats.s_hpt /= 2;
		    if (tp->t_stats.s_hpt < 1) {
			 killed(item, FALSE, TRUE, TRUE);
			 msg("You have disintegrated %s", prname(mname, FALSE));
		    }
		    else {
			msg("%s appears wounded", prname(mname, TRUE));
		    }
		}
		else {
		    msg("You have disintegrated %s", prname(mname, FALSE));
		    killed (item, FALSE, TRUE, TRUE);
		}
	    }
	when WS_CURING:
	    if (cursed) {
		if (!save(VS_POISON, &player, 0)) {
		    msg("You feel extremely sick now");
		    pstats.s_hpt /=2;
		    if (pstats.s_hpt == 0) death (D_POISON);
		}
		if (!save(VS_WAND, &player, 0) && !ISWEARING(R_HEALTH)) {
		    turn_on(player, HASDISEASE);
		    turn_on(player, HASINFEST);
		    turn_on(player, DOROT);
		    fuse(cure_disease, NULL, roll(HEALTIME,SICKTIME), AFTER);
		    infest_dam++;
		}
		else msg("You fell momentarily sick");
	    }
	    else {
		if (on(player, HASDISEASE) || on(player, HASINFEST)) {
		    extinguish(cure_disease);
		    turn_off(player, HASINFEST);
		    infest_dam = 0;
		    cure_disease(); /* this prints message */
		}
		if (on(player, DOROT)) {
		    msg("You feel your skin returning to normal.");
		    turn_off(player, DOROT);
		}
		pstats.s_hpt += roll(pstats.s_lvl, blessed ? 6 : 4);
		if (pstats.s_hpt > max_stats.s_hpt)
		    pstats.s_hpt = max_stats.s_hpt;
		msg("You begin to feel %sbetter.", blessed ? "much " : "");
		    
	    }
	otherwise:
	    msg("What a bizarre schtick!");
    }
}


/*
 * drain:
 *	Do drain hit points from player shtick
 */

void
drain(int ymin, int ymax, int xmin, int xmax)
{
    register int i, j, count;
    register struct thing *ick;
    register struct linked_list *item;

    /*
     * First count how many things we need to spread the hit points among
     */
    count = 0;
    for (i = ymin; i <= ymax; i++) {
	if (i < 1 || i > lines - 3)
	    continue;
	for (j = xmin; j <= xmax; j++) {
	    if (j < 0 || j > cols - 1)
		continue;
	    if (isalpha(mvwinch(mw, i, j)))
		count++;
	}
    }
    if (count == 0)
    {
	msg("You have a tingling feeling");
	return;
    }
    count = pstats.s_hpt / count;
    pstats.s_hpt /= 2;
    /*
     * Now zot all of the monsters
     */
    for (i = ymin; i <= ymax; i++) {
	if (i < 1 || i > lines - 3)
	    continue;
	for (j = xmin; j <= xmax; j++) {
	    if (j < 0 || j > cols - 1)
		continue;
	    if (isalpha(mvwinch(mw, i, j)) &&
	        ((item = find_mons(i, j)) != NULL)) {
		ick = THINGPTR(item);
		if (on(*ick, ISUNIQUE) && save(VS_MAGIC, ick, 0)) 
		    ick->t_stats.s_hpt -= count / 2;
		else
		    ick->t_stats.s_hpt -= count;
		if (ick->t_stats.s_hpt < 1)
		    killed(item, 
			   cansee(i,j)&&(!on(*ick,ISINVIS)||on(player,CANSEE)),
			   TRUE, TRUE);
		else {
		    runto(ick, &hero);

		    /* 
		     * The monster may not like being shot at.  Since the
		     * shot is not aimed directly at the monster, we will
		     * give him a poorer save.
		     */
		    if (on(*ick, ISCHARMED) && save(VS_MAGIC, ick, -2)) {
			msg("The eyes of %s turn clear.",
			    prname(monster_name(ick), FALSE));
			turn_off(*ick, ISCHARMED);
		    }
		    if (cansee(i,j) && (!on(*ick,ISINVIS)||on(player,CANSEE)))
			    msg("%s appears wounded", 
				prname(monster_name(ick), TRUE));
		}
	    }
	}
    }
}

/*
 * initialize a stick
 */
void
fix_stick(struct object *cur)
{
    if (EQUAL(ws_type[cur->o_which], "staff")) {
	cur->o_weight = 100;
	cur->o_charges = 5 + rnd(10);
	strncpy(cur->o_damage, "2d3", sizeof(cur->o_damage));
	cur->o_hplus = 1;
	cur->o_dplus = 0;
	switch (cur->o_which) {
	    case WS_HIT:
		cur->o_hplus = 3;
		cur->o_dplus = 3;
		strncpy(cur->o_damage, "2d8", sizeof(cur->o_damage));
	    when WS_LIGHT:
		cur->o_charges = 20 + rnd(10);
	    }
    }
    else {
	strncpy(cur->o_damage, "1d3", sizeof(cur->o_damage));
	cur->o_weight = 60;
	cur->o_hplus = 1;
	cur->o_dplus = 0;
	cur->o_charges = 3 + rnd(5);
	switch (cur->o_which) {
	    case WS_HIT:
		cur->o_hplus = 3;
		cur->o_dplus = 3;
		strncpy(cur->o_damage, "1d8", sizeof(cur->o_damage));
	    when WS_LIGHT:
		cur->o_charges = 10 + rnd(10);
	    }
    }
    strncpy(cur->o_hurldmg, "1d1", sizeof(cur->o_hurldmg));

}

/*
 * Use the wand that our monster is wielding.
 */
void
m_use_wand(struct thing *monster)
{
    register struct object *obj;

    /* Make sure we really have it */
    if (monster->t_using) 
	obj = OBJPTR(monster->t_using);
    else {
	debug("Stick not set!");
	monster->t_action = A_NIL;
	return;
    }

    if (obj->o_type != STICK) {
	debug("Stick not selected!");
	monster->t_action = A_NIL;
	return;
    }
    /*
     * shoot the stick!
     * assume all blessed sticks are normal for now. 
     * Note that we don't get here if the wand is cursed.
     */
    msg("%s points a %s at you!", prname(monster_name(monster), TRUE),
	ws_type[obj->o_which]);
    do_zap(monster, obj, &monster->t_newpos, obj->o_which, 0);
    monster->t_wand /= 2; /* chance lowers with each use */
}

/*
 * type: type of item, NULL means stick
 * which: which item
 */
bool
need_dir(int type, int which)
{
    if (type == STICK || type == 0) {
	switch (which) {
	    case WS_LIGHT:
	    case WS_DRAIN: 
	    case WS_CHARGE:
	    case WS_CURING:
		return(FALSE);
	    default:
		return(TRUE);
	}
    }
    else if (type == RELIC) {
	switch (which) {
	    case MING_STAFF:
	    case ASMO_ROD:
	    case EMORI_CLOAK:
		return(TRUE);
	    default:
		return(FALSE);
	}
    }
return (FALSE); /* hope we don't get here */
}
/*
 * let the player zap a stick and see what happens
 */
bool
player_zap(int which, int flag)
{
    register struct linked_list *item;
    register struct object *obj;

    obj = NULL;
    if (which == 0) {
	/* This is a stick.  It takes 2 movement periods to zap it */
	if (player.t_action != C_ZAP) {
	    if ((item = get_item(pack,"zap with",ZAPPABLE,FALSE,FALSE)) == NULL)
		return(FALSE);

	    obj = OBJPTR(item);

	    if (need_dir(obj->o_type, obj->o_which)) {
		if (!get_dir(&player.t_newpos))
		    return(FALSE);
	    }
	    player.t_using = item;	/* Remember what it is */
	    player.t_action = C_ZAP;	/* We are quaffing */
	    player.t_no_move = 2 * movement(&player);
	    return(TRUE);
	}

	item = player.t_using;
	/* We've waited our time, let's shoot 'em up! */
	player.t_using = NULL;
	player.t_action = A_NIL;

	obj = OBJPTR(item);

	/* Handle relics specially here */
	if (obj->o_type == RELIC) {
	    switch (obj->o_which) {
		case ORCUS_WAND:
		    msg(nothing);
		    return(TRUE);
		when MING_STAFF:
		    which = WS_MISSILE;
		when EMORI_CLOAK:
		    which = WS_PARALYZE;
		    obj->o_charges = 0; /* one zap/day(whatever that is) */
		    fuse(cloak_charge, obj, CLOAK_TIME, AFTER);
		when ASMO_ROD:
		    switch (rnd(3)) {
			case 0:		which = WS_ELECT;
			when 1:		which = WS_COLD;
			otherwise:	which = WS_FIRE;
		    }
	    }
	}
	else {
	    which = obj->o_which;
	    ws_know[which] = TRUE;
	    flag = obj->o_flags;
	}
    }
    do_zap(&player, obj, &player.t_newpos, which, flag);
    return(TRUE);
}


/*
 * shoot_bolt fires a bolt from the given starting point in the
 * 	      given direction
 */

void
shoot_bolt(struct thing *shooter, coord start, coord dir, bool get_points, 
           short reason, char *name, int damage)
{
    register char dirch, ch;
    register bool used, change;
    register short y, x, bounces;
    coord pos;
    struct linked_list *target=NULL;
    struct {
	coord place;
	char oldch;
    } spotpos[BOLT_LENGTH];

    switch (dir.y + dir.x) {
	case 0: dirch = '/';
	when 1: case -1: dirch = (dir.y == 0 ? '-' : '|');
	when 2: case -2: dirch = '\\';
    }
    pos.y = start.y + dir.y;
    pos.x = start.x + dir.x;
    used = FALSE;
    change = FALSE;

    bounces = 0;	/* No bounces yet */
    for (y = 0; y < BOLT_LENGTH && !used; y++)
    {
	ch = CCHAR( winat(pos.y, pos.x) );
	spotpos[y].place = pos;
	spotpos[y].oldch = CCHAR( mvwinch(cw, pos.y, pos.x) );

	/* Are we at hero? */
	if (ce(pos, hero)) goto at_hero;

	switch (ch)
	{
	    case SECRETDOOR:
	    case '|':
	    case '-':
	    case ' ':
		if (dirch == '-' || dirch == '|') {
		    dir.y = -dir.y;
		    dir.x = -dir.x;
		}
		else {
		    char chx = CCHAR( mvinch(pos.y-dir.y, pos.x) ),
			 chy = CCHAR( mvinch(pos.y, pos.x-dir.x) );
		    bool anychange = FALSE; /* Did we change anthing */

		    if (chy == WALL || chy == SECRETDOOR ||
			chy == '-' || chy == '|') {
			dir.y = -dir.y;
			change ^= TRUE;	/* Change at least one direction */
			anychange = TRUE;
		    }
		    if (chx == WALL || chx == SECRETDOOR ||
			chx == '-' || chx == '|') {
			dir.x = -dir.x;
			change ^= TRUE;	/* Change at least one direction */
			anychange = TRUE;
		    }

		    /* If we didn't make any change, make both changes */
		    if (!anychange) {
			dir.x = -dir.x;
			dir.y = -dir.y;
		    }
		}

		/* Do we change how the bolt looks? */
		if (change) {
		    change = FALSE;
		    if (dirch == '\\') dirch = '/';
		    else if (dirch == '/') dirch = '\\';
		}

		y--;	/* The bounce doesn't count as using up the bolt */

		/* Make sure we aren't in an infinite bounce */
		if (++bounces > BOLT_LENGTH) used = TRUE;
		msg("The %s bounces", name);
		break;
	    default:
		if (isalpha(ch)) {
		    register struct linked_list *item;
		    register struct thing *tp;
		    register char *mname;
		    bool see_monster = cansee(pos.y, pos.x);

		    item = find_mons(unc(pos));
		    tp = THINGPTR(item);
		    mname = monster_name(tp);

		    /*
		     * If our prey shot this, let's record the fact that
		     * he can shoot, regardless of whether he hits us.
		     */
		    if ((tp->t_dest != NULL) && ce(*tp->t_dest, shooter->t_pos)) tp->t_wasshot = TRUE;

		    if (!save(VS_BREATH, tp, -(shooter->t_stats.s_lvl/10))) {
			if (see_monster) {
			    if (on(*tp, ISDISGUISE) &&
				(tp->t_type != tp->t_disguise)) {
				msg("Wait! That's a %s!", mname);
				turn_off(*tp, ISDISGUISE);
			    }

			    turn_off(*tp, CANSURPRISE);
			    msg("The %s hits %s", name, prname(mname, FALSE));
			}

			/* Should we start to chase the shooter? */
			if (shooter != &player			&&
			    shooter != tp			&&
			    shooter->t_index != tp->t_index	&&
			    (tp->t_dest == NULL || rnd(100) < 25)) {
			    /*
			     * If we're intelligent enough to realize that this
			     * is a friendly monster, we will attack the hero
			     * instead.
			     */
			    if (on(*shooter, ISFRIENDLY) &&
				 roll(3,6) < tp->t_stats.s_intel)
				 runto(tp, &hero);

			    /* Otherwise, let's chase the monster */
			    else runto(tp, &shooter->t_pos);
			}
			else if (shooter == &player) {
			    runto(tp, &hero);

			    /*
			     * If the player shot a charmed monster, it may
			     * not like being shot at.
			     */
			    if (on(*tp, ISCHARMED) && save(VS_MAGIC, tp, 0)) {
				msg("The eyes of %s turn clear.", 
				    prname(mname, FALSE));
				turn_off(*tp, ISCHARMED);
				mname = monster_name(tp);
			    }
			}

			/*
			 * Let the defender know that the attacker has
			 * missiles!
			 */
			if (ce(*tp->t_dest, shooter->t_pos))
			    tp->t_wasshot = TRUE;

			used = TRUE;

			/* Hit the monster -- does it do anything? */
			if ((EQUAL(name,"ice")           &&
			     (on(*tp, NOCOLD) || on(*tp, ISUNDEAD)))          ||
			    (EQUAL(name,"flame")         && on(*tp, NOFIRE))  ||
			    (EQUAL(name,"acid")          && on(*tp, NOACID))  ||
			    (EQUAL(name,"lightning bolt")&& on(*tp,NOBOLT))   ||
			    (EQUAL(name,"nerve gas")     &&on(*tp,NOPARALYZE))||
			    (EQUAL(name,"sleeping gas")  &&
			     (on(*tp, NOSLEEP) || on(*tp, ISUNDEAD)))         ||
			    (EQUAL(name,"slow gas")      && on(*tp,NOSLOW))   ||
			    (EQUAL(name,"fear gas")      && on(*tp,NOFEAR))   ||
			    (EQUAL(name,"confusion gas") && on(*tp,ISCLEAR))  ||
			    (EQUAL(name,"chlorine gas")  && on(*tp,NOGAS))) {
			    if (see_monster)
				msg("The %s has no effect on %s.",
					name, prname(mname, FALSE));
			}

			else {
			    bool see_him;

			    see_him = 
				    off(player, ISBLIND)		      &&
				    cansee(unc(tp->t_pos))		      &&
				    (off(*tp, ISINVIS) || on(player, CANSEE)) &&
				    (off(*tp, ISSHADOW)|| on(player, CANSEE)) &&
				    (off(*tp, CANSURPRISE)||ISWEARING(R_ALERT));

			    /* Did a spell get disrupted? */
			    dsrpt_monster(tp, FALSE, see_him);

			    /* 
			     * Check for gas with special effects 
			     */
			    if (EQUAL(name, "nerve gas")) {
				tp->t_no_move = movement(tp) * FREEZETIME;
				tp->t_action = A_FREEZE;
			    }
			    else if (EQUAL(name, "sleeping gas")) {
				tp->t_no_move = movement(tp) * SLEEPTIME;
				tp->t_action = A_FREEZE;
			    }
			    else if (EQUAL(name, "slow gas")) {
				if (on(*tp, ISHASTE))
				    turn_off(*tp, ISHASTE);
				else
				    turn_on(*tp, ISSLOW);
			    }
			    else if (EQUAL(name, "fear gas")) {
				turn_on(*tp, ISFLEE);
				tp->t_dest = &hero;

				/* It is okay to turn tail */
				tp->t_oldpos = tp->t_pos;
			    }
			    else if (EQUAL(name, "confusion gas")) {
				turn_on(*tp, ISHUH);
				tp->t_dest = &hero;
			    }
			    else if ((EQUAL(name, "lightning bolt")) &&
				     on(*tp, BOLTDIVIDE)) {
				    if (creat_mons(tp, tp->t_index, FALSE)) {
				      if (see_monster)
				       msg("The %s divides %s.",
					   name,prname(mname, FALSE));
				      light(&hero);
				    }
				    else if (see_monster)
					msg("The %s has no effect on %s.",
					    name, prname(mname, FALSE));
			    }
			    else {
				if (save(VS_BREATH, tp,
				         -(shooter->t_stats.s_lvl/10)))
				    damage /= 2;

				/* The poor fellow got killed! */
				if ((tp->t_stats.s_hpt -= damage) <= 0) {
				    if (see_monster)
					msg("The %s kills %s", 
					    name, prname(mname, FALSE));
				    else
				     msg("You hear a faint groan in the distance");
				    /*
				     * Instead of calling killed() here, we
				     * will record that the monster was killed
				     * and call it at the end of the routine,
				     * after we restore what was under the bolt.
				     * We have to do this because in the case
				     * of a bolt that first misses the monster
				     * and then gets it on the bounce.  If we
				     * call killed here, the 'missed' space in
				     * spotpos puts the monster back on the
				     * screen
				     */
				    target = item;
				}
				else {	/* Not dead, so just scream */
				     if (!see_monster)
				       msg("You hear a scream in the distance");
				}
			    }
			}
		    }
		    else if (isalpha(show(pos.y, pos.x))) {
			if (see_monster) {
			    if (terse)
				msg("%s misses", name);
			    else
				msg("The %s whizzes past %s",
					    name, prname(mname, FALSE));
			}
			if (get_points) runto(tp, &hero);
		    }
		}
		else if (pos.y == hero.y && pos.x == hero.x) {
at_hero: 	    if (!save(VS_BREATH, &player,
				-(shooter->t_stats.s_lvl/10))){
			if (terse)
			    msg("The %s hits you", name);
			else
			    msg("You are hit by the %s", name);
			used = TRUE;

			/* 
			 * The Amulet of Yendor protects against all "breath" 
			 *
			 * The following two if statements could be combined 
			 * into one, but it makes the compiler barf, so split 
			 * it up
			 */
			if (cur_relic[YENDOR_AMULET]			    ||
			    (EQUAL(name,"chlorine gas")&&on(player, NOGAS)) ||
			    (EQUAL(name,"sleeping gas")&&ISWEARING(R_ALERT))){
			     msg("The %s has no affect", name);
			}
			else if((EQUAL(name, "flame") && on(player, NOFIRE)) ||
			        (EQUAL(name, "ice")   && on(player, NOCOLD)) ||
			        (EQUAL(name,"lightning bolt")&& 
							 on(player,NOBOLT))  ||
			        (EQUAL(name,"fear gas")&&ISWEARING(R_HEROISM))){
			     msg("The %s has no affect", name);
			}

			else {
			    dsrpt_player();

			    /* 
			     * Check for gas with special effects 
			     */
			    if (EQUAL(name, "nerve gas")) {
				msg("The nerve gas paralyzes you.");
				player.t_no_move +=
					movement(&player) * FREEZETIME;
				player.t_action = A_FREEZE;
			    }
			    else if (EQUAL(name, "sleeping gas")) {
				msg("The sleeping gas puts you to sleep.");
				player.t_no_move +=
					movement(&player) * SLEEPTIME;
				player.t_action = A_FREEZE;
			    }
			    else if (EQUAL(name, "confusion gas")) {
				if (off(player, ISCLEAR)) {
				    if (on(player, ISHUH))
					lengthen(unconfuse,
					         rnd(20)+HUHDURATION);
				    else {
					turn_on(player, ISHUH);
					fuse(unconfuse, NULL,
					     rnd(20)+HUHDURATION, AFTER);
					msg(
					 "The confusion gas has confused you.");
				    }
				}
				else msg("You feel dizzy for a moment, but it quickly passes.");
			    }
			    else if (EQUAL(name, "slow gas")) {
				add_slow();
			    }
			    else if (EQUAL(name, "fear gas")) {
				turn_on(player, ISFLEE);
				player.t_dest = &shooter->t_pos;
				msg("The fear gas terrifies you.");
			    }
			    else {
				if (EQUAL(name, "acid")			&&
				    cur_armor != NULL			&&
				    !(cur_armor->o_flags & ISPROT)	&&
				    !save(VS_BREATH, &player, -2)	&&
				    cur_armor->o_ac < pstats.s_arm+1) {
				       msg("Your armor corrodes from the acid");
				       cur_armor->o_ac++;
				}
				if (save(VS_BREATH, &player,
					 -(shooter->t_stats.s_lvl/10)))
				    damage /= 2;
				if ((pstats.s_hpt -= damage) <= 0) 
				    death(reason);
			    }
			}
		    }
		    else
			msg("The %s whizzes by you", name);
		}

		mvwaddch(cw, pos.y, pos.x, dirch);
		draw(cw);
	}

	pos.y += dir.y;
	pos.x += dir.x;
    }

    /* Restore what was under the bolt */
    for (x = y - 1; x >= 0; x--)
	mvwaddch(cw, spotpos[x].place.y, spotpos[x].place.x, spotpos[x].oldch);

    /* If we killed something, do so now.  This will also blank the monster. */
    if (target) killed(target, FALSE, get_points, TRUE);
    return;
}