#ifndef lint
static char *RCSid = "$Header: /usr/brule/guest/empire/empire/emprcs/lib/subs/lndsub.c,v 2.20 1995/10/24 04:46:24 empire Exp $";
#endif /* not lint */

/*
 * lndsub.c
 *
 * land unit subroutines
 *
 */

#include <math.h>
#include "misc.h"
#include "player.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"
#include "land.h"
#include "ship.h"
#include "nsc.h"
#include "mission.h"
#include "plane.h"
#include "combat.h"
#include "damage.h"
#include "edb.h"

double lnd_mobcost();

attack_val(combat_mode, lp)
	int	combat_mode;
	struct	lndstr *lp;
{
	extern	double	assault_penalty;
	int	men;
	int	value;
	struct	lchrstr *lcp;

	if (((int)lp->lnd_effic) < LAND_MINEFF){
		lp->lnd_own=0;
		putland(lp->lnd_uid,lp);
		return 0;
	}

	lcp = &lchr[lp->lnd_type];

	men = total_mil(lp);

	value = ldround(((double)men*(double)lcp->l_att),1);

	switch (combat_mode) {
	case A_ATTACK:
		return value;
	case A_ASSAULT:
		if (!(lcp->l_flags & L_MARINE))
			return (int)(assault_penalty * value);
		break;
	case A_BOARD:
		if (!(lcp->l_flags & L_MARINE))
			return (int)(assault_penalty * men);
	}

	return value;
}

defense_val(lp)
struct	lndstr *lp;
{
	int	men;
	double	value;
	struct	lchrstr *lcp;

	if (((int)lp->lnd_effic) < LAND_MINEFF){
		lp->lnd_own=0;
		putland(lp->lnd_uid,lp);
		return 0;
	}

	lcp = &lchr[lp->lnd_type];

	men = total_mil(lp);

	if (lp->lnd_ship >= 0 && !(lcp->l_flags & L_MARINE))
		return men;

	value = men * lcp->l_def;
	value *= (127.0 + lp->lnd_harden) / 127.0;
	return ldround(value, 1);
}

total_mil(lp)
struct lndstr *lp;
{
	struct	lchrstr *lcp;
	double	men;

	lcp = &lchr[lp->lnd_type];

	men = lcp->l_mil;
	men *= ((double)lp->lnd_effic)/100.0;

	return ldround(men,1);
}

lnd_print(llp, s)
	struct	llist *llp;
	s_char *s;
{
	if (llp->land.lnd_own == player->cnum)
		pr("%s %s\n",prland(&llp->land), s);
	else
		wu(0, llp->land.lnd_own,
		   "%s %s\n",prland(&llp->land),s);
}

lnd_delete(llp, s)
	struct	llist *llp;
	s_char *s;
{
	if (s)
		lnd_print(llp, s);
	putland(llp->land.lnd_uid, &llp->land);
	remque((struct qelem *)llp);
	free((s_char *)llp);
}

