#ifndef lint
static char *RCSid = "$Header: /usr/brule/guest/empire/empire/emprcs/lib/subs/fortdef.c,v 2.5 1995/10/01 22:04:44 empire Exp $";
#endif

/*
 * fortdef.c
 *
 * fort defends an area
 *
 * The base routines can also be used for general purposes.
 * Noisy tells whther to send teles, print things, etc.
 * Defending tells whether they are being defensive, or offensive.
 *
 * from PSL Empire, 1985
 */

#include "misc.h"
#include "var.h"
#include "xy.h"
#include "nat.h"
#include "sect.h"
#include "ship.h"
#include "land.h"
#include "news.h"
#include "nsc.h"
#include "file.h"
#include "options.h"

#define	QUIET	0
#define	NOISY	1

/*
 * See if any nearby ships will open up on the attacker
 * Return damage done to attacker, if any.
 * Subtracts shells used for firing.  Responding ships
 * require military, shells, and guns.
 */
int
shipdef(att, own, x, y)
	natid	att;
	natid	own;
	coord	x;
	coord	y;
{
	return sd(att, own, x, y, NOISY, 1, 1);
}

int
sd(att, own, x, y, noisy, defending, usesubs)
	natid	att;
	natid	own;
	coord	x;
	coord	y;
	int	noisy, defending, usesubs;
{
	int	nshot;
	double	range;
	double	eff;
	struct	shpstr ship;
	struct	nstr_item ni;
	int	vec[I_MAX+1];
	int	dam, rel, rel2;

	if (own == 0)
		return 0;
	if (att == own)
		return 0;
	eff = 1.0;
	snxtitem_dist(&ni, EF_SHIP, x, y, 8);
	while (nxtitem(&ni, (caddr_t)&ship) && eff > 0.30) {
		if (ship.shp_own == att)
			continue;
		if (ship.shp_own == 0)
			continue;

		rel = getrel(getnatp(ship.shp_own),own);
		rel2 = getrel(getnatp(ship.shp_own),att);
		if ((ship.shp_own != own) &&
			((rel != ALLIED) || (rel2 != AT_WAR)))
			continue;
		if (ship.shp_effic < 60)
			continue;
		if ((mchr[ship.shp_type].m_flags & M_SUB) && (!usesubs))
			continue;
		range = techfact(ship.shp_tech, 
			mchr[ship.shp_type].m_frnge * ship.shp_effic / 200.0);
		range = (double)roundrange(range);
		if (range < ni.curdist)
			continue;
		/* must have gun, shell, and milit to fire */
		if (getvec(VT_ITEM, vec, (caddr_t)&ship, EF_SHIP) < 3)
			continue;
		if (vec[I_SHELL] < mchr[ship.shp_type].m_glim)
			vec[I_SHELL] += supply_commod(ship.shp_own,ship.shp_x,
				ship.shp_y,I_SHELL,
				vec[I_SHELL]-mchr[ship.shp_type].m_glim);
		nshot = min(min(vec[I_GUN], vec[I_SHELL]), vec[I_MILIT]);
		nshot = min(nshot, mchr[ship.shp_type].m_glim);
		if (nshot <= 0)
			continue;
		(void)putvar(V_SHELL,vec[I_SHELL]-nshot,(caddr_t)&ship,EF_SHIP);
		putship(ship.shp_uid, &ship);
		if (defending)
			nreport(ship.shp_own, N_FIRE_BACK, att, 1);
		else
			nreport(ship.shp_own, N_FIRE_S_ATTACK, att, 1);
		dam = seagun(ship.shp_effic, nshot);
		eff *= (1.0 - (0.01 * dam));
		if (noisy) {
			pr_beep();
			pr("Incoming shell%s %d damage!\n",
				nshot == 1 ? " does" : "s do", dam);
		}
		if (noisy || (ship.shp_own != own)){
			if (ship.shp_own == own)
				wu(0, own, "%s fired on %s at %s doing %d damage.\n", prship(&ship),
				   cname(att), xyas(x, y,own),dam);
			else{
				if (defending)
					wu(0, ship.shp_own, "%s fired on %s at %s in defense of %s, doing %d damage.\n", prship(&ship),
					   cname(att),
					   xyas(x, y, ship.shp_own), 
					   cname(own),dam);
				else
					wu(0, ship.shp_own,"%s supported %s attacks against %s at %s, doing %d damage.\n", prship(&ship),
					   cname(own),
					   cname(att),
					   xyas(x, y, ship.shp_own), dam);
			}
		}
	}
	return (int) 100 - (eff * 100);
}

