#ifndef lint
static char *RCSid = "$Header: /usr/brule/guest/empire/empire/emprcs/lib/update/land.c,v 2.11 1995/10/24 04:34:49 empire Exp $";
#endif

/*
 * land.c
 *
 * do "production" for land units -- make more efficient,
 * charge for military, etc.
 *
 * from PSL, 1985
 * and Dave Pare, 1986
 * and ts, 1993
 */

#include "misc.h"
#include "var.h"
#include "sect.h"
#include "nat.h"
#include "land.h"
#include "ship.h"
#include "var.h"
#include "news.h"
#include "file.h"
#include "product.h"
#include "optlist.h"
#include "budg.h"

extern int update_pending;
int mil_dbl_pay;

#ifndef MIN
#define MIN(x,y)       ((x) > (y) ? (y) : (x))
#endif

void upd_land();

int
prod_land(etus,natnum,bp,build)
int	etus;
int	natnum;
int     *bp;
int	build;	/* build = 1, maintain = 0 */
{
	register struct lndstr *lp;
	struct	sctstr *sp;
	struct	natstr *np;
	int	n, k=0;
	extern	long lnd_money[MAXNOC];
	int	start_money;

	for (n=0; lp = getlandp(n); n++) {
		if (lp->lnd_own == 0)
			continue;
		if (lp->lnd_own != natnum)
			continue;

		sp = getsectp(lp->lnd_x,lp->lnd_y);
		if (sp->sct_type == SCT_SANCT)
			continue;
		np = getnatp(lp->lnd_own);
		start_money = np->nat_money;
		upd_land(lp, n, etus, np, bp, build);
		lnd_money[lp->lnd_own] += np->nat_money - start_money;
		if ((build && (np->nat_money == start_money)) || (!build))
		    k++;
		if (!update_pending)
		  np->nat_money = start_money;
        }
	return k;
}

void
upd_land(lp, landno, etus, np, bp, build)
register struct lndstr *lp;
int	landno;
register int etus;
struct	natstr *np;
int     *bp;
int	build; /* build = 1, maintain = 0 */
{
	extern	long pops[];
	extern  double money_mil;
	struct	lchrstr *lcp;
	int	vec[I_MAX+1];
	int	cvec[I_MAX+1];
	int	n;
	double	techfact();
	int	min = 75-(int)np->nat_level[NAT_HLEV];
	int	mult;
	extern	double money_land;
	int	needed;
	int	cost;
	int	eff;

	if (lp->lnd_retreat < min)
		lp->lnd_retreat = min;

	lcp = &lchr[lp->lnd_type];
	getvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
	if (build == 1){
		if (np->nat_priorities[PRI_LBUILD] == 0 ||
		    np->nat_money < 0)
			return;
		if (lp->lnd_effic < LAND_MINEFF ||
			!(landrepair(lp,vec,np,bp,etus))){
			lp->lnd_own = 0;
			return;
		}
	}else{
		mult = 1;
		if (np->nat_level[NAT_TLEV] < lp->lnd_tech * 0.85)
			mult = 2;
		cost = -(mult * etus * dmin(0.0, money_land * lcp->l_cost));
		if ((np->nat_priorities[PRI_LMAINT] == 0 ||
		    np->nat_money < cost) && update_pending) {
			if ((eff = lp->lnd_effic - etus/5) < LAND_MINEFF) {
				wu(0, lp->lnd_own,
				   "%s lost to lack of maintenance\n",
				   prland(lp));
				lp->lnd_own = 0;
				return;
			}
			wu(0, lp->lnd_own,
			   "%s lost %d%% to lack of maintenance\n",
			   prland(lp), lp->lnd_effic - eff);
			lp->lnd_effic = eff;
		} else {
			np->nat_money -= cost;
		}
		np->nat_money += (int) (money_mil * etus * lcp->l_mil *
					lp->lnd_effic/100.0);

		/* Grab more stuff */
		if ((opt_NOFOOD == 0) && update_pending)
			resupply_commod(lp,I_FOOD);

		getvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
		if (update_pending) {
		   if ((n = feed_land(lp, vec, etus, &needed)) > 0) {
			wu(0, lp->lnd_own, "%d starved in %s%s\n",
				n, prland(lp),
				(lp->lnd_effic < LAND_MINEFF ?
				", killing it" : ""));
			if (n > 10)
				nreport(lp->lnd_own, N_DIE_FAMINE, 0, 1);
		   }
		   /*
		    * do plague stuff.  plague can't break out on land units,
		    * but it can still kill people.
		    */
		   getvec(VT_COND, cvec, (s_char *)lp, EF_LAND);
		   if (cvec[C_PSTAGE] > 0) {
			n = plague_land(lp, np, vec, cvec, etus);
			switch (n) {
				case PLG_DYING:
					wu(0, lp->lnd_own,
						"PLAGUE deaths reported in %s\n", prland(lp));
					nreport(lp->lnd_own,N_DIE_PLAGUE,0,1);
					break;
				case PLG_EXPOSED:
					wu(0, lp->lnd_own, "%s battling PLAGUE\n", prland(lp));
					break;
				case PLG_INCUBATE:
					if (n == cvec[C_PSTAGE])
						break;
					wu(0, lp->lnd_own,
					   "Outbreak of PLAGUE in %s!\n",
					   prland(lp));
					nreport(lp->lnd_own,N_OUT_PLAGUE,0,1);
					break;
				default:
					break;
			}
			putvec(VT_COND, cvec, (s_char *)lp, EF_LAND);
		   }
		   putvec(VT_ITEM, vec, (s_char *)lp, EF_LAND);
	        } /* end update_pending */
	}
}

