#ifndef lint
static char *RCSid = "$Header: /usr/brule/guest/empire/empire/emprcs/lib/update/revolt.c,v 2.4 1995/10/24 04:44:26 empire Exp $";
#endif

/*
 * revolt.c
 *
 * have disloyal populace revolt!
 *
 * Dave Pare, 1986
 */

#include "misc.h"
#include "var.h"
#include "sect.h"
#include "nat.h"
#include "news.h"
#include "var.h"
#include "file.h"
#include "path.h"
#include "xy.h"
#include "land.h"
#include "nsc.h"
#include "plane.h"

#define	get_che_cnum(x)		((x) >> 8)
#define set_che_cnum(x, cn)	((x) = ((x) & 0xff) | ((cn) << 8))
#define get_che_value(x)	((x) & 0xff)
#define set_che_value(x, n)	((x) = ((x) & 0xff00) | (n))

#define	CHE_MAX			255

void take_casualties();

void
revolt(sp)
	struct	sctstr *sp;
{
	int	che_civ;
	int	che_uw;
	int	civ;
	int	uw;
	u_short	che_combo;
	int	che;
	int	n;
	int	target;

	che_combo = getvar(V_CHE, (s_char *)sp, EF_SECTOR);
	che = get_che_value(che_combo);
	target = get_che_cnum(che_combo);
	if (che_combo != 0 && (target != sp->sct_own || che >= CHE_MAX))
		return;
	civ = getvar(V_CIVIL, (s_char *)sp, EF_SECTOR);
	uw = getvar(V_UW, (s_char *)sp, EF_SECTOR);
	if (che > (civ + uw) * 3)
		return;
	che_uw = 0;
	che_civ = 0;
	/* che due to civilian unrest */
	n = 10 - (random() % 20);
	che_civ = 3 + (civ * n/500);
	if (che_civ < 0)
		che_civ = 0;
	else if (che_civ * 3 > civ)
		che_civ = civ / 3;
	if (che + che_civ > CHE_MAX)
		che_civ = CHE_MAX - che;
	che += che_civ;
	if (che < CHE_MAX) {
		/* che due to uw unrest */
		n = 10 + (random() % 30);
		che_uw = 5 + (uw * n/500);
		if (che_uw > uw)
			che_uw = uw;
		if (che + che_uw > CHE_MAX)
			che_uw = CHE_MAX - che_uw;
		che += che_uw;
	}
	if (che_civ + che_uw > 0) {
		civ -= che_civ;
		uw -= che_uw;
		set_che_cnum(che_combo, sp->sct_own);
		set_che_value(che_combo, che);
		putvar(V_CHE, (int)che_combo, (s_char *)sp, EF_SECTOR);
		if (che_civ > 0)
			putvar(V_CIVIL, civ, (s_char *)sp, EF_SECTOR);
		if (che_uw > 0)
			putvar(V_UW, uw, (s_char *)sp, EF_SECTOR);
#ifdef DEBUG
		logerror("(#%d) %d che fired up in %s",
			sp->sct_own, che, ownxy(sp));
#endif
	}
}

/*
 * summary of effects.
 * if there are no military in the sector, che recruit from
 *   populace if pop loyalty is > 10.  They spread subversion otherwise,
 *   trying to lower pop loyalty.
 * if che outnumber military, they stay and shoot it out, kill the
 *   military.
 * if che are outnumbered by less than 5 to 1, they blow up stuff,
 *   killing innocent civilians (never uw's) and damaging commodities.
 * if che are outnumbered by more than 5 to 1, they try to leave the
 *   sector for a nearby sector with fewer military.
 *
 * if the military lose any attacks, the pop loyalty in the sector
 *   gets worse, representing military defeat.
 * military can "catch" che's after bombing attacks, or after they move.
 *   If military catch them, then they get to shoot it out with a portion
 *   of the che's depending on the # of mil in the sector.  Chance to contact
 *   is around 10% per every equal number of mil:che ratio in the sector.
 *   "contact" is by 20% of the military in the sector, and odds are equal.
 *
 * Without a doubt this routine should be broken up, if only for readabilty.
 */
