#ifndef lint
static char *RCSid = "$Header: /sequent2/empire/EMP/update/RCS/sect.c,v 1.11 89/09/18 00:55:24 mr-frog Exp $";
#endif

/*
 * sect.c
 *
 * perform production updates of the sect and
 * ship files.
 * 
 * from PSL Empire, 1985
 * and Dave Pare, 1986
 */

#include <math.h>
#include "misc.h"
#include "var.h"
#include "sect.h"
#include "nat.h"
#include "item.h"
#include "news.h"
#include "file.h"
#include "xy.h"
#ifdef	FALLOUT
#include "path.h"
#endif	FALLOUT
#include "product.h"

extern	float levels[MAXNOC][4];

prod_sect(etu)
	int	etu;
{
	extern	int pops[];
	register struct sctstr *sp;
	struct	natstr *np;
	int	n;
	int	vec[I_MAX+1];
	int	changed;

	bzero((char *)levels, sizeof(levels));
	/*
	 * first produce everything, then do deliveries
	 * and distributes
	 */
	for (n=0; sp = getsectid(n); n++) {
#ifdef	FALLOUT
	        do_fallout(sp,etu);
#endif	FALLOUT
		if (sp->sct_type == SCT_WATER)
			continue;
		np = getnatp(sp->sct_own);
		guerrilla(sp);
		update(sp, etu, np, &pops[sp->sct_own]);
	}
	for (n=0; sp = getsectid(n); n++) {
		if (sp->sct_type == SCT_WATER)
			continue;
		np = getnatp(sp->sct_own);
		if (np->nat_money < 0)
			continue;
		changed = 0;
		if (getvec(VT_ITEM, vec, (char *)sp, EF_SECTOR) > 0)
			changed += dodeliver(sp, vec);
		changed += dodistribute(sp, vec);
		if (changed)
			putvec(VT_ITEM, vec, (char *)sp, EF_SECTOR);
	}
}

/*
 * update the individual sector
 *
 */