lnd_take_casualty(combat_mode, llp, cas)
int	combat_mode;	/* attacking or assaulting or paratrooping? */
struct	llist *llp;
int	cas;		/* number of casualties to take */
{
	int	eff_eq;
	int	n;
	int	biggest;
	int	civs;
	int	nowned;
	coord	ret_x, ret_y;
	coord	bx, by;
	struct	sctstr sect;
	int	ret_chance;
	s_char	buf[1024];
	int	taken;
	int	nowhere_to_go = 0;
	struct sctstr rsect;
	double	mobcost;
	s_char	orig;
	int	mob;


	eff_eq = ldround((((double)cas*100.0) /
		(double)llp->lcp->l_mil),1);

	llp->land.lnd_effic -= eff_eq;

	if (llp->land.lnd_effic < LAND_MINEFF) {
		sprintf(buf, "dies %s %s!",
			combat_mode?att_mode[combat_mode]:(s_char*) "defending",
			xyas(llp->land.lnd_x, llp->land.lnd_y, llp->land.lnd_own));
		lnd_delete(llp, buf);
		return (((double)llp->land.lnd_effic/100.0)*
			(double)llp->lcp->l_mil);
	} else
		taken = cas;

	if (llp->land.lnd_effic >= llp->land.lnd_retreat)
		return taken;

	/* we're being boarded */
	if (llp->land.lnd_ship >= 0 && combat_mode == A_DEFEND)
		return taken;

	/* Have to make a retreat check */

	ret_chance = llp->land.lnd_retreat - llp->land.lnd_effic;
	if (roll(100) < ret_chance) {
		pr("\n");
		lnd_print(llp, "fails morale check!");
		llp->land.lnd_mission = 0;
		llp->land.lnd_harden = 0;
		if (llp->land.lnd_ship >= 0)
			nowhere_to_go = 1;
		else if (combat_mode == A_DEFEND) {
			/*
			 * defending unit.. find a place to send it
			 * strategy: look for the most-populated 
			 * adjacent sector that is owned by the unit
			 * player->owner. Charge mob..
			 */
			biggest = -1;
			nowned = 0;
			for(n = 1; n <= 6; ++n) {
				ret_x = llp->land.lnd_x + diroff[n][0];
				ret_y = llp->land.lnd_y + diroff[n][1];
				getsect(ret_x, ret_y, &sect);
				if (sect.sct_own != llp->land.lnd_own)
					continue;
				if (sect.sct_type == SCT_MOUNT)
					continue;
				++nowned;
				civs = getvar(V_CIVIL, (s_char *)&sect, EF_SECTOR);
				if (civs > biggest){
					biggest = civs;
					bx = sect.sct_x;
					by = sect.sct_y;
				}
			}
			if (!nowned)
				nowhere_to_go = 1;
			else {
				/* retreat to bx,by */
				llp->land.lnd_x = bx;
				llp->land.lnd_y = by;
                        	getsect(bx,by,&rsect);
				mobcost = lnd_mobcost(&llp->land, rsect.sct_type, rsect.sct_effic);
				mob = llp->land.lnd_mobil-(int)mobcost;
				if (mob < -127)
					mob = -127;
				orig = llp->land.lnd_mobil;
				llp->land.lnd_mobil = (s_char)mob;
				if (llp->land.lnd_mobil > orig)
					llp->land.lnd_mobil = (-127);
				sprintf(buf, "retreats at %d%% efficiency to %s!",
					llp->land.lnd_effic,
					xyas(bx,by,llp->land.lnd_own));
				lnd_delete(llp, buf);
			}
		} else { /* attacking from a sector */
			sprintf(buf, "leaves the battlefield at %d%% efficiency",
				llp->land.lnd_effic);
			lnd_delete(llp, buf);
		}
	}
	if (nowhere_to_go) {
		/* nowhere to go.. take more casualties */
		llp->land.lnd_effic -= 10;
		if (llp->land.lnd_effic < LAND_MINEFF)
			lnd_delete(llp, "has nowhere to retreat, and dies!");
		else
			lnd_print(llp, "has nowhere to retreat and takes extra losses!");
	}

	return taken;
}

lnd_takemob(list, loss)
	struct	qelem *list;
	double	loss;
{
	extern	double	combat_mob;
	struct	qelem *qp, *next;
	struct	llist *llp;
	int	new;
	int	mcost = ldround(combat_mob * loss, 1);

	for (qp = list->q_forw; qp != list; qp = next) {
		next = qp->q_forw;
		llp = (struct llist *)qp;
		if (chance(loss))
			use_supply(&llp->land);
		if (llp->land.lnd_mission == MI_RESERVE)
			new = llp->land.lnd_mobil - mcost/2;
		else
			new = llp->land.lnd_mobil - mcost;
		if (new < -127)
			new = -127;
		llp->land.lnd_mobil = (s_char)new;
	}
}

lnd_spyval(lp)
struct lndstr *lp;
{
	if (lchr[lp->lnd_type].l_flags & L_RECON)
		return (lchr[lp->lnd_type].l_spy * (lp->lnd_effic/100.0))+2;
	else
		return (lchr[lp->lnd_type].l_spy * (lp->lnd_effic/100.0));
}

