#ifndef lint
static char *RCSid = "$Header: /usr/brule/guest/empire/empire/emprcs/lib/commands/sona.c,v 2.5 1995/10/24 04:39:16 empire Exp $";
#endif

/*
 * sona.c
 *
 * sonar from a sub (or other sonar-equipped ship)
 *
 * Jim Griffith, 1989
 */

#include "misc.h"
#include "player.h"
#include "var.h"
#include "xy.h"
#include "sect.h"
#include "nsc.h"
#include "retreat.h"
#include "ship.h"
#include "nat.h"
#include "path.h"
#include "file.h"
#include "queue.h"
#include "plane.h"
#include <fcntl.h>
#include <ctype.h>

sona()
{
	register int i;
	struct	nstr_item ni, nit;
	struct	sctstr sect;
	struct	shpstr ship;
	struct	shpstr targ;
	struct	natstr *natp;
	struct	mchrstr *mcp;
	struct	mchrstr *tmcp;
	struct	nstr_sect ns;
	int	range;
	int	pingrange;
	int	srange;
	int	vrange;
	int	dist;
	s_char	rad[WORLD_Y][WORLD_X+1];
	s_char	vis[WORLD_Y][WORLD_X+1];
	int	x,y;
	int	cx,cy;
	int	mines;
	int	changed = 0;
	int	row;

	if (!snxtitem(&ni, EF_SHIP, player->argp[1]))
		return RET_SYN;
	while (nxtitem(&ni, (s_char *)&ship)) {
		if (!player->owner)
			continue;
		mcp = &mchr[ship.shp_type];
		if (!(mcp->m_flags & M_SONAR))
			continue;
		getsect(ship.shp_x, ship.shp_y, &sect);
		if (sect.sct_type != SCT_WATER)
			continue;
		range = (int) techfact(ship.shp_tech,
			(double) mcp->m_vrnge);
		srange = min(7, 7 * range * ship.shp_effic / 200);
		pr("%s at %s efficiency %d%%, max range %d\n",
		   prship(&ship),
		   xyas(ship.shp_x, ship.shp_y, player->cnum),
		   ship.shp_effic,
		   srange);
		snxtsct_dist(&ns,ship.shp_x,ship.shp_y,srange);
		blankfill((s_char *)rad, &ns.range, 1);
		while(nxtsct(&ns,&sect)){
			if (player->owner || sect.sct_type == SCT_WATER)
				rad[ns.dy][ns.dx] = dchr[sect.sct_type].d_mnem;
			else
				rad[ns.dy][ns.dx] = '?';
		}
		snxtsct_dist(&ns,ship.shp_x,ship.shp_y,srange);
		cx = deltax(ship.shp_x, ns.range.lx);
		cy = deltay(ship.shp_y, ns.range.ly);
		while(nxtsct(&ns,&sect)) {
			if (!line_of_sight(rad, cx, cy, ns.dx, ns.dy)) {
				rad[ns.dy][ns.dx] = ' ';
				continue;
			}
			if (ship.shp_tech >= 310 &&
			    sect.sct_type == SCT_WATER) {
				mines=getvar(V_MINE,(s_char *)&sect,EF_SECTOR);
				if (mines) {
					pr("Sonar detects %d mines in %s!\n",
					   mines,
					   xyas(sect.sct_x,sect.sct_y,player->cnum));
					rad[ns.dy][ns.dx] = 'X';
				}
			}
			changed |= map_set(player->cnum, sect.sct_x, sect.sct_y,
					  rad[ns.dy][ns.dx], 0);

		}
		bzero((s_char *)vis, sizeof(vis));
		snxtitem_dist(&nit, EF_SHIP, ship.shp_x, ship.shp_y, range);
		while (nxtitem(&nit, (caddr_t)&targ)) {

			if (targ.shp_own == player->cnum || targ.shp_own == 0)
				continue;
			tmcp = &mchr[targ.shp_type];
			pingrange = min(7, max(tmcp->m_visib,10)*range/10);
			vrange = pingrange * ship.shp_effic / 200;
			dist = mapdist(targ.shp_x, targ.shp_y,
				ship.shp_x, ship.shp_y);
			pingrange = (max(pingrange, 2) * targ.shp_effic)/100; 
			if (dist > pingrange)
				continue;
			if (tmcp->m_flags & M_SONAR && targ.shp_own) {
				natp = getnatp(targ.shp_own);
				if (natp->nat_flags & NF_SONAR)
					wu(0, targ.shp_own,
					   "Sonar ping from %s detected by %s!\n",
					   
					   xyas(ship.shp_x, ship.shp_y,
						targ.shp_own),
					   prship(&targ));
				if (targ.shp_rflags & RET_SONARED){
					retreat_ship(&targ, 's');
					putship(targ.shp_uid,&targ);
				}
			}
			if (dist > vrange)
				continue;
			x = deltx(&ns.range,(int)targ.shp_x);
			y = delty(&ns.range,(int)targ.shp_y);
			if (rad[y][x] != dchr[SCT_WATER].d_mnem &&
			    rad[y][x] != 'X')
				continue;
			if (tmcp->m_flags & M_SUB &&
			    getrel(getnatp(targ.shp_own), player->cnum) < FRIENDLY) {
				if (mcp->m_vrnge + tmcp->m_visib < 8)
					pr("Sonar detects sub #%d @ %s\n",
					   targ.shp_uid,
					   xyas(targ.shp_x, targ.shp_y, player->cnum));
				else if (mcp->m_vrnge + tmcp->m_visib < 10)
					pr("Sonar detects %s @ %s\n",
					   prship(&targ),
					   xyas(targ.shp_x, targ.shp_y, player->cnum));
				else
					pr("Sonar detects %s %s @ %s\n", cname(targ.shp_own),
					   prship(&targ),
					   xyas(targ.shp_x, targ.shp_y, player->cnum));
			} else
				pr("Sonar detects %s %s @ %s\n", cname(targ.shp_own),
				   prship(&targ),
				   xyas(targ.shp_x, targ.shp_y, player->cnum));

			if (mchr[targ.shp_type].m_visib > vis[y][x]) {
				vis[y][x] = mchr[targ.shp_type].m_visib;
				/* &~0x20 makes it a cap letter */
				rad[y][x] = (*mchr[targ.shp_type].m_name) & ~0x20;
			}
		}
		if (!player->argp[2]) {
			rad[cy][cx] = '0';
			for (row=0; row < ns.range.height; row++)
				if (!blankrow(rad[row]))
					pr("%s\n", rad[row]);
		}
		pr("\n");
		
	}
	if (changed)
		writemap(player->cnum);		
	return RET_OK;
}