/*ARGSUSED*/
int
landrepair(land, vec, np, bp, etus)
	register struct lndstr *land;
	int	*vec;
	struct	natstr *np;
        int     *bp;
	int	etus;
{
        extern  int mil_dbl_pay;
	extern	int land_grow_scale;
	extern	double money_land;
	register int delta;
	struct	sctstr *sp;
	struct	lchrstr *lp;
	float	leftp, buildp;
	int	left, build;
	int	mil, lcm, hcm, gun, shell;
	int     omil, olcm, ohcm, ogun, oshell;
	int	keepmil, keeplcm, keephcm, keepgun, keepshell;
	int	mil_needed, lcm_needed, hcm_needed, gun_needed, shell_needed;
	int	avail;
	int	w_p_eff;
	int	mult;
	int	svec[I_MAX+1];
	int     mvec[I_MAX+1];

	lp = &lchr[land->lnd_type];
	sp = getsectp(land->lnd_x, land->lnd_y);
	if (sp->sct_off)
		return 1;
	getvec(VT_ITEM, svec, (s_char *)sp, EF_SECTOR);
	mult = 1;
	if (np->nat_level[NAT_TLEV] < land->lnd_tech * 0.85)
		mult = 2;

	if (land->lnd_effic == 100) {
		/* land is ok; no repairs needed */
		return 1;
	}
	if (sp->sct_own != land->lnd_own)
		return 1;

	if (update_pending)
	   avail = sp->sct_avail * 100;
	else
	   avail = gt_bg_nmbr(bp, sp, I_MAX+1) * 100;
	
	w_p_eff = 20 + (lp->l_lcm + 2 * lp->l_hcm);
	delta = roundavg((double)avail/w_p_eff);
	if (delta <= 0)
		return 1;
	if (delta > etus*land_grow_scale)
		delta = etus*land_grow_scale;

	/* delta is the max amount we can grow */

        left = 100 - land->lnd_effic;
	if (left > delta)
		left = delta;

        leftp = ((float)left/100.0);

	bzero(mvec, sizeof(mvec));
        mvec[I_MILIT] = mil_needed = ldround((double)(lp->l_mil * leftp),1);
        mvec[I_LCM] = lcm_needed = ldround((double)(lp->l_lcm * leftp),1);
        mvec[I_HCM] = hcm_needed = ldround((double)(lp->l_hcm * leftp),1);
        mvec[I_GUN] = gun_needed = ldround((double)(lp->l_gun * leftp),1);
        mvec[I_SHELL] = shell_needed = ldround((double)(lp->l_shell * leftp),1);

	get_materials(sp, bp, mvec, 0);

        if (mvec[I_MILIT]>=mil_needed)
                buildp=leftp;
        else
                buildp=((float)mvec[I_MILIT]/(float)lp->l_mil);
        if (mvec[I_LCM] < lcm_needed)
                buildp = MIN(buildp,((float)mvec[I_LCM]/(float)lp->l_lcm));
        if (mvec[I_HCM] < hcm_needed)
                buildp = MIN(buildp,((float)mvec[I_HCM]/(float)lp->l_hcm));
        if (mvec[I_GUN] < gun_needed)
		buildp = MIN(buildp,((float)mvec[I_GUN]/(float)lp->l_gun));
        if (mvec[I_SHELL] < shell_needed)
                buildp = MIN(buildp,((float)mvec[I_SHELL]/(float)lp->l_shell));

	build=ldround((double)(buildp*100.0),1);

	bzero(mvec, sizeof(mvec));
        mvec[I_MILIT] = mil_needed = roundavg((double)(lp->l_mil * buildp));
        mvec[I_LCM] = lcm_needed = roundavg((double)(lp->l_lcm * buildp));
        mvec[I_HCM] = hcm_needed = roundavg((double)(lp->l_hcm * buildp));
        mvec[I_GUN] = gun_needed = roundavg((double)(lp->l_gun * buildp));
        mvec[I_SHELL] = shell_needed = roundavg((double)(lp->l_shell * buildp));
	mil_dbl_pay += mil_needed;

	get_materials(sp, bp, mvec, 1);

	if ((sp->sct_type != SCT_HEADQ) && (sp->sct_type != SCT_FORTR))
		build /= 3;

	avail -= build * w_p_eff;
	if (update_pending) {
	   sp->sct_avail = avail/100;
	   if (sp->sct_avail < 0)
	      sp->sct_avail = 0;
	} else {
	   pt_bg_nmbr(bp, sp, I_MAX+1, avail/100);
	   if (gt_bg_nmbr(bp, sp, I_MAX+1) < 0)
		pt_bg_nmbr(bp, sp, I_MAX+1, 0);
	}

if (build<0)
	logerror("land unit %d building %d ! \n",land->lnd_uid,build);
	np->nat_money -= mult * lp->l_cost * build / 100.0;
	if (update_pending) {
	   land->lnd_effic += (s_char)build;

	   putsect(sp);
	}
	return 1;
}