update(sp, etus, np, pop)
	register struct sctstr *sp;
	register int etus;
	register struct natstr *np;
	long	*pop;
{
	extern	double populace();
	extern	double eatrate;
	extern	double bankint;
	int	cvec[I_MAX+1];
	int	vec[I_MAX+1];
	int	people;
	int	work;
	int	n;
	int	starved;

	if (getvec(VT_ITEM, vec, (char *)sp, EF_SECTOR) <= 0)
		return;
	/* If everybody is dead, the sector reverts to unowned. 
	 * This is also checked at the end of the production in
	 * they all starved or were plagued off.
	 */
	if (vec[I_CIVIL] == 0 && vec[I_MILIT] == 0) {
		sp->sct_own = 0;
		sp->sct_oldown = 0;
		return;
	}
	/* calculate the workforce */
	work = roundavg((etus * populace(np, sp, vec, etus)) / 100.0);
	if ((sp->sct_effic < 100 || sp->sct_type != sp->sct_newtype) &&
	    np->nat_money > 0) {
		n = buildeff(sp, work/2);
		np->nat_money -= n;
		work -= n;
	}
	getvec(VT_COND, cvec, (char *)sp, EF_SECTOR);
	people = vec[I_CIVIL] + vec[I_MILIT] + vec[I_UW];
	if (sp->sct_type != SCT_SANCT) {
#ifndef	NOFOOD
		if (vec[I_FOOD] < 1 + etus * people * eatrate) {
			/* need to grow "emergency rations" */ 
			work -= growfood(sp, vec, work, etus);
		}
#endif	NOFOOD
		starved = feed_people(vec, etus);
		if (starved > 0 && sp->sct_own) {
			/* don't report POGO starvation */
			wu(0, sp->sct_own, fmt("%d starved in %s.\n", starved,
				xyas(sp->sct_x, sp->sct_y, sp->sct_own)));
			if (starved > 25)
				nreport(sp->sct_own, N_DIE_FAMINE, 0, 1);
		}
		if (starved > 0)
			starvation(sp);
		else {
			if (sp->sct_work < 100)
				sp->sct_work += 8 + (random() % 15);
			if (sp->sct_work > 100)
				sp->sct_work = 100;
			grow_people(vec, etus);
		}
	} else
		sp->sct_work = 100;
	if (cvec[C_PSTAGE] == 0) {
		cvec[C_PSTAGE] = infect_people(np, vec, sp->sct_effic,
			(int)sp->sct_mobil);
		cvec[C_PTIME] = 0;
	} else {
		n = plague_people(np, vec, cvec, etus);
		switch (n) {
		case PLG_DYING:
			wu(0, sp->sct_own,
				fmt("PLAGUE deaths reported in %s.\n",
				ownxy(sp)));
			nreport(sp->sct_own, N_DIE_PLAGUE, 0, 1);
			break;
		case PLG_INFECT:
			wu(0, sp->sct_own, fmt("%s battling PLAGUE\n",
				 ownxy(sp)));
			break;
		case PLG_INCUBATE:
			/* plage has moved into "infectious" stage */
			if (n != cvec[C_PSTAGE]) {
				wu(0, sp->sct_own,
					fmt("Outbreak of PLAGUE in %s!\n",
					ownxy(sp)));
				nreport(sp->sct_own, N_OUT_PLAGUE, 0, 1);
			}
			break;
		case PLG_EXPOSED:
		default:
			break;
		}
	}
	if (sp->sct_type == SCT_ENLIST && sp->sct_effic >= 60 &&
	    sp->sct_own == sp->sct_oldown)
		np->nat_money -= enlist(vec, etus) * 3;
	/*
	 * now do the production (if sector effic > 60%)
	 */
	if (sp->sct_effic > 60) {
		if (sp->sct_type == SCT_BANK) {
			np->nat_money += vec[I_BAR] * etus * bankint;
		} else if (sp->sct_type == SCT_CAPIT) {
			/* 
			 * XXX produce btu's here?
			 */
			np->nat_money -= etus;
		}
		if (np->nat_money > 0 && dchr[sp->sct_type].d_prd)
			work -= produce(np, sp, vec, work);
	}
	if (vec[I_CIVIL] == 0 && vec[I_MILIT] == 0) {
		sp->sct_own = 0;
		sp->sct_oldown = 0;
	}
	/*
	 * only non-captured civs add to census for nation
	 */
	if (sp->sct_oldown == sp->sct_own)
		*pop += vec[I_CIVIL];
	putvec(VT_ITEM, vec, (char *)sp, EF_SECTOR);
	putvec(VT_COND, cvec, (char *)sp, EF_SECTOR);
	sp->sct_avail = work;
}

int
dodeliver(sp, vec)
	struct	sctstr *sp;
	int	*vec;
{
	register int i;
	int	del[I_MAX+1];
	int	thresh;
	int	dir;
	int	plague;
	int	n;
	int	changed;

	if (sp->sct_mobil <= 0)
		return 0;
	if (getvec(VT_DEL, del, (char *)sp, EF_SECTOR) <= 0)
		return 0;
	changed = 0;
	plague = getvar(V_PTIME, (char *)sp, EF_SECTOR);
	for (i=1; i<=I_MAX; i++) {
		if (del[i] == 0)
			continue;
		thresh = del[i] & ~0x7;
		dir = del[i] & 0x7;
		n = deliver(sp, &ichr[i], dir, thresh, vec[i], plague);
		if (n > 0)  {
			vec[i] -= n;
			changed++;
			if (sp->sct_mobil <= 0)
				break;
		}
	}
	return changed;
}

/*
 * Increase sector efficiency if old type == new type.
 * decrease sector efficiency if old type != new type.
 * Return amount of work used.
 */
