view arogue5/sticks.c @ 155:1af259ac4ed2

arogue7, xrogue: fix save/restore of alchemy jugs. state.c now saves the fuse that refills alchemy jugs, using the method of numbering objects previously applied to Advanced Rogue 5. Alchemy jugs that are empty when the game is saved will continue to function after it is restored. Savefile compatibility should not be affected.
author John "Elwin" Edwards
date Fri, 29 May 2015 17:34:04 -0400
parents c49f7927b0fa
children 56e748983fa8
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"


/*
 * zap a stick and see what happens
 */
do_zap(gotdir, which, flag)
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
 */

drain(ymin, ymax, xmin, xmax)
int ymin, ymax, xmin, 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
 */
fix_stick(cur)
register 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
 */

shoot_bolt(shooter, start, dir, get_points, reason, name, damage)
struct thing *shooter;
coord start, 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);