#ifndef lint
static char *RCSid = "$Header: fire.c,v 1.22 90/03/20 01:09:30 mr-frog Exp $";
#endif /* not lint */

/*
 * fire.c
 *
 * fire at other sectors, ships.
 *
 * from PSL Empire, 1985
 */

#include "misc.h"
#include "var.h"
#include "xy.h"
#include "treaty.h"
#include "nat.h"
#include "ship.h"
#include "sect.h"
#include "news.h"
#include "nsc.h"
#include "file.h"
#include <ctype.h>

enum targ_type {
	targ_land, targ_ship, targ_sub, targ_bogus
};

int fire_issector();

fire()
{
	extern	char *argp[];
	char	vbuf[10];
	char	fbuf[10];
	char	*ptr;
	char	buf[80];
	double	range;
	int	trange;
	coord	fx;
	coord	fy;
	coord	x;
	coord	y;
	int	btused=0;
	int	mil;
	int	gun;
	int	shell;
	int	shots;
	double	guneff;
	int	dam;
#ifdef RETREAT
	int	totaldefdam=0;
#endif /* RETREAT */
	int	def;
	int	vdef;
	int	fshipno;
	int	vshipno;
	double	prb;
	natid	vict;
	struct	shpstr fship;
	struct	sctstr fsect;
	struct	shpstr vship;
	struct	sctstr vsect;
	enum	targ_type target, attacker;
#ifdef SLOW_WAR
	int	rel;
	struct natstr	*natp;
#endif /* SLOW_WAR */
#ifdef BETTERARMOR
        struct  mchrstr *mcp;
#endif /* BETTERARMOR */

	if ((ptr = getstarg(argp[1], "Firing at? ")) == 0 || *ptr == '\0')
		return RET_SYN;
	(void)strcpy(vbuf, ptr);
	if (fire_issector(vbuf)) {
		target = targ_land;
		ptr = fmt("Firing on sector %s from? ", vbuf);
	}
	else {
		target = targ_ship;
		ptr = fmt("Firing on ship #%s from? ", vbuf);
	}
	if ((ptr = getstarg(argp[2], "Firing from? ")) == 0 || *ptr == '\0')
		return RET_SYN;
	(void)strcpy(fbuf, ptr);
	if (fire_issector(fbuf)) {
		attacker = targ_land;
		shots = 1;
	}
	else {
		attacker = targ_ship;
		ptr = getstarg(argp[3], "How many guns (return for max)? ");
		if (ptr == 0 || *ptr == 0)
			shots = -1; /* convert to max later */
		else
			shots = atoi(ptr);
	}
	if (target == targ_ship) {
		vshipno = atoi(vbuf);
		if (vshipno < 0 || !getship(vshipno, &vship) || 
			(!vship.shp_own))  {
				pr("No such ship exists!\n");
				return RET_SYN;
		}
		vdef = seadef(vship.shp_type);
		target = (mchr[vship.shp_type].m_flags & M_SUB) ?
			targ_sub : targ_ship;
		vict = vship.shp_own;
		x = vship.shp_x;
		y = vship.shp_y;
		if (!getsect(x, y, &vsect)) {
			pr("No such sector exists!\n");
			return RET_SYN;
		}
	} else {
		if (!sarg_xy(vbuf, &x, &y) || !getsect(x, y, &vsect)) {
			pr("No such sector exists!\n");
			return RET_SYN;
		}
		if (vsect.sct_type == SCT_SANCT || 
		    vsect.sct_type == SCT_WATER)
			target = targ_bogus;
		else
			target = targ_land;
		vdef = landdef((int)vsect.sct_type);
		vict = vsect.sct_own;
		x = vsect.sct_x;
		y = vsect.sct_y;
#ifdef SLOW_WAR
		natp = getnatp(cnum);
		rel = getrel(natp,vict);
		if ((rel != AT_WAR) && (cnum != vict) && (vict) && (vsect.sct_oldown != cnum)){
			pr("You're not at war with them!\n");
			return RET_FAIL;
		}
#endif /* SLOW_WAR */
	}
	if (attacker == targ_ship) {
		ptr = getstarg(0, "How many guns (return for max)? ");
		if (ptr == 0 || *ptr == 0)
			shots = -1; /* convert to max later */
		else
			shots = atoi(ptr);
		if ((fshipno = atoi(fbuf)) < 0 || !getship(fshipno, &fship)) {
			pr(fmt("%s is not a ship!\n", fbuf));
			return RET_SYN;
		}
		if (fship.shp_own != cnum) {
			pr("Not your ship!\n");
			return RET_FAIL;
		}
		fx = fship.shp_x;
		fy = fship.shp_y;
		attacker = (mchr[fship.shp_type].m_flags & M_SUB) ?
			targ_sub : targ_ship;
		if (attacker == targ_sub){
			pr("Subs may not fire normally.. use torpedo.\n");
			return RET_FAIL;
		}
		if ((mil = getvar(V_MILIT, (char *)&fship, EF_SHIP)) < 1) {
			pr("Not enough military for firing crew.\n");
			return RET_FAIL;
		}
		gun = getvar(V_GUN, (char *)&fship, EF_SHIP);
		gun = min(gun, mchr[fship.shp_type].m_glim);
		if (mchr[fship.shp_type].m_frnge == 0 || gun == 0) {
			pr("Insufficient arms.\n");
			return RET_FAIL;
		}
		if ((shell = getvar(V_SHELL, (char *)&fship, EF_SHIP)) == 0) {
			pr("Klick!     ...\n");
			return RET_FAIL;
		}
		if (fship.shp_effic < 60) {
			pr(fmt("Ship #%d is crippled (%d%%)\n", fshipno,
			       fship.shp_effic));
			return RET_FAIL;
		}
		range = techfact(fship.shp_tech,
				 mchr[fship.shp_type].m_frnge / 2.0);
		pr(fmt("range is %.2f\n", range));
		if (target == targ_sub) {
			if ((mchr[fship.shp_type].m_flags & M_DCH) == 0) {
				pr(fmt("A %s can't drop depth charges!\n",
				       mchr[fship.shp_type].m_name));
				return RET_FAIL;
			}
#ifndef NEWSUBS
			if (fx != x || fy != y) {
				pr("Y-guns can't shoot that far!\n");
				return RET_FAIL;
			}
#endif /* NEWSUBS */
			if (shell < 2) {
				pr("Not enough shells for depth charge!\n");
				return RET_FAIL;
			}
			shots = 2;
		} else {
			gun = min(gun, shell);
			gun = min(gun, mil / 2);
			gun = max(gun, 1);
			if (shots > gun || shots < 0)
				shots = gun;
			else if (shots == 0) {
				pr("No shots fired.\n");
				return RET_FAIL;
			}
		}
		guneff = seagun(fship.shp_type, fship.shp_effic, shots);
		def = seadef(fship.shp_type);
		shell -= shots;
		putvar(V_SHELL, shell, (char *)&fship, EF_SHIP);
#ifndef NOMOBCOST
		fship.shp_mobil = max(fship.shp_mobil - 15, -100);
#endif /* NOMOBCOST */
	} else {
		if (!sarg_xy(fbuf, &fx, &fy) || !getsect(fx, fy, &fsect) ||
		    fsect.sct_own != cnum) {
			pr(fmt("No fortress at %s\n", xyas(fsect.sct_x,
				fsect.sct_y,cnum)));
			return RET_FAIL;
		}
		if (fsect.sct_type != SCT_FORTR) {
			pr("Not a fortress.\n");
			return RET_FAIL;
		}
		attacker = targ_land;
		if ((gun = getvar(V_GUN, (char *)&fsect, EF_SECTOR)) == 0) {
			pr("Insufficient arms.\n");
			return RET_FAIL;
		}
		if ((shell = getvar(V_SHELL, (char *)&fsect, EF_SECTOR)) == 0) {
			pr("Klick!     ...\n");
			return RET_FAIL;
		}
		if (getvar(V_MILIT, (char *)&fsect, EF_SECTOR) < 5) {
			pr("Not enough military for firing crew.\n");
			return RET_FAIL;
		}
		if (target == targ_sub) {
			pr("Target ship not sighted!\n");
			return RET_FAIL;
		}
		range = tfact(cnum, (double)min(gun, 7));
		pr(fmt("range is %.2f\n", range));
		guneff = landgun((int)fsect.sct_effic);
		def = landdef((int)fsect.sct_type);
		putvar(V_SHELL, --shell, (char *)&fsect, EF_SECTOR);
	}
	if (chance(range - ((int)range)))
		range=range+1.0;
	trange = mapdist(x, y, fx, fy);
	if ((trange > range) || (trange > 7)) {
		switch (target) {
		case targ_land:
		case targ_bogus:
			pr("Target out of range.  Thud.\n");
			break ;
		default:
			pr("Target ship out of range.  Splash.\n");
			break ;
		}	
		switch (attacker) {
		case targ_land:
			putsect(&fsect);
			break ;
		default:
			putship(fshipno, &fship);
		}
		return RET_FAIL;
	}
#ifdef SLOW_WAR
	if (target == targ_land){
		rel = getrel(natp,vsect.sct_own);
		if ((rel != AT_WAR) && (cnum != vsect.sct_own) &&
			(vsect.sct_own)){
			pr(fmt("You're not at war with the owner of %s!\n",
				xyas(x,y,cnum)));
			return RET_FAIL;
		}
	}
#endif /* SLOW_WAR */
	if (target == targ_bogus) {
		if (vsect.sct_type == SCT_SANCT) {
			pr(fmt("%s is a %s!!\n", vbuf,
				dchr[SCT_SANCT].d_name));
			return RET_FAIL;
		} else if (vsect.sct_type == SCT_WATER) {
			pr(fmt("You must specify a ship in sector %s!\n",
				vbuf));
			return RET_FAIL;
		}
	}
	if (!trechk(cnum, vict, target == targ_land ? LANFIR : SEAFIR))
		return RET_FAIL;
	switch (target) {
	case targ_sub:
		pr("Kawhomp!!!\07\n");
#ifdef RETREAT
		if (((vship.shp_rflags & RET_INJURED) == 0) || !dam)
			retreat(&vship,'d');
#endif /* RETREAT */
		break ;
	default:
		pr("Kaboom!!!\07\n");
		prb = (double)trange / range;
		prb *= prb;
		if (chance(prb)) {
			pr(fmt("Wind deflects shell%s.\n", splur(shots)));
			guneff *= (chance(0.50) ? 0.5 : 1.0 - prb);
		}
		break ;
	}
	dam = shelldam((double)guneff, vdef);
	btused = roundavg(guneff / 20.0);
	switch (target) {
	case targ_land:
		nreport(cnum, N_SCT_SHELL, vict, 1);
		sectdamage(&vsect, dam);
		if (vict && vict != cnum)
			wu(0, vict,
			   fmt("Country #%d shelled sector %s for %d%% damage",
			       cnum, xyas(x, y, vict), dam));
		pr(fmt("Shell%s hit sector %s for %d%% damage.\n",
		       splur(shots), xyas(x, y, cnum), dam));
		break ;
	case targ_ship:
		nreport(cnum, N_SHP_SHELL, vict, 1);
	default:
#ifdef NEWSUBS
		if (target == targ_sub){
			dam = (random() % 30) + 10;
			if (trange)
				dam = (((float)dam)/((float)(trange+2.0)/2.0));
			dam *= ((float)fship.shp_effic/100.0);
		}
#endif /* NEWSUBS */
#ifdef BETTERARMOR
        	mcp = &mchr[vship.shp_type];
		dam = dam * (63.0/(float)mcp->m_armor);
		pr(fmt("Damage reduced to %d by armor.\n",dam));
#endif /* BETTERARMOR */
#ifdef RETREAT
		if ((target != targ_sub) ||
			((vship.shp_rflags & RET_DCHRGED) == 0))
#endif /* RETREAT */
		check_retreat_and_do_shipdamage(&vship, dam);
#ifdef RETREAT
		else
			shipdamage(&vship, dam);
#endif /* RETREAD */
		if (vict)
			wu(0, vict,
#ifdef	SHIPNAMES
			   fmt("Country #%d shelled %s %s(#%d) for %d%% damage",
			       cnum, mchr[vship.shp_type].m_name,
			       vship.shp_name, vshipno,
#else
			   fmt("Country #%d shelled %s #%d for %d%% damage",
			       cnum, mchr[vship.shp_type].m_name, vshipno,
#endif	SHIPNAMES
			       dam));
		if (vship.shp_effic > 20)
#ifdef	SHIPNAMES
			pr(fmt("Shell%s hit %s %s(#%d) for %d%% damage.\n",
			       splur(shots), mchr[vship.shp_type].m_name,
			       vship.shp_name,
#else
			pr(fmt("Shell%s hit %s #%d for %d%% damage.\n",
			       splur(shots), mchr[vship.shp_type].m_name,
#endif	SHIPNAMES
			       vshipno, dam));
		break ;
	}
	switch (target) {
	case targ_land:
		if ((cnum != vict) &&
			(dam = fortdef(&vsect, def, fx, fy))){
#ifdef RETREAT
			totaldefdam += dam;
#endif /* RETREAT */
			switch (attacker) {
			case targ_land:
				sectdamage(&fsect, dam);
				(void)strcpy(buf, fmt("sector %s",
						xyas(fx, fy, vict)));
				break ;
			default:
#ifdef BETTERARMOR
                                mcp = &mchr[fship.shp_type];
				dam = ((float)dam*(63.0/(float)mcp->m_armor));
				pr(fmt("Damage reduced to %d by armor.\n",dam));
#endif /* BETTERARMOR */
				shipdamage(&fship, dam);
#ifdef	SHIPNAMES
				(void)strcpy(buf, fmt("%s %s(#%d)",
						mchr[fship.shp_type].m_name,
						fship.shp_name,
#else
				(void)strcpy(buf, fmt("%s #%d",
						mchr[fship.shp_type].m_name,
#endif	SHIPNAMES
						fshipno));
				break ;
			}
			if (vict)
				wu(0, vict,
				   fmt("In defence, sector %s did %d%% damage to %s",
				       xyas(x, y, vict), dam, buf));
		}
		if ((cnum != vict) &&
			(dam = shipdef(vict, def, fx, fy))) {
#ifdef RETREAT
			totaldefdam += dam;
#endif /* RETREAT */
			switch (attacker) {
			case targ_land:
				sectdamage(&fsect, dam);
				if (vict)
					wu(0, vict,
						fmt("Return fire damaged sector %s %d%%",
						xyas(fx, fy, vict), dam));
				break ;
			default:
#ifdef BETTERARMOR
				mcp = &mchr[fship.shp_type];
				dam = ((float)dam * (63.0/(float)mcp->m_armor));
				pr(fmt("Damage reduced to %d by armor.\n",dam));
#endif /* BETTERARMOR */
				if (vict)
					wu(0, vict,
#ifdef  SHIPNAMES
					fmt("Return fire damaged %s %s(#%d) %d%%",
						mchr[fship.shp_type].m_name,
						fship.shp_name,
#else
						fmt("Return fire damaged %s #%d %d%%",
						mchr[fship.shp_type].m_name,
#endif  SHIPNAMES
						fshipno, dam));
				shipdamage(&fship, dam);
				break ;
			}
		}
		break ;
	default:
		if ((cnum != vict) &&
			(dam = shipdef(vict, def, fx, fy))){
#ifdef RETREAT
			totaldefdam += dam;
#endif /* RETREAT */
			switch (attacker) {
			case targ_land:
				sectdamage(&fsect, dam);
				if (vict)
					wu(0, vict,
					   fmt("Return fire damaged sector %s %d%%",
					       xyas(fx, fy, vict), dam));
				break ;
			default:
#ifdef BETTERARMOR
                                mcp = &mchr[fship.shp_type];
				dam = ((float)dam * (63.0/(float)mcp->m_armor));
				pr(fmt("Damage reduced to %d by armor.\n",dam));
#endif /* BETTERARMOR */
				if (vict)
					wu(0, vict,
#ifdef	SHIPNAMES
					   fmt("Return fire damaged %s %s(#%d) %d%%",
					       mchr[fship.shp_type].m_name,
					       fship.shp_name,
#else
					   fmt("Return fire damaged %s #%d %d%%",
					       mchr[fship.shp_type].m_name,
#endif	SHIPNAMES
					       fshipno, dam));
				shipdamage(&fship, dam);
				break ;
			}
		}
	}
	if ((cnum != vict) && (dam = defdef(vict, def, fx, fy))){
#ifdef RETREAT
		totaldefdam += dam;
#endif /* RETREAT */
		switch (attacker) {
		case targ_land:
			sectdamage(&fsect, dam);
			(void)strcpy(buf, fmt("sector %s",
					xyas(fx, fy, vict)));
			break ;
		default:
#ifdef BETTERARMOR
                        mcp = &mchr[fship.shp_type];
			dam = ((float)dam * (63.0/(float)mcp->m_armor));
			pr(fmt("Damage reduced to %d by armor.\n",dam));
#endif /* BETTERARMOR */
			shipdamage(&fship, dam);
#ifdef	SHIPNAMES
			(void)strcpy(buf, fmt("%s %s(#%d)",
					mchr[fship.shp_type].m_name,
					fship.shp_name,
#else
			(void)strcpy(buf, fmt("%s #%d",
					mchr[fship.shp_type].m_name,
#endif	SHIPNAMES
					fshipno));
			break ;
		}
		if (vict)
			wu(0, vict,
			   fmt("Fortress(es) defending %s did %d%% damage to %s",
			       xyas(x, y, vict), dam, buf));
	}
#ifdef RETREAT
	if ((totaldefdam == 0) && (target == targ_ship))
		if ((vship.shp_rflags & RET_INJURED) == 0)
			retreat(&vship,'h');
#endif /* RETREAT */
	switch (target) {
	case targ_land:
		putsect(&vsect);
		break ;
	default:
		putship(vshipno, &vship);
		break ;
	}
	switch (attacker) {
	case targ_land:
		putsect(&fsect);
		break ;
	default:
#ifdef	MISSDEF
		if ((target == targ_ship) || (target == targ_sub))
		{
			if ((fship.shp_effic > 20) && (cnum != vict))
			{
				missdef(&fship,vict);
			};
		};
#endif	MISSDEF
		putship(fshipno, &fship);
		break ;
	}
	NAT_DELTA(nat_btu, cnum, -btused);
	return RET_OK;
}

int
fire_issector(arg)
	char *arg;
{
	char c;

	while (c = *arg++) {
		if (!isdigit(c) && !isspace(c)) return 1;
	}
	return 0;
}