int
intelligence_report(destination,lp,spy,mess)
struct	lndstr *lp;
int	spy;
s_char	*mess;
{
	struct	lchrstr *lcp;
	s_char	buf1[80],buf2[80],buf3[80];	
	double	estimate = 0.0;	/* estimated defense value */
	long	edv = 0;

	if (destination == 0)
		return 0;

	if (lp->lnd_own == 0)
		return 0;

	lcp = &lchr[lp->lnd_type];

	bzero(buf1,80);
	bzero(buf2,80);
	bzero(buf3,80);
	if (chance((double)(spy+lcp->l_vis)/10.0)){
		if (destination == player->cnum)
			pr("%s %s", mess, prland(lp));
		else
			sprintf(buf1,"%s %s", mess, prland(lp));
		edv |= EDV_COAS;

		estimate = lcp->l_mil;

		if (chance((double)(spy+lcp->l_vis)/20.0)){

			if (destination == player->cnum)
				pr(" (efficiency %d",lp->lnd_effic);
			else
				sprintf(buf2," (efficiency %d",lp->lnd_effic);
			edv |= EDV_EFF;
			estimate = lcp->l_mil * lp->lnd_effic / 100.0;

			if (chance((double)(spy+lcp->l_vis)/20.0)){
				int	t;
				t = lp->lnd_tech - 20 + roll(40);
				t = max(t,0);
				if (destination == player->cnum)
					pr(", tech %d)\n",t);
				else
					sprintf(buf3,", tech %d)\n",t);
				edv |= EDV_TECH;
			}else{
				if (destination == player->cnum)
					pr(")\n");
				else
					sprintf(buf3,")\n");
			}
		}else{
			if (destination == player->cnum)
				pr("\n");
			else
				sprintf(buf2,"\n");
		}	
	}

	if (destination != player->cnum){
		wu(0, destination, "%s%s%s",buf1,buf2,buf3);
	}
	if (edv)
		prsync(destination, lp, edv);

	if (lp->lnd_ship < 0 || lcp->l_flags & L_MARINE)
		estimate *= lcp->l_def;

	return (int)estimate;
}

count_sect_units(sp)
struct sctstr *sp;
{
	int	count = 0;
        struct  nstr_item ni;
        struct  lndstr land;

        snxtitem_all(&ni, EF_LAND);
        while (nxtitem(&ni, (s_char *)&land)) {
		if (!land.lnd_own)
			continue;
                if (land.lnd_x == sp->sct_x && land.lnd_y == sp->sct_y)
			++count;
	}

	return count;
}

void
count_units(sp)
struct shpstr *sp;
{
        struct  nstr_item ni;
        struct  lndstr land;

        if (sp->shp_effic < SHIP_MINEFF)
                return;

        sp->shp_nland=0;

        snxtitem_xy(&ni, EF_LAND, sp->shp_x, sp->shp_y);
        while (nxtitem(&ni, (s_char *)&land)){
                if (land.lnd_own == 0)
                	continue;
                if (land.lnd_ship == sp->shp_uid)
			sp->shp_nland++;
        }

        putship(sp->shp_uid,sp);
}

void
lnd_sel(ni, list/*, wantflags, nowantflags */)
	struct	nstr_item *ni;
	struct	qelem *list;
/*	int	wantflags;
	int	nowantflags;
*/
{
	struct	lndstr land;
	struct	lchrstr *lcp;
	struct	llist *llp;

	initque(list);
	while (nxtitem(ni, (s_char *)&land)) {
		if (!player->owner)
			continue;
		lcp = &lchr[land.lnd_type];
/*		if (wantflags && (lcp->m_flags & wantflags) != wantflags)
			continue;
		if (nowantflags && lcp->m_flags & nowantflags)
			continue;
*/
		land.lnd_mission = 0;
		land.lnd_rflags = 0;
		land.lnd_harden = 0;
		bzero(land.lnd_rpath,RET_LEN);
		putland(land.lnd_uid, &land);
		llp = (struct llist *) malloc(sizeof(struct llist));
		llp->lcp = lcp;
		bcopy((s_char *)&land, (s_char *)&llp->land,
			sizeof(struct lndstr));
		llp->mobil = (double)land.lnd_mobil;
		insque(&llp->queue, list);
	}
}