void
guerrilla(sp)
	struct	sctstr *sp;
{
	extern	s_char *effadv();
	struct	sctstr *nsp;
	int	recruit;
	int	move;
	int	ratio;
	int	che;
	int	mil;
	int	cc, mc;
	double	odds;
	int	civ;
	int	n;
	int	uw;
	natid	target;
	struct	natstr *tnat;
	int	convert;
	natid	actor;
	natid	victim;
	u_short	che_combo;
	int	vec[I_MAX+1];
	int	tmp;
	int	min_mil;
	int	val;
	struct	lndstr *lp;
	s_char	*nxtitemp();
	struct	nstr_item ni;

	mc = cc = 0;
	recruit = 0;
	convert = 0;
	move = 0;
	if ((n = getvar(V_CHE, (s_char *)sp, EF_SECTOR)) <= 0)
		return;
	che_combo = n;
	if (getvec(VT_ITEM, vec, (s_char *)sp, EF_SECTOR) <= 0)
		return;
	civ = vec[I_CIVIL];

	uw = vec[I_UW];
	victim = sp->sct_own;
	actor = sp->sct_oldown;
	che = get_che_value(che_combo);

	mil = vec[I_MILIT];
	snxtitem_xy(&ni, EF_LAND, sp->sct_x,sp->sct_y);
	while (lp=(struct lndstr *)nxtitemp(&ni, 0)){
		if (lp->lnd_own != sp->sct_own)
			continue;

		mil += ((float)(lchr[lp->lnd_type].l_mil*lp->lnd_effic)/100.0);

		/* Security troops are 2x as effective */
		if (lchr[lp->lnd_type].l_flags & L_SECURITY){
			int	che_kill, r;
			struct lchrstr *lcp;

			lcp = &lchr[lp->lnd_type];
			mil += ((float)(lcp->l_mil * lp->lnd_effic)/100.0);
			r = ((lcp->l_mil* lp->lnd_effic)/500.0);
			if (r == 0)
				r = 1;
			che_kill = roll(r);
			wu(0, sp->sct_own, 
			   "%s kills %d guerrilla%s in raid at %s!\n",
			   prland(lp),
			   che_kill, splur(che_kill),
			   ownxy(sp));
			che -= che_kill;
		}
	}

	/* Security forces killed all the che */
	if (che == 0)
		return;

