/*
 * Functions to deal with the various sticks one
 * might find while wandering around the dungeon.
 *
 * @(#)sticks.c	9.0	(rdk)	 7/17/84
 *
 * Super-Rogue
 * Copyright (C) 1984 Robert D. Kindelberger
 * 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 <ctype.h>
#include <string.h>
#include "rogue.h"
#include "rogue.ext"

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

/*
 * fix_stick:
 *	Init a stick for the hero
 */
void
fix_stick(struct object *cur)
{
	struct rod *rd;

	cur->o_type = STICK;
	cur->o_charges = 4 + rnd(5);
	strcpy(cur->o_hurldmg, "1d1");
	rd = &ws_stuff[cur->o_which];
	cur->o_weight = rd->ws_wght;
	cur->o_vol = rd->ws_vol;
	if (strcmp(rd->ws_type, "staff") == 0) {
		strcpy(cur->o_damage, "2d3");
		cur->o_charges += rnd(5) + 3;
	}
	else {
		strcpy(cur->o_damage, "1d1");
	}
	switch (cur->o_which) {
		case WS_HIT:
			if(rnd(100) < 15) {
				cur->o_hplus = 9;
				cur->o_dplus = 9;
				strcpy(cur->o_damage,"3d8");
			}
			else {
				cur->o_hplus = 3;
				cur->o_dplus = 3;
				strcpy(cur->o_damage,"1d8");
			}
		when WS_LIGHT:
			cur->o_charges += 7 + rnd(9);
	}
}

/*
 * do_zap:
 *	Zap a stick at something
 */