/* This function assumes that the list was created by lnd_sel */
void
lnd_mar(list, minmobp, maxmobp, togetherp, actor)
	struct	qelem *list;
	double	*minmobp;
	double	*maxmobp;
	int	*togetherp;
	natid	actor;
{
	extern int update_pending;
	struct	qelem *qp;
	struct	qelem *next;
	struct	llist *llp;
	struct	sctstr sect;
	struct	lndstr land;
	int	vec[I_MAX+1];
	coord   allx;
	coord   ally;
	int	first=1;
	s_char	mess[128];

	*minmobp = 9876.0;
	*maxmobp = -9876.0;
	*togetherp = 1;
	for (qp = list->q_back; qp != list; qp = next) {
		next = qp->q_back;
		llp = (struct llist *) qp;
		getland(llp->land.lnd_uid, &land);
		if (land.lnd_own != actor) {
			mpr(actor, "%s was disbanded at %s\n",
			    prland(&land),
			    xyas(land.lnd_x, land.lnd_y, land.lnd_own));
			remque((struct qelem *)llp);
			free((s_char *)llp);
			continue;
		}
		if (land.lnd_ship >= 0) {
			lnd_mess("is on a ship", llp);
			continue;
		}
		if (!getsect(land.lnd_x, land.lnd_y, &sect)) {
			lnd_mess("was sucked into the sky by a strange looking spaceland", llp); /* heh -KHS */
			continue;
		}
		if (sect.sct_own != land.lnd_own) {
			sprintf(mess, "has been kidnapped by %s", cname(sect.sct_own));
			lnd_mess(mess, llp);
			continue;
		}
		if (first){
			allx = land.lnd_x;
			ally = land.lnd_y;
			first = 0;
		}
		if (land.lnd_x != allx ||
		    land.lnd_y != ally)
			*togetherp = 0;
		if (land.lnd_mobil + 1 < (int)llp->mobil) {
			llp->mobil = (double)land.lnd_mobil;
		}
		if (llp->mobil < *minmobp)
			*minmobp = llp->mobil;
		if (llp->mobil > *maxmobp)
			*maxmobp = llp->mobil;
		bcopy((s_char *)&land, (s_char *)&llp->land,
			sizeof(struct lndstr));
	}
}

lnd_put(list, actor)
	struct	qelem *list;
	natid	actor;
{
	register struct qelem *qp;
	register struct qelem *newqp;
	struct	llist *llp;

	qp = list->q_back;
	while (qp != list) { 
		llp = (struct llist *) qp;
		if (actor) {
			mpr(actor, "%s stopped at %s\n", prland(&llp->land),
			    xyas(llp->land.lnd_x, llp->land.lnd_y, llp->land.lnd_own));
			if (llp->mobil < -127)
				llp->mobil = -127;
			llp->land.lnd_mobil = llp->mobil;
		}
		putland(llp->land.lnd_uid, &llp->land);
		newqp = qp->q_back;
		remque(qp);
		free((s_char *)qp);
		qp = newqp;
	}
}