	target = get_che_cnum(che_combo);
	if (target == 0) {
		/* the deity can't be a target! */
		return;
	}
	tnat = getnatp(target);
	if ((tnat->nat_stat & STAT_INUSE) == 0) {
		/* target nation has dissolved: che's retire.  */
		logerror("%d Che targeted at country %d retiring", che, target);
		civ += che;
		putvar(V_CHE, 0, (s_char *)sp, EF_SECTOR);
		putvar(V_CIVIL, civ, (s_char *)sp, EF_SECTOR);
		return;
	}
	if (sp->sct_own != target) {
		/*logerror("own %d != target %d", sp->sct_own, target);*/
		move++;
		goto domove;
	}
	ratio = mil / che;
	odds = (double) che / (mil+che);
	if (mil == 0) {
		wu(0, sp->sct_own, "Revolutionary subversion reported in %s!\n",
			ownxy(sp));
		recruit++;
		convert++;
	} else if (che > mil && mil > 0) {
		/*logerror("guerrilla shootout with military");*/
		/*
		 * shoot it out with the military, and kill them off.
		 * If loyalty bad enough, then take the sector over,
		 * and enlist 5% of civ as military force.
		 */
		while (che > 0 && mil > 0) {
			if (chance(odds)) {
				mc++;
				mil--;
			} else {
				cc++;
				che--;
			}
		}
		if (mil > 0) {
			/* military won.  */
			n = sp->sct_loyal - (random() % 15);
			if (n < 0)
				n = 0;
			sp->sct_loyal = n;
			/*logerror("(#%d) mil beat che in %s", sp->sct_own,*/
				/*ownxy(sp));*/
		} else {
			convert++;
			recruit++;
			/*logerror("(#%d) che beat mil in %s", sp->sct_own,*/
				/*ownxy(sp));*/
		}
		take_casualties(sp,mc);
	} else if (ratio < 5) {
		/*
		 * guerrillas have to resort to blowing things up.
		 * Note this disrupts work in the sector.
		 */
		n = 0;
		n = (random() % 10) + (random() % che);
		if (n > 100)
			n = 100;
		tmp = sp->sct_work - n;
		if (tmp < 0)
			tmp = 0;
		sp->sct_work = tmp;
		wu(0, sp->sct_own,
			"Production %s disrupted by terrorists in %s\n",
				effadv(n), ownxy(sp));
		tmp = sp->sct_effic - n/10;
		if (tmp < 0)
			tmp = 0;
		sp->sct_effic = tmp;
		/*logerror("(#%d) che blew up %s for %d", sp->sct_own,*/
			/*ownxy(sp), n);*/
		recruit++;
	} else {
		/* ratio >= 5 */
		/*logerror("(#%d) %d che fleeing %d mil in %s", sp->sct_own,*/
			/*che, mil, ownxy(sp));*/
		move++;
	}
	if (mil > 0 && che > 0) {
		/*
		 * we only get here if we haven't had combat previously.
		 * Chance to catch them.
		 * 20% of mil involved in attacking the che's.
		 */
		if (chance(ratio*0.10)) {
			n = (mil/5) + 1;
			if ((n+che) == 0){
				logerror("n=%d che=%d\n",n,che);
				if (che == 0)
					return;
			}
			odds = (double) che / (n + che);
			while (che > 0 && n > 0) {
				if (chance(odds)) {
					mc++;
					n--;
				} else {
					cc++;
					che--;
				}
			}
			take_casualties(sp,mc);
			recruit = 0;
			/*logerror("Caught che; mc: %d, cc: %d", cc, mc);*/
		}
	}
	if (convert && sp->sct_loyal >= 50) {
		struct plnstr *pp;
		register int n;
		/* che won, and sector converts. */
		if (sp->sct_own == sp->sct_oldown)
			sp->sct_oldown = 0;
		else
			takeover(sp, sp->sct_oldown);
		civ += uw;
		uw = 0;
		/*
		 * so we can't keep losing money by having
		 * our cap retaken
		 */
		sp->sct_own = sp->sct_oldown;
		if (sp->sct_type == SCT_CAPIT &&
		    sp->sct_newtype == SCT_CAPIT)
			sp->sct_newtype = SCT_AGRI;
		n = civ / 20;
		civ -= n;
		putvar(V_CIVIL, civ, (s_char *)sp, EF_SECTOR);
		putvar(V_UW, uw, (s_char *)sp, EF_SECTOR);
		putvar(V_MILIT, n, (s_char *)sp, EF_SECTOR);
		move++;
		recruit = 0;
		if (sp->sct_own)
			wu(0, sp->sct_own, "Sector %s has been retaken!\n",
				xyas(sp->sct_x, sp->sct_y, sp->sct_own));

		for (n=0; pp = getplanep(n); n++) {
			if (pp->pln_x != sp->sct_x)
				continue;
			if (pp->pln_y != sp->sct_y)
				continue;
			if (pp->pln_own == 0)
				continue;
			if (pp->pln_flags & PLN_LAUNCHED)
				continue;
			pp->pln_own = sp->sct_own;
			if (sp->sct_own)
				wu(0,sp->sct_own,
				   "\t%s captured by guerillas!\n",
				   prplane(pp));
		}
	}
	if (recruit && che > 0) {
		/* loyalty drops during recruitment efforts */
		n = sp->sct_loyal;
		if (n < 30)
			n += (random() % 5) + 1;
		else if (n < 70)
			n += (random() % 10) + 4;
		if (n > 127)
			n = 127;
		sp->sct_loyal = n;
		if (sp->sct_oldown != sp->sct_own || n > 100) {
			n = civ * (random() % 3) / 200;
			if (n + che > CHE_MAX)
				n = CHE_MAX - che;
			che += n;
			civ -= n;
			putvar(V_CIVIL, civ, (s_char *)sp, EF_SECTOR);
		}
		n = uw * (random() % 3) / 200;
		if (n + che > CHE_MAX)
			n = CHE_MAX - che;
		che += n;
		uw -= n;
		putvar(V_UW, uw, (s_char *)sp, EF_SECTOR);
	}
domove:
	if (move && che > 0) {
		struct sctstr *maybe_sp = 0;
		if (convert)
			min_mil = 999;
		else
			min_mil = mil;
		for (n=1; n<=6; n++) {
			nsp = getsectp(sp->sct_x+diroff[n][0],
				sp->sct_y+diroff[n][1]);
			if (dchr[nsp->sct_type].d_mcst == 0)
				continue;
			if (nsp->sct_own != target)
				continue;
			if ((val = getvar(V_CHE, (s_char *)nsp, EF_SECTOR)) > 0) {
				che_combo = val;
				if (get_che_cnum(che_combo) != target)
					continue;
				if (get_che_value(che_combo) + che > CHE_MAX)
					continue;
			}
			val = getvar(V_MILIT, (s_char *)nsp, EF_SECTOR);
			if (val >= min_mil)
				continue;
			maybe_sp = nsp;
			min_mil = val;
		}
		/*
		 * if n <= 6, we found a sector owned by TARGET which
		 * is a nice sector.  Otherwise, we move to the first
		 * one we find ("maybe_sp").
		 */
		if (maybe_sp != 0) {
			che_combo = getvar(V_CHE, (s_char *)nsp, EF_SECTOR);
			che += get_che_value(che_combo);
			set_che_value(che_combo, che);
			set_che_cnum(che_combo, target);
			putvar(V_CHE, (int) che_combo, (s_char *)nsp, EF_SECTOR);
			che = 0;
		}
	}
	if (che > 0) {
		set_che_value(che_combo, che);
		set_che_cnum(che_combo, target);
		putvar(V_CHE, (int) che_combo, (s_char *)sp, EF_SECTOR);
	} else
		putvar(V_CHE, 0, (s_char *)sp, EF_SECTOR);
	if (mc > 0 || cc > 0) {
		/* don't tell who won just to be mean */
		wu(0, target,
			"Guerrilla warfare in %s\n",
			xyas(sp->sct_x, sp->sct_y, target));
		wu(0, target, "  body count: troops: %d, rebels: %d\n", mc, cc);
		nreport(actor, N_FREEDOM_FIGHT, victim, 1);
	}
}