void
plane_sona(plane_list,x,y,head)
struct  qelem *plane_list;
int	x,y;
struct	shiplook *head;
{
	struct	plnstr	*pp;
	struct	plchrstr *pcp;
	struct	mchrstr *tmcp;
	struct	shpstr *targ,s;
	struct	natstr *natp;
	struct  qelem   *qp;
	struct  qelem   *next;
	struct  plist   *ip;
	struct  sctstr  sect;
	int	found=0;
	int	range,i;
	int	pingrange;
	int	vrange;
	int	dist;

	getsect(x,y,&sect);
	if ((sect.sct_type != SCT_WATER) && (sect.sct_type != SCT_HARBR))
		return;
        for (qp = plane_list->q_forw; qp != plane_list; qp = next) {
                next = qp->q_forw;
                ip = (struct plist *) qp;
                pp = &ip->plane;
                pcp = ip->pcp;
		if (!(pcp->pl_flags & P_A)) /* if it isn't an ASW plane */
			continue;
		range = (int) techfact(pp->pln_tech, (double) ((100-pcp->pl_acc)/10));
/*
		for (i=0; targ = getshipp(i); i++) {
*/
		for (i=0; getship(i,&s) ; i++) {
			targ = &s;
			if (targ->shp_own == pp->pln_own || targ->shp_own == 0)
				continue;
/*
			if (have_looked(targ->shp_uid,head))
				continue;
*/
			if (have_found(targ->shp_uid,head))
				continue;
			set_have_looked(targ->shp_uid,head);
			tmcp = &mchr[targ->shp_type];
			if (!(tmcp->m_flags & M_SUB))
				continue;
			if (roll(100) > pln_identchance(pp, shp_hardtarget(targ), EF_SHIP))
				continue;
			pingrange = max(tmcp->m_visib, 10) * range / 10;
			vrange = ((float)pingrange) * ((float)pp->pln_effic / 200.0);
			dist = mapdist(targ->shp_x, targ->shp_y, x, y);
			pingrange = (max(pingrange, 2) * targ->shp_effic);
			pingrange = roundavg(pingrange/100.0); 
			if (dist > pingrange)
				continue;
			if (tmcp->m_flags & M_SONAR && targ->shp_own) {
				natp = getnatp(targ->shp_own);
				if (natp->nat_flags & NF_SONAR)
				wu(0, targ->shp_own,
				   "Sonar ping from %s detected by %s!\n",
				   xyas(x, y, targ->shp_own),
				   prship(targ));
			}
			if ((dist > vrange))
				continue;
			set_have_found(targ->shp_uid,head);
			if (!found){
				mpr(pp->pln_own,
				    "\nSonar contact in %s\n",xyas(x,y,pp->pln_own));
				found=1;
			}
			if (getrel(getnatp(targ->shp_own), pp->pln_own) < FRIENDLY &&
			    roll(100) > pln_identchance(pp, shp_hardtarget(targ), EF_SHIP))
				if (roll(100) > pln_identchance(pp, shp_hardtarget(targ), EF_SHIP))
					mpr(pp->pln_own,
					    "sub #%d %s\n",
					    targ->shp_uid,
					    xyas(targ->shp_x, targ->shp_y, pp->pln_own));
				else
					mpr(pp->pln_own,
					    "%s %s\n",
					    prship(targ),
					    xyas(targ->shp_x, targ->shp_y, pp->pln_own));
			else
				mpr(pp->pln_own,
				    "%s %s @ %s\n", cname(targ->shp_own),
				    prship(targ),
				    xyas(targ->shp_x, targ->shp_y, pp->pln_own));
		}
	}
}