void
do_zap(bool gotdir)
{
	reg struct linked_list *item;
	reg struct object *obj;
	reg struct thing *tp;
	reg int y, x, wh;
	struct room *rp;
	bool bless, curse;
	int better = 0;

	if ((item = get_item("zap with", STICK)) == NULL)
		return;
	obj = OBJPTR(item);
	wh = obj->o_which;
	bless = o_on(obj, ISBLESS);
	curse = o_on(obj, ISCURSED);
	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);
	rp = player.t_room;
	if (bless)
		better = 3;
	else if (curse)
		better = -3;
	switch (wh) {
	case WS_SAPLIFE:
		if (!bless) {
			if (him->s_hpt > 1)
			him->s_hpt /= 2;	/* zap half his hit points */
		}
	when WS_CURE:
		if (!curse) {
			ws_know[WS_CURE] = TRUE;
			heal_self(6, FALSE);
			unconfuse(FALSE);
			notslow(FALSE);
			sight(FALSE);
		}
	when WS_PYRO:
		if (!bless) {
			msg("The rod explodes !!!");
			chg_hpt(-roll(6,6), FALSE, K_ROD);
			ws_know[WS_PYRO] = TRUE;
			del_pack(item);		/* throw it away */
		}
	when WS_HUNGER:
		if (!bless) {
			struct linked_list *ip;
			struct object *lb;

			food_left /= 3;
			if ((ip = pack) != NULL) {
				lb = OBJPTR(ip);
				if (lb->o_type == FOOD) {
					if ((lb->o_count -= roll(1,4)) < 1)
						del_pack(ip);
				}
			}
		}
	when WS_PARZ:
	case WS_MREG:
	case WS_MDEG:
	case WS_ANNIH: {
		struct linked_list *mitem;
		struct thing *it;
		reg int i,j;

		for (i = hero.y - 3; i <= hero.y + 3; i++) {
			for (j = hero.x - 3; j <= hero.x + 3; j++) {
				if (!cordok(i, j))
					continue;
				if (isalpha(mvwinch(mw,i,j))) {
					mitem = find_mons(i, j);
					if (mitem == NULL)
						continue;
					it = THINGPTR(mitem);
					switch(wh) {
						case WS_ANNIH:
							if (!curse)
								killed(mitem,FALSE);
						when WS_MREG:
							if (!bless)
								it->t_stats.s_hpt *= 2;
						when WS_MDEG:
							if (!curse) {
								it->t_stats.s_hpt /= 2;
								if (it->t_stats.s_hpt < 2)
									killed(mitem,FALSE);
							}
						when WS_PARZ:
							if (!curse) {
								it->t_flags |= ISPARA;
								it->t_flags &= ~ISRUN;
							}
						}
					}
				}
			}
		}
	when WS_LIGHT:
		if (!curse) {
			ws_know[WS_LIGHT] = TRUE;
			if (rp == NULL)
				msg("The corridor glows and then fades.");
			else {
				msg("The room is lit.");
				rp->r_flags &= ~ISDARK;
				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 (him->s_hpt < 2) {
			msg("You are too weak to use it.");
			return;
		}
		else if (!curse) {
			if (rp == 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_POLYM:
	case WS_TELAWAY:
	case WS_TELTO:
	case WS_CANCEL:
	case WS_MINVIS:
	{
		reg char monster, oldch;

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

			if (wh != WS_MINVIS)
				unhold(monster);
			item = find_mons(y, x);
			if (item == NULL)
				break;
			tp = THINGPTR(item);
			omonst = tp->t_indx;
			if (wh == WS_POLYM && !curse) {
				detach(mlist, item);
				discard(item);
				oldch = tp->t_oldch;
				delta.y = y;
				delta.x = x;
				monster = rnd_mon(FALSE, TRUE);
				item = new_monster(monster, &delta, FALSE);
				if (!(tp->t_flags & ISRUN))
					runto(&delta, &hero);
				if (isalpha(mvwinch(cw, y, x)))
					mvwaddch(cw, y, x, monsters[monster].m_show);
				tp->t_oldch = oldch;
				ws_know[WS_POLYM] |= (monster != omonst);
			}
			else if (wh == WS_MINVIS && !bless) {
				tp->t_flags |= ISINVIS;
				mvwaddch(cw,y,x,tp->t_oldch);	/* hide em */
				runto(&tp->t_pos, &hero);
			}
			else if (wh == WS_CANCEL && !curse) {
				tp->t_flags |= ISCANC;
				tp->t_flags &= ~ISINVIS;
			}
			else {
				if (wh == WS_TELAWAY) {
					if (curse)
						break;
					tp->t_pos = *rnd_pos(&rooms[rnd_room()]);
				}
				else {					/* WS_TELTO */
					if (bless)
						break;
					tp->t_pos.y = hero.y + delta.y;
					tp->t_pos.x = hero.x + delta.x;
				}
				if (isalpha(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);
				tp->t_oldch = mvwinch(cw,tp->t_pos.y,tp->t_pos.x);
			}
		}
	}
	when WS_MISSILE:
	{
		struct coord *whe;
		static struct object bolt = {
			{0, 0}, "", "6d6", "", '*', 0, 0, 1000, 0, 0, 0, 0, 0, 0,
		};

		if (curse)
			strcpy(bolt.o_hurldmg,"3d3");
		else if (bless)
			strcpy(bolt.o_hurldmg,"9d9");
		ws_know[WS_MISSILE] = TRUE;
		do_motion(&bolt, delta.y, delta.x);
		whe = &bolt.o_pos;
		if (isalpha(mvwinch(mw, whe->y, whe->x))) {
			struct linked_list *it;

			runto(whe, &hero);
			it = find_mons(whe->y, whe->x);
			if (it != NULL) {
				if (!save_throw(VS_MAGIC + better, THINGPTR(it))) {
					hit_monster(whe, &bolt);
					break;
				}
			}
		}
		msg("Missle vanishes.");
	}
	when WS_NOP:
		msg("Your %s flickers momentarily and then fades",
			ws_stuff[wh].ws_type);
	when WS_HIT: {
		char ch;

		delta.y += hero.y;
		delta.x += hero.x;
		ch = winat(delta.y, delta.x);
		if (curse) {				/* decrease for cursed */
			strcpy(obj->o_damage,"1d1");
			obj->o_hplus = obj->o_dplus = 0;
		}
		else if (bless) {			/* increase for blessed */
			strcpy(obj->o_damage,"5d8");
			obj->o_hplus = obj->o_dplus = 12;
		}
		if (isalpha(ch))
			fight(&delta, obj, FALSE);
	}
	when WS_HASTE_M:
	case WS_CONFMON:
	case WS_SLOW_M:
	case WS_MOREMON: {
		reg int m1,m2;
		struct coord mp;
		struct linked_list *titem;

		y = hero.y;
		x = hero.x;
		do {
			y += delta.y;
			x += delta.x;
		} while (step_ok(winat(y, x)));
		if (isalpha(mvwinch(mw, y, x))) {
			item = find_mons(y, x);
			if (item == NULL)
				break;
			tp = THINGPTR(item);
			if (wh == WS_HASTE_M && !bless) {			/* haste it */
				if (on(*tp, ISSLOW))
					tp->t_flags &= ~ISSLOW;
				else
					tp->t_flags |= ISHASTE;
			}
			else if (wh == WS_CONFMON && !curse) {		/* confuse it */
				tp->t_flags |= ISHUH;
				if (pl_on(ISHELD) && tp->t_type == 'd')
					player.t_flags &= ~ISHELD;
			}
			else if (wh == WS_SLOW_M && !curse) {		/* slow it */
				if (on(*tp, ISHASTE))
					tp->t_flags &= ~ISHASTE;
				else
					tp->t_flags |= ISSLOW;
				tp->t_turn = TRUE;
			}
			else if (!bless) {	/* WS_MOREMON: multiply it */
				char ch;
				struct thing *th;

				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 (hero.x == m1 && hero.y == m2)
							continue;
						ch = winat(m2,m1);
						if (step_ok(ch)) {
							mp.x = m1;			/* create it */
							mp.y = m2;
							titem = new_monster(tp->t_indx, &mp, FALSE);
							th = THINGPTR(titem);
							th->t_flags |= ISMEAN;
							runto(&mp, &hero);
						}
					}
				}
			}
			delta.y = y;
			delta.x = x;
			runto(&delta, &hero);
		}
	}
	when WS_ELECT:
	case WS_FIRE:
	case WS_COLD: {
		reg char dirch, ch, *name;
		reg bool bounced, used;
		int boingcnt, boltlen;
		struct coord pos;
		struct coord spotpos[BOLT_LENGTH * 2];
		static struct object bolt =	{
			{0, 0}, "", "6d6", "", '*', 0, 0, 1000, 0, 0, 0, 0, 0, 0,
		};

		boltlen = BOLT_LENGTH;
		if (curse) {
			strcpy(bolt.o_hurldmg,"3d3");
			boltlen -= 3;
		}
		else if (bless) {
			strcpy(bolt.o_hurldmg,"9d9");
			boltlen += 3;
		}
		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;
		boingcnt = 0;
		used = FALSE;
		if (wh == WS_ELECT)
			name = "bolt";
		else if (wh == WS_FIRE)
			name = "flame";
		else
			name = "ice";
		for (y = 0; y < boltlen && !used; y++) {
			ch = winat(pos.y, pos.x);
			spotpos[y] = pos;
			switch (ch) {
				case SECRETDOOR:
				case '|':
				case '-':
				case ' ':
					bounced = TRUE;
					if (++boingcnt > 6) 
						used = TRUE;	/* only so many bounces */
					delta.y = -delta.y;
					delta.x = -delta.x;
					y--;
					msg("The bolt bounces");
					break;
				default:
					if (isalpha(ch)) {
						struct linked_list *it;

						it = find_mons(pos.y, pos.x);
						runto(&pos, &hero);
						if (it != NULL) {
							if (!save_throw(VS_MAGIC+better,THINGPTR(it))) {
								bolt.o_pos = pos;
								hit_monster(&pos, &bolt);
								used = TRUE;
							}
							else if(ch != 'M' || show(pos.y,pos.x)=='M') {
								msg("%s misses", name);
							}
						}
					}
					else if(bounced && pos.y==hero.y && pos.x==hero.x) {
						bounced = FALSE;
						if (!save(VS_MAGIC + better)) {
							msg("The %s hits you.", name);
							chg_hpt(-roll(6, 6),FALSE,K_BOLT);
							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[wh] = TRUE;
		}
	when WS_ANTIM: {
		reg int m1, m2, x1, y1;
		struct linked_list *ll;
		struct thing *lt;
		int ch, radius;

		y1 = hero.y;
		x1 = hero.x;
		do {
			y1 += delta.y;
			x1 += delta.x;
			ch = winat(y1, x1);
		} while (ch == PASSAGE || ch == FLOOR);
		if (curse)
			radius = 2;
		else if (bless)
			radius = 0;
		else
			radius = 1;
		for (m1 = x1 - radius; m1 <= x1 + radius; m1++) {
			for (m2 = y1 - radius; m2 <= y1 + radius; m2++) {
				if (!cordok(m2, m1))
					continue;
				ch = winat(m2, m1);
				if (m1 == hero.x && m2 == hero.y)
					continue;
				if (ch == ' ')
					continue;
				ll = find_obj(m2,m1);
				if (ll != NULL) {
					detach(lvl_obj,ll);
					discard(ll);
				}
				ll = find_mons(m2,m1);
				if (ll != NULL) {
					lt = THINGPTR(ll);
					him->s_exp += lt->t_stats.s_exp;
					unhold(lt->t_type);
					/*
					 * throw away anything that the monster
					 * was carrying in its pack
					 */
					free_list(lt->t_pack);
					detach(mlist,ll);
					discard(ll);
					mvwaddch(mw,m2,m1,' ');
				}
				mvaddch(m2,m1,' ');
				mvwaddch(cw,m2,m1,' ');
			}
		}
		touchwin(cw);
		touchwin(mw);
		check_level();
	}
	otherwise:
		msg("What a bizarre schtick!");
	}
	obj->o_charges--;
}

/*
 * drain:
 *	Do drain hit points from player stick
 */
void
drain(int ymin, int ymax, int xmin, int xmax)
{
	reg int i, j, cnt;
	reg struct thing *ick;
	reg 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 (isalpha(mvwinch(mw, i, j)))
				cnt++;
	if (cnt == 0) {
		msg("You have a tingling feeling.");
		return;
	}
	cnt = him->s_hpt / cnt;
	him->s_hpt /= 2;
	/*
	 * Now zot all of the monsters
	 */
	for (i = ymin; i <= ymax; i++) {
		for (j = xmin; j <= xmax; j++) {
			if(isalpha(mvwinch(mw, i, j))) {
				item = find_mons(i, j);
				if (item == NULL)
					continue;
				ick = THINGPTR(item);
				if ((ick->t_stats.s_hpt -= cnt) < 1)
					killed(item,cansee(i,j) && !(ick->t_flags & ISINVIS));
			}
		}
	}
}

/*
 * charge_str:
 *	Return number of charges left in a stick
 */
char *
charge_str(struct object *obj)
{
	static char buf[20];

	buf[0] = '\0';
	if (o_on(obj,ISKNOW) || o_on(obj,ISPOST))
		sprintf(buf, " [%d]", obj->o_charges);
	return buf;
}