void
lnd_sweep(land_list, verbose, takemob, actor)
	struct  qelem *land_list;
	int 	verbose;
	int	takemob;
	natid	actor;
{
	struct  qelem   *qp;
	struct  qelem   *next;
	struct  llist   *llp;
	struct  sctstr  sect;
	int	mines, m, max, sshells, lshells;
	double	mobcost;

        for (qp=land_list->q_back;qp!=land_list;qp=next) {
                next = qp->q_back;
                llp = (struct llist *) qp;
		if (!(llp->lcp->l_flags & L_ENGINEER)) {
			if (verbose)
				mpr(actor, "%s is not an engineer!\n",
				    prland(&llp->land));
			continue;
		}
		if (takemob && llp->mobil < 0.0) {
			if (verbose)
				lnd_mess("is out of mobility", llp);
			continue;
		}
		getsect(llp->land.lnd_x,llp->land.lnd_y,&sect);
		if (sect.sct_oldown == llp->land.lnd_own) {
			if (verbose)
				mpr(actor, "%s is in a sector completely owned by you.  Don't bother digging up mines there!\n",
				    prland(&llp->land));
			continue;
		}
		if (sect.sct_type == SCT_BSPAN) {
			if (verbose)
				mpr(actor, "%s is on a bridge.  No mines there!\n", prland(&llp->land));
			continue;
		}
		if (takemob) {
			mobcost = llp->land.lnd_effic * 0.01 * llp->lcp->l_spd;
			mobcost = 480.0 / (mobcost +
					   techfact(llp->land.lnd_tech,mobcost));
			llp->mobil -= mobcost;
			llp->land.lnd_mobil = (int)llp->mobil;
		}
		putland(llp->land.lnd_uid, &llp->land);
		if (!(mines=getvar(V_MINE,(s_char *)&sect,EF_SECTOR)))
			continue;
		max = vl_find(V_SHELL, llp->lcp->l_vtype,
			      llp->lcp->l_vamt, (int)llp->lcp->l_nv);
		lshells = getvar(V_SHELL, (s_char *)&llp->land, EF_LAND);
		sshells = getvar(V_SHELL, (s_char *)&sect, EF_SECTOR);
		for (m=0; mines > 0 && m < max * 2; m++) {
			if (chance(0.5 * llp->lcp->l_att)) {
				mpr(actor, "Sweep...\n");
				mines--;
				if (lshells < max)
					++lshells;
				else
					++sshells;
			}
		}
		putvar(V_MINE, mines, (s_char *)&sect, EF_SECTOR);
		putvar(V_SHELL, lshells, (s_char *)&llp->land, EF_LAND);
		putvar(V_SHELL, sshells, (s_char *)&sect, EF_SECTOR);
		putland(llp->land.lnd_uid, &llp->land);
		putsect(&sect);
	}
}

static int
contains_engineer(list)
	struct	qelem *list;
{
	struct  qelem   *qp;
	struct  qelem   *next;
	struct  llist   *llp;

        for (qp=list->q_back;qp!=list;qp=next) {
                next = qp->q_back;
                llp = (struct llist *) qp;
		if (llp->lcp->l_flags & L_ENGINEER)
			return 1;
	}
	return 0;
}

int
lnd_check_mines(land_list)
	struct  qelem *land_list;
{
	struct  qelem   *qp;
	struct  qelem   *next;
	struct  llist   *llp;
	struct  sctstr  sect;
	int	mines;
	int	stopping = 0;
	int	has_engineers = contains_engineer(land_list);

        for (qp=land_list->q_back;qp!=land_list;qp=next) {
                next = qp->q_back;
                llp = (struct llist *) qp;
		getsect(llp->land.lnd_x,llp->land.lnd_y,&sect);
		if (sect.sct_oldown == llp->land.lnd_own)
			continue;
		if (sect.sct_type == SCT_BSPAN)
			continue;
		if (!(mines=getvar(V_MINE,(s_char *)&sect,EF_SECTOR)))
			continue;
		if (chance(DMINE_LHITCHANCE(mines)/(1 + 2*has_engineers))) {
			lnd_hit_mine(&llp->land, llp->lcp);
			mines--;
			putvar(V_MINE,mines,(s_char *)&sect,EF_SECTOR);
			putsect(&sect);
			putland(llp->land.lnd_uid, (s_char *)&llp->land);
			if (!llp->land.lnd_own) {
				stopping = 1;
				remque(qp);
				free((s_char *)qp);
			}
		}
	}
	return stopping;
}

