view arogue5/sticks.c @ 264:778938a5c21d

UltraRogue: add location for character files. When using the -n option, UltraRogue will look for character files in a single location, similar to save files. The location is chosen by defining CHRDIR in getplay.c, at least until UltraRogue gets integrated with the build systems.
author John "Elwin" Edwards
date Sun, 19 Feb 2017 19:47:09 -0500
parents 56e748983fa8
children e52a8a7ad4c5
line wrap: on
line source

/*
 * Functions to implement the various sticks one might find
 * while wandering around the dungeon.
 *
 * Advanced Rogue
 * Copyright (C) 1984, 1985 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.
 */

#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
 */
bool
do_zap(bool gotdir, int which, int flag)
{
    register struct linked_list *item;
    register struct object *obj = NULL;
    register struct thing *tp;
    register int y, x;
    struct linked_list *nitem;
    struct object *nobj;
    bool cursed, blessed, is_stick;

    blessed = FALSE;
    cursed = FALSE;
    is_stick = FALSE;

    if (which == 0) {
	if ((item = get_item(pack, "zap with", ZAPPABLE)) == NULL)
	    return(FALSE);
	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 ASMO_ROD:
		    switch (rnd(3)) {
			case 0:
			    which = WS_ELECT;
			when 1:
			    which = WS_COLD;
			otherwise:
			    which = WS_FIRE;
		    }
	    }
	    cursed = FALSE;
	    blessed = FALSE;
	}
	else {
	    which = obj->o_which;
	    ws_know[which] = TRUE;
	    cursed = (obj->o_flags & ISCURSED) != 0;
	    blessed = (obj->o_flags & ISBLESSED) != 0;
	    is_stick = TRUE;
	}
    }
    else {
	cursed = flag & ISCURSED;
	blessed = flag & ISBLESSED;
    }
    switch (which) {	/* no direction for these */
	case WS_LIGHT:
	case WS_DRAIN:
	case WS_CHARGE:
	case WS_CURING:
	    break;

	default:
	    if (!get_dir())
		return(FALSE);
	    if (!gotdir) {
		do {
		    delta.y = rnd(3) - 1;
		    delta.x = rnd(3) - 1;
		} while (delta.y == 0 && delta.x == 0);
	    }
    }

    if (is_stick) {
	if (obj->o_charges < 1) {
	    msg(nothing);
	    return(TRUE);
	}
	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;
	}
    }

    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.");
		return(TRUE);
	    }
	    if (cursed)
		pstats.s_hpt /= 2;
	    else
		drain(hero.y-1, hero.y+1, hero.x-1, hero.x+1);
	when WS_POLYMORPH:
	case WS_TELMON:
	case WS_CANCEL:
	{
	    register char monster, oldch;
	    register int rm;

	    y = hero.y;
	    x = hero.x;
	    while (shoot_ok(winat(y, x))) {
		y += delta.y;
		x += delta.x;
	    }
	    if (isalpha(monster = CCHAR( mvwinch(mw, y, x) ))) {
		register struct room *rp;

		item = find_mons(y, x);
		tp = THINGPTR(item);
		/* if the monster gets the saving throw, leave the case */
		if (save(VS_MAGIC, tp, 0)) {
		    msg(nothing);
		    break;
		}

		/* 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(suffocate);
		}
		rp = roomin(&tp->t_pos);
		/*
		 * check to see if room should go dark
		 */
		if (on(*tp, HASFIRE)) {
		    if (rp != NULL) {
			register struct linked_list *fire_item;

			for (fire_item = rp->r_fires; fire_item != NULL;
			     fire_item = next(fire_item)) {
			    if (THINGPTR(fire_item) == tp) {
				detach(rp->r_fires, fire_item);
				destroy_item(fire_item);
				if (rp->r_fires == NULL) {
				    rp->r_flags &= ~HASFIRE;
				    if(cansee(tp->t_pos.y,tp->t_pos.x))
					light(&hero);
				}
				break;
			    }
			}
		    }
		}

		if (which == WS_POLYMORPH) {
		    register struct linked_list *pitem;

		    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;
		    monster = tp->t_type;
		    if (isalpha(mvwinch(cw, y, x)))
			mvwaddch(cw, y, x, monster);
		    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);
			}
		    }
		    msg(terse ? "A new %s!" : "You have created a new %s!",
			monsters[tp->t_index].m_name);
		}
		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[4] &= CANC5MASK;
		}
		else { /* A teleport stick */
		    if (cursed) {	/* Teleport monster to player */
			if ((y == (hero.y + delta.y)) &&
			    (x == (hero.x + delta.x)))
				msg(nothing);
			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(item, FALSE, TRUE);
			return(TRUE);
		    }
		    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);
		    turn_off(*tp, ISDISGUISE);
		    mvwaddch(mw, y, x, ' ');
		    mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, monster);
		    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)) {
			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);
	    }
	}
	when WS_MISSILE:
	{
	    static struct object bolt =
	    {
		MISSILE , {0, 0}, "", 0, "", "1d4 " , NULL, 0, WS_MISSILE, 50, 1
	    };

	    sprintf(bolt.o_hurldmg, "%dd4", pstats.s_lvl);
	    do_motion(&bolt, delta.y, delta.x, &player);
	    if (!hit_monster(unc(bolt.o_pos), &bolt, &player))
	       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 */

	    delta.y += hero.y;
	    delta.x += hero.x;
	    ch = CCHAR( winat(delta.y, delta.x) );
	    if (isalpha(ch))
	    {
		strike = *obj;
		strike.o_hplus  = 6;
		if (EQUAL(ws_type[which], "staff"))
		    strcpy(strike.o_damage,"3d8");
		else
		    strcpy(strike.o_damage,"2d8");
		fight(&delta, &strike, FALSE);
	    }
	}
	case WS_SLOW_M:
	    y = hero.y;
	    x = hero.x;
	    while (shoot_ok(winat(y, x))) {
		y += delta.y;
		x += delta.x;
	    }
	    if (isalpha(mvwinch(mw, y, x))) {
		item = find_mons(y, x);
		tp = THINGPTR(item);
		runto(tp, &hero);
		if (on(*tp, ISUNIQUE) && save(VS_MAGIC, tp, 0))
		    msg(nothing);
		else if (on(*tp, NOSLOW))
		    msg(nothing);
		else if (cursed) {
		    if (on(*tp, ISSLOW))
			turn_off(*tp, ISSLOW);
		    else
			turn_on(*tp, ISHASTE);
		}
		else if (blessed) {
		    turn_off(*tp, ISRUN);
		    turn_on(*tp, ISHELD);
		    return(TRUE);
		}
		else {
		    if (on(*tp, ISHASTE))
			turn_off(*tp, ISHASTE);
		    else
			turn_on(*tp, ISSLOW);
		    tp->t_turn = TRUE;
		}
	    }
	when WS_CHARGE:
	    if (ws_know[WS_CHARGE] != TRUE && is_stick)
		msg("This is a wand of charging.");
	    if ((nitem = get_item(pack, "charge", STICK)) != NULL) {
		nobj = OBJPTR(nitem);
	        if ((++(nobj->o_charges) == 1) && (nobj->o_which == WS_HIT))
		    fix_stick(nobj);
	        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:
	case WS_FIRE:
	case WS_COLD:
	    {
		char *name;

		if (which == WS_ELECT)
		    name = "lightning bolt";
		else if (which == WS_FIRE)
		    name = "flame";
		else
		    name = "ice";

		shoot_bolt(	&player, hero, 
				delta, TRUE, D_BOLT, 
				name, roll(pstats.s_lvl,6));
	    }
	when WS_PETRIFY: {
	    reg int m1, m2, x1, y1;
	    reg char ch;
	    reg struct linked_list *ll;
	    reg struct thing *lt;

	    y1 = hero.y;
	    x1 = hero.x;
	    do {
		y1 += delta.y;
		x1 += delta.x;
		ch = CCHAR( 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 = CCHAR( winat(m2,m1) );
		    if (m1 == hero.x && m2 == hero.y)
			continue;
		    if (ch != ' ') {
			ll = find_obj(m2,m1);
			if (ll != NULL) {
			    detach(lvl_obj,ll);
			    o_discard(ll);
			}
			ll = find_mons(m2,m1);
			if (ll != NULL) {
			    lt = THINGPTR(ll);
			    if (on(*lt, ISUNIQUE)) 
			        monsters[lt->t_index].m_normal = TRUE;
			    check_residue(lt);
			    detach(mlist,ll);
			    t_discard(ll);
			    mvwaddch(mw,m2,m1,' ');
			}
			mvaddch(m2,m1,' ');
			mvwaddch(cw,m2,m1,' ');
		    }
		}
	    }
	    touchwin(cw);
	    touchwin(mw);
	}
	when WS_CONFMON:
	    if (cursed) {
		if (off(player, ISCLEAR)) {
		    if (on(player, ISHUH))
			lengthen(unconfuse, rnd(20)+HUHDURATION);
		    else {
			turn_on(player, ISHUH);
			fuse(unconfuse,0,rnd(20)+HUHDURATION,AFTER);
			msg("Wait, what's going on here. Huh? What? Who?");
		    }
		}
		else msg("You feel dizzy for a moment, but it quickly passes.");
	    }
	    else {
		y = hero.y;
		x = hero.x;
		while (shoot_ok(winat(y, x)))
		{
		    y += delta.y;
		    x += delta.x;
		}
		if (isalpha(mvwinch(mw, y, x)))
		{
		    item = find_mons(y, x);
		    tp = THINGPTR(item);
		    if (save(VS_MAGIC, tp, 0) || on(*tp, ISCLEAR))
			 msg(nothing);
		    else
			 turn_on (*tp, ISHUH);
		    runto(tp, &hero);
		}
	    }
	when WS_PARALYZE:
	    if (cursed) {
		no_command += FREEZETIME;
		msg("You can't move.");
	    }
	    else {
		y = hero.y;
		x = hero.x;
		while (shoot_ok(winat(y, x)))
		{
		    y += delta.y;
		    x += delta.x;
		}
		if (isalpha(mvwinch(mw, y, x)))
		{
		    item = find_mons(y, x);
		    tp = THINGPTR(item);
		    if (save(VS_WAND, tp, 0) || on(*tp, NOPARALYZE))
			msg(nothing);
		    else {
			tp->t_no_move = FREEZETIME;
		    }
		    runto(tp, &hero);
		}
	    }
	    when WS_FEAR:
		y = hero.y;
		x = hero.x;
		while (shoot_ok(winat(y, x)))
		{
		    y += delta.y;
		    x += delta.x;
		}
		if (isalpha(mvwinch(mw, y, x)))
		{
		    item = find_mons(y, x);
		    tp = THINGPTR(item);
		    runto(tp, &hero);
		    if (save(VS_WAND, tp, 0) || 
			on(*tp, ISUNDEAD)    || 
			on(*tp, NOFEAR))
			    msg(nothing);
		    else {
			turn_on(*tp, ISFLEE);
			turn_on(*tp, WASTURNED);

			/* 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);
		    }
		}
	when WS_MDEG:
	    y = hero.y;
	    x = hero.x;
	    while (shoot_ok(winat(y, x)))
	    {
		y += delta.y;
		x += delta.x;
	    }
	    if (isalpha(mvwinch(mw, y, x)))
	    {
		item = find_mons(y, x);
		tp = THINGPTR(item);
		if (cursed) {
		     tp->t_stats.s_hpt *= 2;
		     msg("The %s appears to be stronger now!", 
			monsters[tp->t_index].m_name);
		}
		else if (on(*tp, ISUNIQUE) && save(VS_WAND, tp, 0))
		     msg (nothing);
		else {
		     tp->t_stats.s_hpt /= 2;
		     msg("The %s appears to be weaker now", 
			monsters[tp->t_index].m_name);
		}
		runto(tp, &hero);
	        if (tp->t_stats.s_hpt < 1)
		     killed(item, TRUE, TRUE);
	    }
	when WS_DISINTEGRATE:
	    y = hero.y;
	    x = hero.x;
	    while (shoot_ok(winat(y, x))) {
		y += delta.y;
		x += delta.x;
	    }
	    if (isalpha(mvwinch(mw, y, x))) {
		item = find_mons(y, x);
		tp = THINGPTR(item);
		turn_on (*tp, ISMEAN);
		runto(tp, &hero);
		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++) {
			    ch = CCHAR( 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,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 */
		    tp = THINGPTR(item);
		    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);
			     msg("You have disintegrated the %s", 
				    monsters[tp->t_index].m_name);
			}
			else {
			    msg("The %s appears wounded",
				monsters[tp->t_index].m_name);
			}
		    }
		    else {
			msg("You have disintegrated the %s", 
				monsters[tp->t_index].m_name);
			killed (item, FALSE, TRUE);
		    }
		}
	    }
	when WS_CURING:
	    ws_know[WS_CURING] = TRUE;
	    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, 0, roll(HEALTIME,SICKTIME), AFTER);
		    infest_dam++;
		}
		else msg("You fell momentarily sick");
	    }
	    else {
		if (on(player, HASDISEASE)) {
		    extinguish(cure_disease);
		    cure_disease();
		    msg(terse ? "You feel yourself improving."
			      : "You begin to feel yourself improving again.");
		}
		if (on(player, HASINFEST)) {
		    turn_off(player, HASINFEST);
		    infest_dam = 0;
		    msg(terse ? "You feel yourself improving."
			      : "You begin to feel yourself improving again.");
		}
		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!");
    }
    return(TRUE);
}