/*
 * Determine if any nearby gun-equipped sectors are within
 * range and able to fire at an attacker.  Firing sectors
 * need to have guns, shells, and military.  Sector being
 * attacked is x,y -- attacker is at ax,ay.
 */
int
defdef(att, def_own, defval, ax, ay)
	natid	att;
	natid	def_own;
	int	defval;
	coord	ax;
	coord	ay;
{
	return dd(att, def_own, defval, ax, ay, NOISY, 1);
}

int
dd(att, def_own, ax, ay, noisy, defending)
	natid	att;
	natid	def_own;
	coord	ax;
	coord	ay;
	int	noisy, defending;
{
	int	dam, rel, rel2;
	double	tech;
	double	range;
	struct	sctstr firing;
	struct	nstr_sect ns;
	int	vec[I_MAX+1];

	if (def_own == 0)
		return 0;
	if (att == def_own)
		return 0;
	tech = tfact(def_own, 1.0);
	dam = 0;
	snxtsct_dist(&ns, ax, ay, 8);
	while (nxtsct(&ns, &firing) && dam < 80) {
		if (firing.sct_own == att)
			continue;
		if (firing.sct_own == 0)
			continue;
		if (firing.sct_effic < (u_char)FORTEFF )
			continue;
		rel = getrel(getnatp(firing.sct_own),def_own);
		rel2 = getrel(getnatp(firing.sct_own),att);
		if ((firing.sct_own != def_own) &&
			((rel != ALLIED) || (rel2 != AT_WAR)))
			continue;
		if (getvec(VT_ITEM, vec, (caddr_t)&firing, EF_SECTOR) < 0)
			continue;

		range = tech * 7.0;
		if (firing.sct_effic > 59)
			range++;
		range = (double)roundrange(range);
		if (range < ns.curdist)
			continue;
	/* XXX defdef damage is additive, but ship or land unit damage isn't */
		dam += sb(att, def_own, &firing,
				ax, ay, noisy, defending);
	}
	return dam;
}

/* Shoot back
 *
 * See if the sector being fired at will defend itself.
 */
int
sb(att, def, sp, tx, ty, noisy, defending)
	natid	att, def;
	struct	sctstr *sp;
	coord	tx;
	coord	ty;
	int	noisy, defending;
{
	register int damage;
	natid	own;
	int	shell;
	double	range;
	int	range2, gun;

	if (sp->sct_type != SCT_FORTR) {
		/* XXX I don't like this restriction */
		return 0;
	}

	if (sp->sct_effic < (u_char)FORTEFF)
		return 0;

	own = sp->sct_own;
	if (own == 0)
		return 0;
	if (att == own)
		return 0;
	range = tfact(own, 7.0);
	if (sp->sct_effic > 59)
		range++;
	range = (double)roundrange(range);
	range2 = mapdist((int)sp->sct_x, (int)sp->sct_y, tx, ty);
	gun = getvar(V_GUN, (caddr_t)sp, EF_SECTOR);
	if (gun == 0)
		return 0;
	shell = getvar(V_SHELL, (caddr_t)sp, EF_SECTOR);
	if (shell <= 0)
		shell += supply_commod(sp->sct_own,sp->sct_x,sp->sct_y,I_SHELL,
			1);
	if (shell <= 0)
		return 0;
	if (range < range2)
		return 0;
	putvar(V_SHELL, shell - 1, (caddr_t)sp, EF_SECTOR);
	putsect(sp);
	damage = landgun((int)sp->sct_effic,gun);
	if (sp->sct_own != def)
		wu(0,sp->sct_own, "%s fired on %s in %s in defense of %s, doing %d damage!\n",
			xyas(sp->sct_x,sp->sct_y,sp->sct_own),
			cname(att), xyas(tx,ty,sp->sct_own),
			cname(def), damage);
	if (defending)
		nreport(sp->sct_own, N_FIRE_BACK, att, 1);
	else
		nreport(sp->sct_own, N_FIRE_F_ATTACK, att, 1);
	if (noisy)
		pr("Incoming shell! %d damage done.\007\n", damage);
	return damage;
}