int
lnd_list(land_list)
	struct  qelem *land_list;
{
	struct  qelem   *qp;
	struct  qelem   *next;
	struct  llist   *llp;
	struct	lndstr	*lnd;
	int	vec[I_MAX+1];

	pr("lnd#     land type       x,y    a  eff  sh gun xl  mu tech retr fuel\n");

        for (qp=land_list->q_back;qp!=land_list;qp=next) {
                next = qp->q_back;
                llp = (struct llist *) qp;
		lnd = &llp->land;
		pr("%4d ", lnd->lnd_uid);
		pr("%-16.16s ", llp->lcp->l_name);
		prxy("%4d,%-4d ", lnd->lnd_x, lnd->lnd_y, llp->land.lnd_own);
		pr("%1c", lnd->lnd_army);
		pr("%4d%%", lnd->lnd_effic);
		getvec(VT_ITEM, vec, (s_char *)lnd, EF_LAND);
		pr("%4d", vec[I_SHELL]);
		pr("%4d", vec[I_GUN]);
		count_land_planes(lnd);
		pr("%3d",lnd->lnd_nxlight);
		pr("%4d", lnd->lnd_mobil);
		pr("%4d", lnd->lnd_tech);
		pr("%4d%%", lnd->lnd_retreat);
		pr("%5d\n", lnd->lnd_fuel);
	}
}

void 
lnd_mess(str, llp)
	s_char  *str;
	struct	llist *llp;
{
	mpr(llp->land.lnd_own, "%s %s & stays in %s\n",
	    prland(&llp->land),
	    str, xyas(llp->land.lnd_x, llp->land.lnd_y, llp->land.lnd_own));
	if (llp->mobil < -127)
		llp->mobil = -127;
	llp->land.lnd_mobil = llp->mobil;
	putland(llp->land.lnd_uid, &llp->land);
	remque((struct qelem *)llp);
	free((s_char *)llp);
}

static int
lnd_count(list)
	struct	qelem	*list;
{
	struct	qelem *qp;
	struct	qelem *next;
	struct	llist *llp;
	int	count = 0;

	for (qp = list->q_back; qp != list; qp = next) {
		next = qp->q_back;
		llp = (struct llist *) qp;
		++count;
	}
	return count;
}

static int
lnd_damage(list, totdam)
	struct	qelem	*list;
	int	totdam;
{
	struct	qelem *qp;
	struct	qelem *next;
	struct	llist *llp;
	int	dam;
	int	count;

	if (!totdam || !(count = lnd_count(list)))
		return 0;
	dam = ldround(((double)totdam/(double)count),1);
	for (qp = list->q_back; qp != list; qp = next) {
		next = qp->q_back;
		llp = (struct llist *) qp;
		/* have to get it again because of collateral damage */
		getland(llp->land.lnd_uid, &llp->land);
		landdamage(&llp->land, dam);
		putland(llp->land.lnd_uid, &llp->land);
		if (!llp->land.lnd_own) {
			remque(qp);
			free((s_char *)qp);
		}
	}
	return dam;
}

static int lnd_easiest_target(list)
	struct	qelem	*list;
{
	struct	qelem *qp;
	struct	qelem *next;
	struct	llist *llp;
	int	hard;
	int	easiest = 9876; /* things start great for victim */
	int	count = 0;

	for (qp = list->q_back; qp != list; qp = next) {
		next = qp->q_back;
		llp = (struct llist *) qp;
		hard = lnd_hardtarget(&llp->land);
		if (hard < easiest)
			easiest = hard; /* things get worse for victim */
		++count;
	}
	return easiest - count;
}

static int
lnd_missile_interdiction(list, newx, newy, victim)
	struct	qelem *list;
	coord	newx, newy;
	natid	victim;
{
        int     dam;
	struct	qelem msl_list, *qp, *newqp;

	msl_sel(&msl_list, newx, newy, victim, P_T, P_MAR, MI_INTERDICT);

	dam = msl_launch_mindam(&msl_list, newx, newy,
				lnd_easiest_target(list), EF_LAND, 
				lnd_count(list) * 20, "troops", victim,
				MI_INTERDICT);
	if (dam) {
		mpr(victim, "missile interdiction mission does %d damage!\n", dam);
		collateral_damage(newx, newy, dam);
	}
	qp = msl_list.q_forw;
	while(qp != msl_list.q_forw) {
		newqp=qp->q_forw;
		remque(qp);
		free(qp);
		qp = newqp;
	}
	return dam;
}