void
take_casualties(sp, mc)
struct	sctstr *sp;
int	mc;
{
	int	orig_mil;
	int	cantake;
	int	nunits=0, each, deq;
	struct	lndstr *lp;
	s_char	*nxtitemp();
	struct	nstr_item ni;

	/* casualties come out of mil first */
	orig_mil = getvar(V_MILIT, (s_char *)sp, EF_SECTOR);

	if (mc <= orig_mil){
		putvar(V_MILIT, (orig_mil-mc), (s_char *)sp, EF_SECTOR);
		return;
	}
	putvar(V_MILIT, 0, (s_char *)sp, EF_SECTOR);

	/* remaining casualites */
	mc -= orig_mil;

	/*
	 * Need to take total_casualties and divide
	 * them amongst the land units in the sector
	 * Do security troops first, then others.
	 * Try not to kill any unit.
	 */
	snxtitem_xy(&ni, EF_LAND, sp->sct_x,sp->sct_y);
	while(lp=(struct lndstr *)nxtitemp(&ni, 0)){
		nunits++;
		if (lchr[lp->lnd_type].l_flags & L_SECURITY)
			nunits++;
	}

	if (nunits==0)
		return;

	each = (mc/nunits)+2;

	/* kill some security troops */
	snxtitem_xy(&ni, EF_LAND, sp->sct_x,sp->sct_y);
	while(lp=(struct lndstr *)nxtitemp(&ni, 0)){
		if (!(lchr[lp->lnd_type].l_flags & L_SECURITY))
			continue;

		cantake = (((float)(lp->lnd_effic-40)/100.0)*
				(float)lchr[lp->lnd_type].l_mil)*2;

		if (cantake >= each){
			deq = (((float)each/(float)(lchr[lp->lnd_type].l_mil*2))
				*100.0);
			mc -= each;
		}else if (cantake > 0){
			deq = (((float)cantake/
				(float)(lchr[lp->lnd_type].l_mil*2)) * 100.0);
			mc -= (((float)deq/100.0)*
				(float)lchr[lp->lnd_type].l_mil)*2;
		}else
			deq = 0;

		lp->lnd_effic -= deq;
		lp->lnd_mobil -= deq/2;
		if (mc<=0) return;
	}

	/* kill some normal troops */
	snxtitem_xy(&ni, EF_LAND, sp->sct_x,sp->sct_y);
	while(lp=(struct lndstr *)nxtitemp(&ni, 0)){
		if (lchr[lp->lnd_type].l_flags & L_SECURITY)
			continue;

		cantake = (((float)(lp->lnd_effic-40)/100.0)*
				(float)lchr[lp->lnd_type].l_mil);

		if (cantake >= each){
			deq = (((float)each/(float)(lchr[lp->lnd_type].l_mil*2))
				*100.0);
			mc -= each;
		}else if (cantake > 0){
			deq = (((float)cantake/(float)lchr[lp->lnd_type].l_mil)
				* 100.0);
			mc -= (((float)deq/100.0)*
				(float)lchr[lp->lnd_type].l_mil);
		}else
			deq = 0;
		lp->lnd_effic -= deq;
		lp->lnd_mobil -= deq/2;
		if (mc<=0) return;
	}

	/* Hmm.. still some left.. kill off units now */
	/* kill some normal troops */
	snxtitem_xy(&ni, EF_LAND, sp->sct_x,sp->sct_y);
	while(lp=(struct lndstr *)nxtitemp(&ni, 0)){
		if (lchr[lp->lnd_type].l_flags & L_SECURITY)
			continue;

		mc -= (((float)lp->lnd_effic/100.0) *
			(float)lchr[lp->lnd_type].l_mil);
		lp->lnd_effic = 0;
		wu(0,lp->lnd_own,"%s dies fighting guerrillas in %s\n",
		   prland(lp),
		   xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
		lp->lnd_own = 0;
		if (mc<=0) return;
	}

	/* Hmm.. still some left.. kill off units now */
	/* kill some security troops */
	snxtitem_xy(&ni, EF_LAND, sp->sct_x,sp->sct_y);
	while(lp=(struct lndstr *)nxtitemp(&ni, 0)){
		if (!(lchr[lp->lnd_type].l_flags & L_SECURITY))
			continue;

		mc -= (((float)lp->lnd_effic/100.0) *
			(float)lchr[lp->lnd_type].l_mil)*2;
		lp->lnd_effic = 0;
		wu(0,lp->lnd_own,"%s dies fighting guerrillas in %s\n",
		   prland(lp),
		   xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
		lp->lnd_own = 0;
		if (mc<=0) return;
	}

	/* Hmm.. everyone dead.. too bad */
}