/*
 * 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);
		else {
		    runto(ick, &hero);
		    if (cansee(i,j) && (!on(*ick,ISINVIS)||on(player,CANSEE)))
			    msg("The %s appears wounded",
				monsters[ick->t_index].m_name);
		}
	    }
	}
    }
}

/*
 * 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);
	strcpy(cur->o_damage,"2d3");
	cur->o_hplus = 1;
	cur->o_dplus = 0;
	switch (cur->o_which) {
	    case WS_HIT:
		cur->o_hplus = 3;
		cur->o_dplus = 3;
		strcpy(cur->o_damage,"2d8");
	    when WS_LIGHT:
		cur->o_charges = 20 + rnd(10);
	    }
    }
    else {
	strcpy(cur->o_damage,"1d3");
	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;
		strcpy(cur->o_damage,"1d8");
	    when WS_LIGHT:
		cur->o_charges = 10 + rnd(10);
	    }
    }
    strcpy(cur->o_hurldmg,"1d1");

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

bool
shoot_bolt(struct thing *shooter, coord start, coord dir, bool get_points, 
           short reason, char *name, int damage)
{
    register char dirch = 0, ch;
    register bool used, change;
    register short y, x, bounces;
    bool mdead = FALSE;
    coord pos;
    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 const char *mname;
		    bool see_monster = cansee(pos.y, pos.x);

		    item = find_mons(unc(pos));
		    tp = THINGPTR(item);
		    mname = monsters[tp->t_index].m_name;

		    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);
			    }

			    sprintf(outstring,"The %s hits the %s", name, mname);
			    msg(outstring);
			}

			tp->t_wasshot = TRUE;
			runto(tp, &hero);
			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){
				sprintf(outstring,"The %s has no effect on the %s.",
					name, mname);
				msg(outstring);
			    }
			}

			/* 
			 * Check for gas with special effects 
			 */
			else if (EQUAL(name, "nerve gas")) {
			    tp->t_no_move = FREEZETIME;
			}
			else if (EQUAL(name, "sleeping gas")) {
			    tp->t_no_move = SLEEPTIME;
			}
			else if (EQUAL(name, "slow gas")) {
			    if (on(*tp, ISHASTE))
				turn_off(*tp, ISHASTE);
			    else
				turn_on(*tp, ISSLOW);
			    tp->t_turn = TRUE;
			}
			else if (EQUAL(name, "fear gas")) {
			    turn_on(*tp, ISFLEE);
			    tp->t_dest = &hero;
			}
			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){
				      sprintf(outstring,"The %s divides the %s.",name,mname);
				      msg(outstring);
				  }
				  light(&hero);
				}
				else if (see_monster){
				    sprintf(outstring,"The %s has no effect on the %s.",
					name, mname);
				    msg(outstring);
				}
			}
			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){
				    sprintf(outstring,"The %s kills the %s", name, mname);
				    msg(outstring);
				}
				else
				 msg("You hear a faint groan in the distance");
				killed(item, FALSE, get_points);

				/* Replace the screen character */
				spotpos[y].oldch = CCHAR( mvwinch(cw, pos.y, pos.x) );

				mdead = TRUE;
			    }
			    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 {
				sprintf(outstring,"The %s whizzes past the %s", name, mname);
				msg(outstring);
			    }
			}
			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,"fear gas")&&ISWEARING(R_HEROISM))){
			     msg("The %s has no affect", name);
			}
			/* 
			 * Check for gas with special effects 
			 */
			else if (EQUAL(name, "nerve gas")) {
			    msg("The nerve gas paralyzes you.");
			    no_command += FREEZETIME;
			}
			else if (EQUAL(name, "sleeping gas")) {
			    msg("The sleeping gas puts you to sleep.");
			    no_command += SLEEPTIME;
			}
			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,0,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(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;
    }
    for (x = y - 1; x >= 0; x--)
	mvwaddch(cw, spotpos[x].place.y, spotpos[x].place.x, spotpos[x].oldch);
    return(mdead);
}