int
lnd_interdict(list, newx, newy, victim)
	struct	qelem *list;
	coord	newx, newy;
	natid	victim;
{
	int stopping = 0;

	stopping |= lnd_damage(list, unit_interdict(newx,newy,victim,"land units", lnd_easiest_target(list), MI_INTERDICT));

	stopping |= lnd_damage(list, lnd_missile_interdiction(list,newx,newy,victim));
	return stopping;
}

/* high value of hardtarget is harder to hit */
int lnd_hardtarget(lp)
	struct lndstr *lp;
{
	struct	sctstr	sect;
	struct	lchrstr *lcp = lchr + lp->lnd_type;

	return (int)(((double)lp->lnd_effic/100.0) *
		(10 + dchr[sect.sct_type].d_dstr * 2 +
		 (double)lcp->l_spd/2.0 - lcp->l_vis));
}

lnd_hit_mine(lp, lcp)
	struct lndstr *lp;
	struct	lchrstr *lcp;
{
	double	m;

	mpr(lp->lnd_own, "Blammo! Landmines detected in %s! ",
		xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));

	nreport(lp->lnd_own, N_LHIT_MINE, 0, 1);

	m = roll(20) + 10;
	if (lcp->l_flags & L_ENGINEER)
		m /= 2.0;

	landdamage(lp, ldround(m,1));

	return (int)m;
}

double
lnd_mobcost(lp, des, eff)
	struct	lndstr *lp;
	int	des;
	int	eff;
{
	double	mobcost;

	mobcost = lp->lnd_effic * 0.01 * lchr[lp->lnd_type].l_spd;
	if (mobcost < 0.01)
		mobcost = 0.01;
	mobcost = sector_mcost(des, eff) * 5.0 * 480.0 /
		  (mobcost + techfact(lp->lnd_tech, mobcost));
	return mobcost;
}

lnd_mar_one_sector(list, dir, actor, together)
	struct	qelem *list;
	int	dir;
	natid	actor;
	int	together;
{
	struct	sctstr	sect, osect;
	struct  qelem   *qp;
	struct  qelem   *next;
	struct  llist   *llp;
	coord	dx;
	coord	dy;
	coord   newx;
	coord   newy;
	int	stopping = 0;
	s_char	dp[80];

	if (dir <= DIR_STOP || dir >= DIR_VIEW) {
		lnd_put(list, actor);
		return 1;
	}
	dx = diroff[dir][0];
	dy = diroff[dir][1];
	for (qp=list->q_back;qp!=list;qp=next) {
		next = qp->q_back;
		llp = (struct llist *)qp;
		getsect(llp->land.lnd_x, llp->land.lnd_y, &osect);
		newx = xnorm(llp->land.lnd_x + dx);
		newy = ynorm(llp->land.lnd_y + dy);
		getsect(newx, newy, &sect);
		if (sect.sct_own != actor) {
			if (together) {
				pr("can't go to %s\n",xyas(newx, newy, actor));
				return 1;
			} else {
				sprintf(dp, "can't go to %s",
					xyas(newx, newy, actor));
				lnd_mess(dp, llp);
				continue;
			}
		}
		if (llp->mobil <= 0.0) {
			lnd_mess("is out of mobility", llp);
			continue;
		}
		llp->land.lnd_x = newx;
		llp->land.lnd_y = newy;
		llp->mobil -= lnd_mobcost(&llp->land, sect.sct_type, sect.sct_effic);;
		llp->land.lnd_mobil = (int)llp->mobil;
		putland(llp->land.lnd_uid, &llp->land);
		putsect(&osect);
		if (osect.sct_own != player->cnum) {
			pr("You no longer own %s\n",
			   xyas(osect.sct_x, osect.sct_y, player->cnum));
		}
	}
	if (QEMPTY(list))
		return stopping;
	lnd_sweep(list, 0, 1, actor);
	stopping |= lnd_check_mines(list);
	if (QEMPTY(list))
		return stopping;
	stopping |= lnd_interdict(list, newx, newy, actor);
	return stopping;
}