/*
 * returns the number who starved, if any.
 */
int
feed_land(lp, vec, etus, needed)
	struct lndstr *lp;
	register int *vec;
	int	etus;
	int	*needed;
{
	extern	double eatrate;
	double	food_eaten, ship_eaten;
	double	people_left;
	int	can_eat, need, svec[I_MAX+1];
	int	total_people;
	int	starved;
	struct	lchrstr *lcp;
	struct	shpstr *sp;

	if (opt_NOFOOD) return 0; /* no food no work to be done */

	lcp = &lchr[lp->lnd_type];

	food_eaten = (etus * eatrate) * total_mil(lp);
	starved = 0;
	*needed = 0;
	/*
	 * If we're on a ship, and we don't have enough food,
	 * get some food off the carrying ship. (Don't starve
	 * the ship, tho...
	 */
	if ((food_eaten > vec[I_FOOD]) && (lp->lnd_ship >= 0)){
		need = (int)food_eaten - vec[I_FOOD];
		sp = getshipp(lp->lnd_ship);
		getvec(VT_ITEM, svec, (s_char *)sp, EF_SHIP);
		ship_eaten = (etus * eatrate) *
			(svec[I_CIVIL]+svec[I_MILIT]+svec[I_UW]);
		if ((svec[I_FOOD]-need) > ship_eaten){
			vec[I_FOOD] += need;
			svec[I_FOOD] -= need;
		}
		else if ((svec[I_FOOD]-ship_eaten) > 0){
			vec[I_FOOD] += (svec[I_FOOD] - ship_eaten);
			svec[I_FOOD] -= (svec[I_FOOD] - ship_eaten);
		}
		putvec(VT_ITEM, svec, (s_char *)sp, EF_SHIP);
	}

	if (food_eaten > vec[I_FOOD]) {
		*needed = food_eaten - vec[I_FOOD];
		if (*needed < (food_eaten - vec[I_FOOD]))
			(*needed)++;
		can_eat = (vec[I_FOOD] / (etus * eatrate));
		total_people = total_mil(lp);
		/* only want to starve off at most 1/2 the populace. */
		if (can_eat < (total_people/2))
			can_eat = total_people/2;

		people_left = (vec[I_FOOD] + 0.01) / (food_eaten + 0.01);
		starved = total_mil(lp);
		/* only want to starve off at most 1/2 the populace. */
		if (people_left < 0.5)
			people_left = 0.5;
		lp->lnd_effic *= people_left;
		starved -= total_mil(lp);
		if (update_pending)
			wu(0, lp->lnd_own, "%s lost %d%% to starvation\n",
			   prland(lp),
			   lcp->l_mil?(100 * starved / lcp->l_mil):starved);
		vec[I_FOOD] = 0;
	} else {
		vec[I_FOOD] -= (int)food_eaten;
	}
	return starved;
}

/*
 * Given the fact that plague exists, kill off
 * people if in plague state DYING.  Increment
 * the plague time.  Return "current" plague
 * stage.  No reports generated here anymore.
 */
int
plague_land(lp, np, vec, cvec, etus)
	struct	lndstr *lp;
	struct	natstr *np;
	register int *vec;
	register int *cvec;
	int	etus;
{
	int	stage;
	double	plg_num;
	double	plg_denom;
	double	pct_left;

	cvec[C_PTIME] -= etus;
	stage = cvec[C_PSTAGE];
	switch (stage) {
	case PLG_DYING:
		plg_num = 100.0 * etus;
		plg_denom = (np->nat_level[NAT_RLEV] + 100.0) *
			(vec[C_PTIME] + etus + 1.0);
		pct_left = 1.0 - plg_num / plg_denom;
		if (pct_left < 0.2)
			pct_left = 0.2;
		lp->lnd_effic *= pct_left;
		break;
	case PLG_INFECT:
	case PLG_INCUBATE:
		break;
	case PLG_EXPOSED:
		cvec[C_PTIME] = 0;
		break;
	default:
		/* bad */
		cvec[C_PTIME] = 0;
		break;
	}
	if (cvec[C_PTIME] <= 0) {
		cvec[C_PSTAGE]--;
		cvec[C_PTIME] = 32 + (random() % 32);
	}
	return stage;
}