int
buildeff(sp, work)
	register struct sctstr *sp;
	int	work;
{
	register int work_cost;
	int	total_cost;
	int	n;

	work_cost = 0;
	total_cost = 0;
	if (sp->sct_type != sp->sct_newtype) {
		/*
		 * Tear down existing sector.
		 * Easier to destroy than to build.
		 */
		work_cost = (sp->sct_effic + 3) / 4;
		if (work_cost > work)
			work_cost = work;
		n = sp->sct_effic - work_cost * 4;
		if (n <= 0) {
			n = 0;
			sp->sct_type = sp->sct_newtype;
		}
		sp->sct_effic = n;
		work -= work_cost;
		total_cost += work_cost;
	}
	if (sp->sct_type == sp->sct_newtype) {
		work_cost = 100 - sp->sct_effic;
		if (work_cost > work)
			work_cost = work;
		sp->sct_effic += work_cost;
		total_cost += work_cost;
	}
	return total_cost;
}

int
growfood(sp, vec, work, etus)
	struct	sctstr *sp;
	register int *vec;
	int	work;
	int	etus;
{
	extern	double fgrate;
	extern	double fcrate;
	double	food_fertil;
	double	food_workers;
	double	food;
	int	work_used;

/* I'm being very nice and commenting out this so players
 * won't whine about starvation
	if (sp->sct_fertil == 0 || work == 0)
		return 0;
 */
	food_workers = work * fcrate;
	food_fertil = etus * sp->sct_fertil * fgrate;
	food = food_fertil;
	if (food > food_workers)
		food = food_workers;
	/*
	 * be nice; grow minimum one food unit.
	 * This makes life simpler for the player.
	 */
	vec[I_FOOD] += (int) food;
	if (vec[I_FOOD] == 0)
		vec[I_FOOD] = 1;
	if (vec[I_FOOD] > 999)
		vec[I_FOOD] = 999;
	work_used = (int) food / fcrate;
	return work_used;
}

/*
 * enlistment sectors are special; they require military
 * to convert civ into mil in large numbers.
 * Conversion will happen much more slowly without
 * some mil initially.
 */
int
enlist(vec, etus)
	register int *vec;
	int	etus;
{
	int	maxmil;
	int	enlisted;

	enlisted = 0;
	maxmil = (vec[I_CIVIL] / 2) - vec[I_MILIT];
	if (maxmil > 0) {
		enlisted = (etus * (10 + vec[I_MILIT]) * 0.05);
		if (enlisted > maxmil)
			enlisted = maxmil;
		vec[I_CIVIL] -= enlisted;
		vec[I_MILIT] += enlisted;
	}
	return enlisted;
}

#ifdef	FALLOUT
/* Fallout is calculated here. */

extern int melt_item_denom[];

int do_fallout(sp,etus)
	register struct sctstr *sp;
	register int etus;

{
  extern	double decay_per_etu,fallout_spread;
  int	vec[I_MAX+1];
  int	cvec[I_MAX+1];
  int	melt,n; /* radiation doesn't really melt people :) */
  int	decay;

  getvec(VT_ITEM, vec, (char *)sp, EF_SECTOR);
  getvec(VT_COND, cvec, (char *)sp, EF_SECTOR);

  if (cvec[C_FALLOUT]) {
    for (n=1; n<=I_MAX; n++)
      {
	melt = roundavg(vec[n]*etus*(long)cvec[C_FALLOUT] /
			(1000.0*melt_item_denom[n]));
	if (melt>5)
	  wu(0,sp->sct_own, fmt("Lost %d %s to radiation in %s.\n",
				melt,ichr[n].i_name,ownxy(sp)));
	if (melt<vec[n])
	  vec[n] -= melt;
	else
	  vec[n] = 0;
      }
  }
  for (n=DIR_FIRST; n<=DIR_LAST; n++)
    {
      struct sctstr *ap;
      static int vec2[I_MAX+1];
      ap = getsectp(sp->sct_x+diroff[n][0],sp->sct_y+diroff[n][1]);
      getvec(VT_COND,vec2,ap,EF_SECTOR);
      {
	register int	inc;
	inc = roundavg(etus*fallout_spread*vec2[C_FALLOUT]);
#if 0
	if (vec2[C_FALLOUT])
	  {
	    wu(0,0,fmt("Fallout from sector %d,%d to %d,%d is %d=%d*%e*%d\n",
		       sp->sct_x,sp->sct_y,sp->sct_x+diroff[n][0],
		       sp->sct_y+diroff[n][1],inc,etus,
		       fallout_spread,vec2[C_FALLOUT]));
	  }
#endif
	cvec[C_FALLOUT] += inc;
      }
    }

  decay = roundavg((decay_per_etu+6*fallout_spread)*etus*cvec[C_FALLOUT]);
#if 0
  if (decay>cvec[C_FALLOUT])
    wu(0,0,fmt("Fallout underflow in sector %s.\n\t(library routine error, situation under control)\n",ownxy(sp)));
#endif
  cvec[C_FALLOUT] = (decay<cvec[C_FALLOUT]) ? cvec[C_FALLOUT]-decay : 0;

  putvec(VT_ITEM, vec, (char *)sp, EF_SECTOR);
  putvec(VT_COND, cvec, (char *)sp, EF_SECTOR);
#ifdef	GODZILLA
  if ((cvec[C_FALLOUT] > 20) && chance(100))
	do_godzilla(sp);
#endif	GODZILLA
}
#endif	FALLOUT