/*
 * find all artillery units belonging
 * to the attacker or defender that can fire.
 * Each arty unit adds +1%/damage point
 *
 */
int
lnd_support(victim, attacker, x, y)
	natid	victim, attacker;
	coord	x,y;
{
	struct	nstr_item ni;
	struct	lndstr	land;	
	int	rel, rel2;
	double	dam = 0.0;
	int	dist;
	double	range, range2, landunitgun();
	
	snxtitem_all(&ni,EF_LAND);
	while(nxtitem(&ni,(s_char *)&land)){
		if (lchr[land.lnd_type].l_frg == 0)
			continue;
		if ((land.lnd_x == x) && (land.lnd_y == y))
			continue;
		if (land.lnd_ship >= 0)
			continue;
		if (land.lnd_mission > 0)
			continue;
		if (land.lnd_effic < LAND_MINFIREEFF)
			continue;
		rel = getrel(getnatp(land.lnd_own),attacker);
		rel2 = getrel(getnatp(land.lnd_own),victim);
		if ((land.lnd_own != attacker) &&
		    ((rel != ALLIED) || (rel2 != AT_WAR)))
			continue;

		/* do we have supplies? */
		if (!has_supply(&land))
			continue;

		/* are we in range? */
		dist=mapdist(land.lnd_x,land.lnd_y,x,y);

		range=techfact((int)land.lnd_tech,
			       (double)lchr[land.lnd_type].l_frg);

		range2=roundrange(range);
		if (dist>range2)
			continue;

		use_supply(&land);
		nreport(land.lnd_own,N_FIRE_L_ATTACK,victim,1);
		if (roll(100) > lchr[land.lnd_type].l_acc){
			dam += landunitgun(land.lnd_type,land.lnd_effic) / 2;
		} else {
			dam += landunitgun(land.lnd_type,land.lnd_effic);
		}
		if (land.lnd_own != attacker)
			wu(0,land.lnd_own,
			   "%s supported %s at %s\n",
			   prland(&land),
			   cname(attacker),xyas(x,y,land.lnd_own));
	}
	return (int)dam;
}

s_char *
lnd_path(together, lp, buf)
	int	together;
	struct	lndstr	*lp;
	s_char	*buf;
{
	coord   destx;
	coord   desty;
	struct	sctstr d_sect, sect;
	s_char	*cp;
	double	dummy;
	    
	if (!sarg_xy(buf, &destx, &desty))
		return 0;
	if (!together){
		pr("Cannot go to a destination sector if not all starting in the same sector\n");
		return 0;
	}
	if (!getsect(destx, desty, &d_sect)) {
		pr("%d,%d is not a sector\n", destx, desty);
		return 0;
	}
	getsect(lp->lnd_x, lp->lnd_y, &sect);
	cp = (s_char *)BestLandPath(buf, &sect, &d_sect, &dummy);
	if (!cp) {
		pr("No owned path from %s to %s!\n",
		   xyas(lp->lnd_x,lp->lnd_y,player->cnum),
		   xyas(d_sect.sct_x,d_sect.sct_y,player->cnum));
		return 0;
	}
	pr("Using path '%s'\n",cp);
	return cp;
}

int
lnd_can_attack(lp)
	struct	lndstr	*lp;
{
	struct	lchrstr *lcp = &lchr[lp->lnd_type];
		
	if (lcp->l_flags & L_SUPPLY ||
	    lcp->l_flags & L_SECURITY ||
	    lcp->l_flags & L_FLAK ||
	    lcp->l_frg)
		return 0;

	return 1;	
}