/* 
 * line_of_sight() - is there a "straight" all water path from (x,y) to (tx,ty)
 * Ken & Irina Stevens, 1995
 */

#define DOT(ax,ay,bx,by) ((ax)*(bx) + (ay)*(by))
#define LEN(x,y) ((x)*(x) + (y)*(y))
#define DIST(ax,ay,bx,by) LEN(bx - ax, by -ay)

static int
line_of_sight(rad, ax, ay, bx, by)
	s_char	rad[][WORLD_X+1];
	int	ax, ay, bx, by;
{
	int	dx = bx - ax;
	int	dy = by - ay;
	int	dlen = LEN(dx, dy);
	int	n;
	int	cx = 0;
	int	cy = 0;
	int	tx, ty;		/* test point */
	double	cd_dist = dlen; /* closest distance from c to vector d */
	double	md_dist;	/* minimum distance from t to vector d */
	double	td_dist;	/* distance from t to vector d */
	double	td_proj;	/* the projection of t onto vector d */
	int	closest;	/* index of closest */
	int	blocked = 0;

	while (cd_dist) {
		if (blocked)
			return 0;
		md_dist = 100;  /* will always be <= 2 */
		closest = -1;
		for (n = 1; n <= 6; ++n) { /* Directions */
			tx = cx + diroff[n][0];
			ty = cy + diroff[n][1];
			if (DIST(tx,ty,dx,dy) >= cd_dist)
				continue;
			td_proj = (double)DOT(tx,ty,dx,dy) / dlen;
			td_dist = DIST(tx,ty,td_proj*dx,td_proj*dy);
			if (td_dist < md_dist) {
				md_dist = td_dist;
				closest = n;
			}			
		}
		if (closest < 0) /* not possible */
			return 0;
		cx = cx + diroff[closest][0];
		cy = cy + diroff[closest][1];
		blocked = (rad[ay+cy][ax+cx] != dchr[SCT_WATER].d_mnem);
		cd_dist = DIST(cx,cy,dx,dy);
	}
	return 1;
}

int
blankrow(s)
	s_char *s;
{
	while (*s) {
		if (*s != ' ')
			return 0;
		++s;
	}
	return 1;
}