#ifdef BUDGET

#define SHOULD_PRODUCE(sp,t)	(((sp->sct_type == t) || (t == -1)) ? 1 : 0)

/*
 * Produce only a set sector type for a specific nation
 * (or all, if sector_type == -1)
 *
 */
produce_sect(natnum,etu,sector_type)
	int	natnum;
	int	etu, sector_type;
{
	extern	int pops[];
	register struct sctstr *sp;
	register struct natstr *np;
	extern	double populace();
	extern	double eatrate;
	extern	double bankint;
	int	vec[I_MAX+1];
	int	people;
	int	work;
	int	n, spam;
	int	starved;

	for (n=0; sp = getsectid(n); n++) {
		if (sp->sct_type == SCT_WATER)
			continue;
		if (sp->sct_own != natnum)
			continue;
		if (sp->sct_updated != 0)
			continue;
		if (!SHOULD_PRODUCE(sp,sector_type))
			continue;
		np = getnatp(natnum);
		if (np->nat_money < 0)
			continue;
		if ((np->nat_priorities[sp->sct_type] == 0) &&
			((pchr[dchr[sp->sct_type].d_prd].p_cost != 0) ||
			(sp->sct_type ==SCT_ENLIST))){
				logerror("Skipping %s production for country %s\n",dchr[sp->sct_type].d_name,np->nat_cnam);
                                continue;
		}

		if (getvec(VT_ITEM, vec, (char *)sp, EF_SECTOR) <= 0)
			continue;
		/* If everybody is dead, the sector reverts to unowned. 
	 	* This is also checked at the end of the production in
	 	* they all starved or were plagued off.
	 	*/
		if (vec[I_CIVIL] == 0 && vec[I_MILIT] == 0) {
			sp->sct_own = 0;
			sp->sct_oldown = 0;
			continue;
		}

		sp->sct_updated = 1;

		/* calculate the workforce */
		work = (vec[I_CIVIL] * sp->sct_work) / 100.0
		      +(vec[I_MILIT] * 2 / 5.0) + vec[I_UW];
		work = roundavg((etu * work) / 100.0);
		if ((sp->sct_effic < 100 || sp->sct_type != sp->sct_newtype) &&
			np->nat_money > 0) {
			spam = buildeff(sp, work/2);
			np->nat_money -= spam;
			work -= spam;
		}
		if (sp->sct_type == SCT_ENLIST && sp->sct_effic >= 60 &&
			sp->sct_own == sp->sct_oldown)
			np->nat_money -= enlist(vec, etu) * 3;

		/*
	 	* now do the production (if sector effic > 60%)
	 	*/
		if (sp->sct_effic > 60){
		if (np->nat_money > 0 && dchr[sp->sct_type].d_prd)
			work -= produce(np, sp, vec, work);
		}

		putvec(VT_ITEM, vec, (char *)sp, EF_SECTOR);
		sp->sct_avail = work;
	}
}
#endif /* BUDGET */
