/*
 * Functions to implement the various sticks one might find
 * while wandering around the dungeon.
 *
 * @(#)sticks.c	3.14 (Berkeley) 6/15/81
 *
 * 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
fix_stick(struct object *cur)
{
    if (strcmp(ws_type[cur->o_which], "staff") == 0)
	strcpy(cur->o_damage,"2d3");
    else
	strcpy(cur->o_damage,"1d1");
    strcpy(cur->o_hurldmg,"1d1");

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

void
do_zap(int gotdir)
{
    struct linked_list *item;
    struct object *obj;
    struct room *rp;
    struct thing *tp;
    int y, x;

    if ((item = get_item("zap with", STICK)) == NULL)
	return;
    obj = (struct object *) ldata(item);
    if (obj->o_type != STICK)
    {
	msg("You can't zap with that!");
	after = FALSE;
	return;
    }
    if (obj->o_charges == 0)
    {
	msg("Nothing happens.");
	return;
    }
    if (!gotdir)
	do {
	    delta.y = rnd(3) - 1;
	    delta.x = rnd(3) - 1;
	} while (delta.y == 0 && delta.x == 0);
    switch (obj->o_which)
    {
	case WS_LIGHT:
	    /*
	     * Reddy Kilowat wand.  Light up the room
	     */
	    ws_know[WS_LIGHT] = TRUE;
	    if ((rp = roomin(&hero)) == NULL)
		msg("The corridor glows and then fades");
	    else
	    {
		addmsg("The room is lit");
		if (!terse)
		    addmsg(" by a shimmering blue light.");
		endmsg();
		rp->r_flags &= ~ISDARK;
		/*
		 * Light the room and put the player back up
		 */
		light(&hero);
		mvwaddch(cw, hero.y, hero.x, PLAYER);
	    }
	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)
	     */
	    if (pstats.s_hpt < 2)
	    {
		msg("You are too weak to use it.");
		return;
	    }
	    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);
	when WS_POLYMORPH:
	case WS_TELAWAY:
	case WS_TELTO:
	case WS_CANCEL:
	{
	    int monster;
	    int oldch;
	    int rm;

	    y = hero.y;
	    x = hero.x;
	    while (step_ok(winat(y, x)))
	    {
		y += delta.y;
		x += delta.x;
	    }
	    if (isupper(monster = mvwinch(mw, y, x)))
	    {
		int omonst = monster;

		if (monster == 'F')
		    player.t_flags &= ~ISHELD;
		item = find_mons(y, x);
		tp = (struct thing *) ldata(item);
		if (obj->o_which == WS_POLYMORPH)
		{
		    detach(mlist, item);
		    oldch = tp->t_oldch;
		    delta.y = y;
		    delta.x = x;
		    new_monster(item, monster = rnd(26) + 'A', &delta);
		    if (!(tp->t_flags & ISRUN))
			runto(&delta, &hero);
		    if (isupper(mvwinch(cw, y, x)))
			mvwaddch(cw, y, x, monster);
		    tp->t_oldch = oldch;
		    ws_know[WS_POLYMORPH] |= (monster != omonst);
		}
		else if (obj->o_which == WS_CANCEL)
		{
		    tp->t_flags |= ISCANC;
		    tp->t_flags &= ~ISINVIS;
		}
		else
		{
		    if (obj->o_which == WS_TELAWAY)
		    {
			do
			{
			    rm = rnd_room();
			    rnd_pos(&rooms[rm], &tp->t_pos);
			} until(winat(tp->t_pos.y, tp->t_pos.x) == FLOOR);
		    }
		    else
		    {
			tp->t_pos.y = hero.y + delta.y;
			tp->t_pos.x = hero.x + delta.x;
		    }
		    if (isupper(mvwinch(cw, y, x)))
			mvwaddch(cw, y, x, tp->t_oldch);
		    tp->t_dest = &hero;
		    tp->t_flags |= ISRUN;
		    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 = mvwinch(cw, tp->t_pos.y, tp->t_pos.x);
		}
	    }
	}
	when WS_MISSILE:
	{
	    static struct object bolt =
	    {
		'*' , {0, 0}, 0, "", "1d4" , 0, 0, 100, 1, 0, 0, 0
	    };

	    do_motion(&bolt, delta.y, delta.x);
	    if (isupper(mvwinch(mw, bolt.o_pos.y, bolt.o_pos.x))
		&& !save_throw(VS_MAGIC, THINGPTR(find_mons(unc(bolt.o_pos)))))
		    hit_monster(unc(bolt.o_pos), &bolt);
	    else if (terse)
		msg("Missile vanishes");
	    else
		msg("The missile vanishes with a puff of smoke");
	    ws_know[WS_MISSILE] = TRUE;
	}
	when WS_HIT:
	{
	    int ch;

	    delta.y += hero.y;
	    delta.x += hero.x;
	    ch = winat(delta.y, delta.x);
	    if (isupper(ch))
	    {
		if (rnd(20) == 0)
		{
		    strcpy(obj->o_damage,"3d8");
		    obj->o_dplus = 9;
		}
		else
		{
		    strcpy(obj->o_damage,"1d8");
		    obj->o_dplus = 3;
		}
		fight(&delta, ch, obj, FALSE);
	    }
	}
	when WS_HASTE_M:
	case WS_SLOW_M:
	    y = hero.y;
	    x = hero.x;
	    while (step_ok(winat(y, x)))
	    {
		y += delta.y;
		x += delta.x;
	    }
	    if (isupper(mvwinch(mw, y, x)))
	    {
		item = find_mons(y, x);
		tp = (struct thing *) ldata(item);
		if (obj->o_which == WS_HASTE_M)
		{
		    if (on(*tp, ISSLOW))
			tp->t_flags &= ~ISSLOW;
		    else
			tp->t_flags |= ISHASTE;
		}
		else
		{
		    if (on(*tp, ISHASTE))
			tp->t_flags &= ~ISHASTE;
		    else
			tp->t_flags |= ISSLOW;
		    tp->t_turn = TRUE;
		}
		delta.y = y;
		delta.x = x;
		runto(&delta, &hero);
	    }
	when WS_ELECT:
	case WS_FIRE:
	case WS_COLD:
	{
	    int dirch;
	    char *name;
	    int ch;
	    int bounced, used;
	    coord pos;
	    coord spotpos[BOLT_LENGTH];
	    static struct object bolt =
	    {
		'*' , {0, 0}, 0, "", "6d6" , 0, 0, 100, 0, 0, 0 ,0
	    };


	    switch (delta.y + delta.x)
	    {
		case 0: dirch = '/';
		when 1: case -1: dirch = (delta.y == 0 ? '-' : '|');
		when 2: case -2: dirch = '\\';
	    }
	    pos = hero;
	    bounced = FALSE;
	    used = FALSE;
	    if (obj->o_which == WS_ELECT)
		name = "bolt";
	    else if (obj->o_which == WS_FIRE)
		name = "flame";
	    else
		name = "ice";
	    for (y = 0; y < BOLT_LENGTH && !used; y++)
	    {
		ch = winat(pos.y, pos.x);
		spotpos[y] = pos;
		switch (ch)
		{
		    case DOOR:
		    case SECRETDOOR:
		    case '|':
		    case '-':
		    case ' ':
			bounced = TRUE;
			delta.y = -delta.y;
			delta.x = -delta.x;
			y--;
			msg("The bolt bounces");
			break;
		    default:
			if (!bounced && isupper(ch))
			{
			    if (!save_throw(VS_MAGIC, THINGPTR(find_mons(unc(pos)))))
			    {
				bolt.o_pos = pos;
				hit_monster(unc(pos), &bolt);
				used = TRUE;
			    }
			    else if (ch != 'M' || show(pos.y, pos.x) == 'M')
			    {
				if (terse)
				    msg("%s misses", name);
				else
				    msg("The %s whizzes past the %s", name, monsters[ch-'A'].m_name);
				runto(&pos, &hero);
			    }
			}
			else if (bounced && pos.y == hero.y && pos.x == hero.x)
			{
			    bounced = FALSE;
			    if (!save(VS_MAGIC))
			    {
				if (terse)
				    msg("The %s hits", name);
				else
				    msg("You are hit by the %s", name);
				if ((pstats.s_hpt -= roll(6, 6)) <= 0)
				    death('b');
				used = TRUE;
			    }
			    else
				msg("The %s whizzes by you", name);
			}
			mvwaddch(cw, pos.y, pos.x, dirch);
			draw(cw);
		}
		pos.y += delta.y;
		pos.x += delta.x;
	    }
	    for (x = 0; x < y; x++)
		mvwaddch(cw, spotpos[x].y, spotpos[x].x, show(spotpos[x].y, spotpos[x].x));
	    ws_know[obj->o_which] = TRUE;
	}
	when WS_NOP:
	otherwise:
	    msg("What a bizarre schtick!");
    }
    obj->o_charges--;
}

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

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

    /*
     * First count how many things we need to spread the hit points among
     */
    cnt = 0;
    for (i = ymin; i <= ymax; i++)
	for (j = xmin; j <= xmax; j++)
	    if (isupper(mvwinch(mw, i, j)))
		cnt++;
    if (cnt == 0)
    {
	msg("You have a tingling feeling");
	return;
    }
    cnt = pstats.s_hpt / cnt;
    pstats.s_hpt /= 2;
    /*
     * Now zot all of the monsters
     */
    for (i = ymin; i <= ymax; i++)
	for (j = xmin; j <= xmax; j++)
	    if (isupper(mvwinch(mw, i, j)) &&
	        ((item = find_mons(i, j)) != NULL))
	    {
		ick = (struct thing *) ldata(item);
		if ((ick->t_stats.s_hpt -= cnt) < 1)
		    killed(item, cansee(i, j) && !on(*ick, ISINVIS));
	    }
}

/*
 * charge a wand for wizards.
 */
char *
charge_str(struct object *obj)
{
    static char buf[20];

    if (!(obj->o_flags & ISKNOW))
	buf[0] = '\0';
    else if (terse)
	sprintf(buf, " [%d]", obj->o_charges);
    else
	sprintf(buf, " [%d charges]", obj->o_charges);
    return buf;
}
