#ifndef lint
static char *RCSid = "$Header: atta.c,v 1.1 89/12/14 08:19:14 jay Exp $";
#endif /* not lint */

/*
 * atta.c
 *
 * attack another sector.
 *
 * from PSL Empire, 1985
 */

#include "misc.h"
#include "file.h"
#include "var.h"
#include "sect.h"
#include "path.h"
#include "news.h"
#include "treaty.h"
#include "nat.h"
#include "xy.h"

#define DEFENDER	6

static struct	unit {
	short	x;
	short	y;
	float	def;
	short	troops;
	int	des;
	int	eff;
} unit[DEFENDER+1];	/* 6 for attackers, 1 (#6) for defenders */

static double calcodds();

atta()
{
	extern	char *argp[];
	coord	lx, ly;
	struct sctstr dsect, asect;
	register int n;
	register int a_nsect;	/* number of attacking sectors */
	natid	d_cnum;
	int	off_x;
	int	off_y;
	int	o_mil;		/* occupation troops */
	int	d_mil;		/* total defending mil */
	int	mil;
	int	movein;
	int	a_mil;
	int	a_total;	/* total attacking mil */
	int	i;
	int	s;
	int	mob_support;
	int	success;
	int	mcst;
	int	asects;
	int	mcost;
	int	dam;
	int	plague;
	int	d_cas;
	int	a_cas;
	float	saveodds;
	double	movein_pct;
	double	odds;
	double	init_cas;
	struct	dchrstr *dp;
	char	*p;
#ifdef MERC
	int	mob;
#endif
#ifdef SLOW_WAR
	int	rel;
	struct natstr   *natp;
#endif /* SLOW_WAR */
#ifdef SNEAK_ATTACK
	char	y_or_n[80], *ppp=(char *)0;
#endif /* SNEAK_ATTACK */

	if (!(p = getstarg(argp[1], "Sector :  ")))
		return RET_SYN;
	if (!sarg_xy(p, &lx, &ly))
		return RET_SYN;
	if (neigh(lx, ly, cnum) == 0) {
		pr(fmt("You are not adjacent to %s\n", xyas(lx, ly, cnum)));
		return RET_FAIL;
	}
	if (!(p = getstarg(argp[2], "Move in if victorious? ")))
		return RET_SYN;
	if (*p == 'n' || *p == 'N')
		movein = 0;
	else
		movein = 1;
	getsect(lx, ly, &dsect);
	d_cnum = dsect.sct_own;
	if (dsect.sct_own == cnum) {
		pr("You can't attack your own sector.\n");
		return RET_FAIL;
	}
#ifdef SLOW_WAR
	natp = getnatp(cnum);
	rel = getrel(natp,dsect.sct_own);
#ifdef SNEAK_ATTACK
        if ((rel != AT_WAR) && (dsect.sct_own) && (dsect.sct_oldown != cnum) &&
	    (rel != SITZKRIEG) && (rel != MOBILIZATION)) {
		pr("You're not at war with them!\n");
		while (!ppp || ((*ppp != 'y') && (*ppp != 'n'))){
			bzero(y_or_n,80);
			if (ppp && (*ppp != 'y') && (*ppp != 'n'))
				pr("Answer y or n!\n");

			ppp=getstarg(y_or_n,fmt("Do you really want to sneak attack [yn]? "));
			if (isupper(*ppp))
				*ppp=tolower(*ppp);
		}
		if (*ppp != 'y'){
			pr("Sneak attack cancelled!\n");
			return RET_FAIL;
		}
	}
	if ((rel != AT_WAR) && (dsect.sct_own) && (dsect.sct_oldown != cnum) &&
	    ((rel == MOBILIZATION) || (rel == SITZKRIEG))) {
#else
	if ((rel != AT_WAR) && (dsect.sct_own) && (dsect.sct_oldown != cnum)){
#endif /* SNEAK_ATTACK */
		pr("You're not at war with them!\n");
		return RET_FAIL;
	}
#endif /* SLOW_WAR */
	dp = &dchr[dsect.sct_type];
#if	defined(BMAP) || defined(AUTONAV)
	setbigmap(lx, ly, dchr[dsect.sct_type].d_mnem);
	writebigmap();
#endif
	if ((mcst = dp->d_mcst) <= 0) {
		pr(fmt("You can't attack %s.\n", dchr[dsect.sct_type].d_name));
		return RET_FAIL;
	}
	if (dsect.sct_own && !trechk(cnum, dsect.sct_own, LANATT))
		return RET_FAIL;
	plague = getvar(V_PSTAGE, (char *)&dsect, EF_SECTOR)
		== PLG_INFECT ? 1 : 0;
	d_mil = getvar(V_MILIT, (char *)&dsect, EF_SECTOR);
	unit[DEFENDER].x = lx;
	unit[DEFENDER].y = ly;
	unit[DEFENDER].troops = d_mil;
	unit[DEFENDER].des = dsect.sct_type;
	unit[DEFENDER].eff = dsect.sct_effic;
	pr(fmt("%s is a %d%% %s with approximately %d military.\n",
		xyas(lx, ly, cnum), round((int)dsect.sct_effic, 10),
		dp->d_name, round(d_mil, 10)));
	a_total = 0;
	a_nsect = 0;
	/* Determine attacking troop count */
	for (n = 1; n <= 6; n++) {	/* Directions */
		off_x = lx + diroff[n][0];
		off_y = ly + diroff[n][1];
		if (!getsect(off_x, off_y, &asect))
			continue;
		if (asect.sct_own != cnum || dchr[asect.sct_type].d_ostr < 1)
			continue;
#ifdef SNEAK_ATTACK
		if ((rel != AT_WAR) && (asect.sct_oldown == dsect.sct_own))
			continue;
#endif /* SNEAK_ATTACK */
		mcost = dchr[asect.sct_type].d_mcst + mcst * 2;
		mob_support = asect.sct_mobil * 24 / mcost;
		mil = getvar(V_MILIT, (char *)&asect, EF_SECTOR);
		if (mil > mob_support) {
			pr(fmt("The %d mobility units in %s will only",
				asect.sct_mobil, xyas(off_x, off_y, cnum)));
			pr(fmt(" support %d troops, \n", mob_support));
		} else {
			mob_support = mil;
			if (mob_support == 0)
				pr(fmt("No troops in %s\n",
					xyas(off_x, off_y, cnum)));
		}
		if (mob_support == 0)
			continue;
		a_mil = atopi(getstring(
		    fmt("Number of troops from %s at %s (max %d) : ",
			dchr[asect.sct_type].d_name,
			xyas(off_x, off_y, cnum), mob_support)));
		if (a_mil == 0)
			continue;
		getsect(off_x, off_y, &asect);
		a_mil = min(a_mil, min(mob_support, mil));
		a_total += a_mil;
#ifdef MERC
		/* require at least one mobility point to attack */
		mob = (int) ((float) a_mil / (24.0/(float) mcost));
		if(mob < 1)
			mob = 1;
		asect.sct_mobil -= mob;
#else
		asect.sct_mobil -= (int) ((float) a_mil / (24.0/(float) mcost));
#endif
		putsect(&asect);
		unit[a_nsect].x = off_x;
		unit[a_nsect].y = off_y;
		unit[a_nsect].troops = a_mil;
		unit[a_nsect].des = asect.sct_type;
		unit[a_nsect].eff = asect.sct_effic;
		a_nsect++;
		if (getvar(V_PSTAGE, (char *)&asect, EF_SECTOR) == PLG_INFECT)
			plague++;
	}
	if ((a_total <= 0) || (a_nsect <= 0)) {
		if (d_cnum != 0) {
			wu(0, d_cnum,
			    fmt("%s (#%d) considered attacking you @%s",
			    cname(cnum), cnum, xyas(lx, ly, d_cnum)));
		}
		pr("No troops for attack...\n");
		return RET_FAIL;
	}
	getsect(lx, ly, &dsect);	/* Shrink update collision window */
	d_mil = getvar(V_MILIT, (char *)&dsect, EF_SECTOR);
	if (abs(d_mil - unit[DEFENDER].troops) > 20)
		pr(fmt("Last second troop movements!  Now %d troops in %s!\n",
			round(d_mil, 10), xyas(lx, ly, cnum)));
	unit[DEFENDER].troops = d_mil;
	unit[DEFENDER].eff = dsect.sct_effic;
	odds = calcodds(a_nsect);
	saveodds = odds;
	pr(fmt("Your combat odds are %.1f%%\n", odds * 100.0));
	/*
	 * defender sector replying with gunfire: pick
	 * a random attacking sector to blast.
	 */
	n = rand() % a_nsect;
	dam = 0;
	init_cas = 0;
	/* Initial defensive counter-strike */
	if (odds > 0.30) {
		/*
		 * Only bother defending if the attacker has a chance
		 * of winning.
		 */
		dam = defdef(d_cnum, landdef((int)dsect.sct_type),
			unit[n].x, unit[n].y);
		if (dam > 0) {
#ifdef MERC
			if(d_cnum != 0)
#endif
			wu(0, d_cnum,
			    fmt("Fortress(es) defending %s did %d%% damage to %s",
				xyas(dsect.sct_x, dsect.sct_y, d_cnum), dam,
				xyas(unit[n].x, unit[n].y, d_cnum)));
			getsect(unit[n].x, unit[n].y, &asect);
			init_cas = getvar(V_MILIT, (char *)&asect, EF_SECTOR);
			sectdamage(&asect, dam);
			putsect(&asect);
			unit[n].eff = asect.sct_effic;
			init_cas -= getvar(V_MILIT, (char *)&asect, EF_SECTOR);
			unit[n].troops -= init_cas;
			if (unit[n].troops < 0)
				unit[n].troops = 0;
			odds = calcodds(a_nsect);
			pr(fmt("Your combat odds are now %.1f%%\n",
				odds * 100.0));
		}
		dam = shipdef(d_cnum, landdef((int)dsect.sct_type),
			unit[n].x, unit[n].y);
		if (dam > 0) {
#ifdef MERC
			if(d_cnum != 0)
#endif
			wu(0, d_cnum,
			    fmt("Ship(es) defending %s did %d%% damage to %s",
				xyas(dsect.sct_x, dsect.sct_y, d_cnum), dam,
				xyas(unit[n].x, unit[n].y, d_cnum)));
			getsect(unit[n].x, unit[n].y, &asect);
			init_cas = getvar(V_MILIT, (char *)&asect, EF_SECTOR);
			sectdamage(&asect, dam);
			putsect(&asect);
			unit[n].eff = asect.sct_effic;
			init_cas -= getvar(V_MILIT, (char *)&asect, EF_SECTOR);
			unit[n].troops -= init_cas;
			if (unit[n].troops < 0)
				unit[n].troops = 0;
			odds = calcodds(a_nsect);
			pr(fmt("Your combat odds are now %.1f%%\n",
				odds * 100.0));
		}
	}
	/* Pull attackers out of attacking sectors */
	for (n=0; n < a_nsect; n++) {
		getsect(unit[n].x, unit[n].y, &asect);
		putvar(V_MILIT,
			getvar(V_MILIT, (char *)&asect, EF_SECTOR) - unit[n].troops,
			(char *)&asect, EF_SECTOR);
		putsect(&asect);
	}
	success = 0;
	n = 0;	/* attacking sector round-robin counter */
	asects = a_nsect;	/* # of attacking sectors still surviving */
	a_cas = 0;	/* Casualty counts */
	d_cas = 0;
	/* automatic success if no defenders */
	if (unit[6].troops == 0)
		success++;
	while (!success && asects) {
		if (((a_cas + d_cas) % 70) == 69)
			pr("\n");
		odds = calcodds(a_nsect);
		if (chance(odds)) {
			pr("!");
			if (--unit[6].troops <= 0)
				success++;
			d_cas++;
		} else {
			for (s = 0; s < a_nsect; s++) {
				i = (n + s) % a_nsect;
				if (unit[i].troops <= 0)
					continue;
				/* There is at least one */
				if (--unit[i].troops == 0) {
					pr("*");
					asects--;
				} else
					pr("@");
				a_cas++;
				n = i + 1;	/* spin */
				break;
			}
		}
	}
	a_cas += init_cas;	/* don't forget initial casualties */
	pr("\n");
	if (success) {
#ifdef SNEAK_ATTACK
		if (rel != AT_WAR && dsect.sct_own) {
			pr(fmt("Your sneak attack was successful\n"));
			pr(fmt("But it will cost you $5000\n"));
			pr(fmt("War has been declared!!!!\n"));
			wu(0,dsect.sct_own,
                           fmt("Country %s (#%d) has Sneak Attacked!!\n",
                               cname(cnum),cnum)); 
			wu(0,dsect.sct_own,
                           fmt("Country %s (#%d) has Declared WAR on you!!\n",
                               cname(cnum),cnum)); 
			dolcost += 5000;
			rel = MOBILIZATION;
			nreport(cnum, N_DECL_WAR, dsect.sct_own, 1);
			setrel(cnum,dsect.sct_own,rel);
		}
#endif /* SNEAK_ATTACK */
		pr(fmt("We have captured %s, sir!\n", xyas(lx, ly, cnum)));
		if (d_cnum)
			wu(0, d_cnum, fmt("%s (#%d) lost %d troops taking %s",
				cname(cnum), cnum, a_cas,
				xyas(lx, ly, d_cnum)));
		nreport(cnum, N_WON_SECT, d_cnum, 1);
		a_total = 0;
		for (n = 0; n < a_nsect; n++)
			a_total += unit[n].troops;
		if (a_total == 0)  /* if everyone was killed off */
			movein = 0;
		if (movein)
			movein_pct = dmax(1.0 / (a_nsect + 1.0), 1.0 / a_total);
		else
			movein_pct = 0.0;
		o_mil = 0;
		for (n = 0; n < a_nsect; n++) {
			getsect(unit[n].x, unit[n].y, &asect);
			if (movein_pct > 0.0) {
				i = (int) (unit[n].troops * movein_pct + 0.5);
				if (i + o_mil > 999)
					i = 999 - o_mil;
				if (i > 0) {
					o_mil += i;
					unit[n].troops -= i;
				}
			}
			if (unit[n].troops > 0) {
				/* Move back */
				/*
				 * There is a possibility that the sector
				 * might have been "lost" when no civ and
				 * no mil exist in the sector.
				 */
				if (asect.sct_own == 0) {
					asect.sct_own = cnum;
					if (asect.sct_oldown == 0)
						asect.sct_oldown = cnum;
				}
				pr(fmt("%d troops return to %s\n",
					unit[n].troops,
					xyas(unit[n].x, unit[n].y, cnum)));
				if (plague
					&& !getvar(V_PTIME, (char *)&asect, EF_SECTOR))
					putvar(V_PTIME, PLG_EXPOSED,
						(char *)&asect, EF_SECTOR);
				putvar(V_MILIT,
					getvar(V_MILIT, (char *)&asect, EF_SECTOR)
					+ unit[n].troops, (char *)&asect, EF_SECTOR);
				putsect(&asect);
			}
		}
		if (movein)
			pr(fmt("%d of your troops now occupy %s\n",
				o_mil, xyas(lx, ly, cnum)));
		else
			pr(fmt("%s left unoccupied\n", xyas(lx, ly, cnum)));
		if (dsect.sct_type == SCT_CAPIT)
			caploss(&dsect, d_cnum,
				"* We have captured %s's capital, sir! *\n");
		o_mil = takeover(&dsect, o_mil);
		dsect.sct_mobil = 0;
	} else {
		pr("You have been defeated!\n");
		nreport(cnum, N_SCT_LOSE, d_cnum, 1);
#ifdef SNEAK_ATTACK
		if (rel != AT_WAR && dsect.sct_own) {
			pr(fmt("Your sneak attack was unsuccessful\n"));
			pr(fmt("But it will cost you $5000\n"));
			pr(fmt("War has been declared!!!!\n"));
			wu(0,dsect.sct_own,
                           fmt("Country %s (#%d) has Sneak Attacked!!\n",
                               cname(cnum),cnum)); 
			wu(0,dsect.sct_own,
                           fmt("Country %s (#%d) has Declared WAR on you!!\n",
                               cname(cnum),cnum)); 
			dolcost += 5000;
			rel = MOBILIZATION;
			nreport(cnum, N_DECL_WAR, dsect.sct_own, 1);
			setrel(cnum,dsect.sct_own,rel);
		}
#endif /* SNEAK_ATTACK */
		o_mil = unit[DEFENDER].troops;
		if (d_cnum)
			wu(0, d_cnum,
				fmt("%s (#%d) lost %d troops attacking %s",
				cname(cnum), cnum, a_cas,
				xyas(lx, ly, d_cnum)));
		plague = 0;
		/*
		 * subtract defender's mobility from sector based
		 * on the number of attackers.  This way we won't get
		 * 1 attacking military zeroing the mob in the defender's
		 * sector.
		 */
		n = dsect.sct_mobil - ((saveodds > 0.25)
			? (d_mil / (24.0 / (float)mcost)) : 1);
		dsect.sct_own = d_cnum;
		dsect.sct_mobil = n < 0 ? 0 : n;
	}
	pr(fmt("- Casualties -\n\tYours: %d\n", a_cas));
	pr(fmt("\tTheirs: %d\n", d_cas));
	pr(fmt("Papershuffling ... %.1f B.T.U\n", (d_cas + a_cas) * 0.15));
	NAT_DELTA(nat_btu, cnum, (int) -((d_cas + a_cas) * 0.15 + 0.5));
	/* update military in defending sector */
	putvar(V_MILIT, o_mil, (char *)&dsect, EF_SECTOR);
	/* update plague in defending sector */
	if (plague && !getvar(V_PSTAGE, (char *)&dsect, EF_SECTOR))
			putvar(V_PSTAGE, PLG_EXPOSED, (char *)&dsect, EF_SECTOR);
	putsect(&dsect);
	return RET_OK;
}

static double
calcodds(nsect)
	int	nsect;
{
	register int n;
	double	att_str;
	double	def_str;
	double	odds;
	double	eff;

	/* Attacker strength */
	att_str = 0.0;
	for (n = 0; n < nsect; n++) {
		eff = unit[n].eff / 100.0;
		eff = (dchr[unit[n].des].d_ostr / 2.0 - 1.0) * eff + 1.0;
		att_str += (eff * unit[n].troops);
		unit[n].def = eff;
	}

	/* Defender strength */
	eff = unit[DEFENDER].eff / 100.0;
	eff = (dchr[unit[DEFENDER].des].d_dstr / 2.0 - 1.0) * eff + 1.0;
	def_str = (eff * unit[DEFENDER].troops);
	unit[DEFENDER].def = eff;

	/* calculate odds */
	if (att_str <= 0.0)
		odds = 0.0;
	else
		odds = att_str / (def_str + att_str);
	return odds;
}
