#ifndef lint
static char *RCSid = "$Header: /usr/brule/guest/empire/empire/emprcs/lib/commands/torp.c,v 2.14 1995/10/25 20:08:30 empire Exp $";
#endif /* not lint */

/*
 * torp.c
 *
 * fire torpedoes at enemy shipping.
 *
 * from PSL Empire, 1985
 */

#include <stdio.h>
#include "misc.h"
#include "player.h"
#include "var.h"
#include "ship.h"
#include "file.h"
#include "xy.h"
#include "nat.h"
#include "nsc.h"
#include "news.h"
#include "retreat.h"
#include "damage.h"

void anti_torp();
void fire_dchrg();

s_char *prsub();
 torp()
{
	extern	int torpedo_damage;
	natid	vshipown;
	int	range;
	int	dam;
	int	shells;
	int	subno;
	int	victno;
	double	erange;
	double	hitchance;
	struct	shpstr vship;
	struct	shpstr sub;
	s_char 	*ptr;
        double	mobcost;
	struct	mchrstr *mcp;
	struct	nstr_item nbst;
	s_char	buf[1024];
	s_char 	*sav;
	int	ntorping=0;
	s_char	prompt[128];

	if (!(sav = getstarg(player->argp[1], "From ship(s)? ", buf)))
		return RET_SYN;
        if (!snxtitem(&nbst, EF_SHIP, sav))
                return RET_SYN;
	while (nxtitem(&nbst, (s_char *)&sub)){
		if (sub.shp_own != player->cnum)
			continue;
		if ((mchr[sub.shp_type].m_flags & M_TORP) == 0)
			continue;
		shells = getvar(V_SHELL, (s_char *)&sub, EF_SHIP);
		if (shells < 3)
			shells += supply_commod(sub.shp_own,sub.shp_x,sub.shp_y,I_SHELL,
				3-shells);
		if (getvar(V_GUN, (s_char *)&sub, EF_SHIP) == 0 || shells < 3)
			continue;
		if (getvar(V_MILIT, (s_char *)&sub, EF_SHIP) < 1)
			continue;
		if (sub.shp_effic < 60)
			continue;
		if (sub.shp_mobil <= 0)
			continue;
		ntorping++;
	}
	pr("%d ships are eligible to torp\n",ntorping);
	snxtitem(&nbst, EF_SHIP, sav);
	while (nxtitem(&nbst, (s_char *)&sub)) {
		if (!sub.shp_own)
			continue;
		if (sub.shp_own != player->cnum) {
			continue;
		}
		if ((mchr[sub.shp_type].m_flags & M_TORP) == 0) {
			pr("Ship # %d: A %s can't fire torpedoes!\n", sub.shp_uid,
			       mchr[sub.shp_type].m_name);
			continue;
		}
		shells = getvar(V_SHELL, (s_char *)&sub, EF_SHIP);
		if (shells < 3)
			shells += supply_commod(sub.shp_own,sub.shp_x,sub.shp_y,I_SHELL,
				3-shells);
		if (getvar(V_GUN, (s_char *)&sub, EF_SHIP) == 0 || shells < 3) {
			pr("Ship #%d has insufficient armament\n",sub.shp_uid);
			continue;
		}
		if (getvar(V_MILIT, (s_char *)&sub, EF_SHIP) < 1){
			pr("Ship #%d has insufficient crew\n",sub.shp_uid);
			continue;
		}
		if (sub.shp_effic < 60) {
			pr("Ship #%d torpedo tubes inoperative.\n",sub.shp_uid);
			continue;
		}
		if (sub.shp_mobil <= 0) {
			pr("Ship #%d has insufficient mobility\n",sub.shp_uid);
			continue;
		}
		bzero(buf,80);
		subno=sub.shp_uid;
		sprintf(prompt, "Ship %d, target? ",sub.shp_uid);
		if ((ptr = getstarg(player->argp[2],prompt, buf)) == 0)
			return RET_SYN;
		if ((victno = atoi(ptr)) < 0)
			return RET_SYN;
		if (!getship(victno, &vship))
			return RET_FAIL;
		if (!vship.shp_own)
			return RET_FAIL;
		vshipown=vship.shp_own;
		if (victno == subno) {
			pr("Shooting yourself, eh?  How strange...\n");
			continue;
		}
		if (mchr[vship.shp_type].m_flags & M_SUB) {
			if (!(mchr[sub.shp_type].m_flags & M_SUBT)){
				pr("You can't torpedo a submarine!\n");
				continue;
			}
		}
		if ((mchr[sub.shp_type].m_flags & M_SUB) == 0)
			anti_torp(sub.shp_uid,ntorping,vshipown);
		getship(sub.shp_uid,&sub);
		if (sub.shp_own == 0){
			continue;
		}
		erange = ((double)sub.shp_effic/100.0) *
			techfact(sub.shp_tech, ((double)mchr[sub.shp_type].m_frnge));
		erange = (double)roundrange(erange);
		pr("Effective torpedo range is %.1f\n", erange);
		shells -= 3;
		putvar(V_SHELL, shells, (s_char *)&sub, EF_SHIP);
		putship(sub.shp_uid,&sub);
		mcp = &mchr[sub.shp_type];
		mobcost = sub.shp_effic * 0.01 * mcp->m_speed;
		mobcost = (480.0 / (mobcost + techfact(sub.shp_tech, mobcost)));

		/* Mob cost for a torp is equal to the cost of 1/2 sector of movement */
		mobcost /= 2.0;
		sub.shp_mobil -= mobcost;
		pr("Whooosh... ");
		getship(victno, &vship);
		vshipown = vship.shp_own;
		range = mapdist(sub.shp_x, sub.shp_y, vship.shp_x, vship.shp_y);
		hitchance = DTORP_HITCHANCE(range, mchr[sub.shp_type].m_visib);
		if (range <= erange) {
			pr("Hitchance = %d%%\n",(int)(hitchance*100));
		}
		if (range > erange) {
			pr("Out of range\n");
		} else if (hitchance>=1.0 || chance(hitchance)) {
			pr("BOOM!...\n");
			dam = TORP_DAMAGE();
			if(vshipown != 0)
			wu(0, vshipown, "%s in %s torpedoed %s for %d damage.\n", prsub(&sub),
			   xyas(sub.shp_x, sub.shp_y, vshipown),
			   prship(&vship),
			   dam);
			if (vship.shp_rflags & RET_TORPED){
				retreat_ship(&vship, 't');
				shipdamage(&vship, dam);
			}else
				shipdamage(&vship, dam);
			pr("Torpedo hit %s for %d damage.\n",
			   prship(&vship),
			   dam);

			if (vship.shp_effic < SHIP_MINEFF)
				pr("%s sunk!\n",
				   prship(&vship));
			putship(victno, &vship);
			if (mchr[sub.shp_type].m_flags & M_SUB)
				nreport(vshipown, N_TORP_SHIP, 0, 1);
			else
				nreport(vshipown, N_SHIP_TORP, player->cnum, 1);
		} else {
			pr("Missed\n");
			if(vshipown != 0)
			wu(0, vshipown, "Torpedo sighted @ %s by %s\n",
			   xyas(sub.shp_x, sub.shp_y, vshipown),
			   prship(&vship));
		}
		sub.shp_mission=0;
		putship(subno, &sub);
		if (mchr[sub.shp_type].m_flags & M_SUB)
			anti_torp(sub.shp_uid,ntorping,vshipown);
	}
	return RET_OK;
}

void
anti_torp(f,ntorping,vshipown)
int	f,ntorping;
int	vshipown;
{
	int	range;
	double	erange;
	struct	shpstr sub;
	struct	shpstr dd;
	int	x;

	getship(f,&sub);

	if (sub.shp_own == vshipown)
		return;

	if ((mchr[sub.shp_type].m_flags & M_SUB) == 0)
		pr("Starting our attack run...\n");

	x=0;
	while (getship(x++,&dd) && sub.shp_effic >= SHIP_MINEFF){
		if (dd.shp_own == 0)
			continue;
		if (dd.shp_own != vshipown)
			continue;

		if (!canshoot(&dd,&sub))
			continue;

		erange = techfact(dd.shp_tech,
			((double)mchr[dd.shp_type].m_frnge))/ 2.0;

		erange = (double)roundrange(erange);

		range = mapdist(sub.shp_x, sub.shp_y, dd.shp_x, dd.shp_y);

		if (range > erange)
			continue;

		if (cantorp(&dd,&sub)){
			/* Try torping.. if we can, maybe we can fire */
			if (!fire_torp(&dd,&sub,range,ntorping))
				if (candchrg(&dd,&sub))
					fire_dchrg(&dd,&sub,range,ntorping);
		}else
			fire_dchrg(&dd,&sub,range,ntorping);
	}
}

/* Can ship A shoot at ship B? */
canshoot(a,b)
struct	shpstr *a,*b;
{
	/* Anyone can shoot a normal ship */
	if ((mchr[b->shp_type].m_flags & M_SUB) == 0)
		return 1;

	/* You can depth-charge a sub */
	if (mchr[a->shp_type].m_flags & M_DCH)
		return 1;

	/* If you have SUBT flag, you can torp a sub */
	if (mchr[a->shp_type].m_flags & M_SUBT)
		return 1;

	return 0;
}

/* Can ship A torp ship B? */
cantorp(a,b)
struct	shpstr *a,*b;
{
	if ((mchr[a->shp_type].m_flags & M_TORP) == 0)
		return 0;

	/* Anyone with TORP flag can torp a normal ship */
	if ((mchr[b->shp_type].m_flags & M_SUB) == 0)
		return 1;

	/* Ship b is a sub, so we need to have the SUBT flag */
	if (mchr[a->shp_type].m_flags & M_SUBT)
		return 1;

	return 0;
}

/* Can ship A depth-charge (or fire guns at) ship B? */
candchrg(a,b)
struct	shpstr *a,*b;
{
	if ((mchr[b->shp_type].m_flags & M_SUB) == 0){
		if ((mchr[a->shp_type].m_flags & M_SUB) == 0)
			return 1;

		return 0;
	}

	if ((mchr[a->shp_type].m_flags & M_DCH) == 0)
		return 0;

	return 1;
}

void
fire_dchrg(sp,targ,range,ntargets)
struct	shpstr *sp, *targ;
int	range, ntargets;
{
	int	dam;
	int	shells;
	double	guneff;

	shells = getvar(V_SHELL, (s_char *)sp, EF_SHIP);
	shells+=supply_commod(sp->shp_own,sp->shp_x,sp->shp_y,I_SHELL,2-shells);

	if (shells < 2)
		return;

	if (getvar(V_MILIT, (s_char *)sp, EF_SHIP) < 1)
		return;

	if (getvar(V_GUN, (s_char *)sp, EF_SHIP) < 1)
		return;

	/* ok, all set.. now, we shoot */
	shells -= 2;
	putvar(V_SHELL, shells, (s_char *)sp, EF_SHIP);
	putship(sp->shp_uid,sp);

	if ((mchr[targ->shp_type].m_flags & M_SUB) == 0){
		pr_beep();
		pr("Kaboom!!! Incoming shells!\n");
		if(sp->shp_own != 0)
			wu(0, sp->shp_own,
			   "%s fired at %s\n",
			   prship(sp),
			   prship(targ));

		guneff = seagun(sp->shp_effic, 1);
		dam = (int)guneff;
		if (ntargets > 2)
			dam /= ((float)ntargets/2.0);
		pr_beep();
		pr("BLAM! %d damage!\n", dam);
		shipdamage(targ, dam);
		putship(targ->shp_uid,targ);
	}else{
		pr("\nCAPTAIN!  !!Depth charges!!...\n");
		if(sp->shp_own != 0)
			wu(0, sp->shp_own,
				"%s depth charged %s\n",
			   prship(sp), prsub(targ));

		dam = (random() % 30) + 30;
               	if (range)
                       	dam = (((float)dam) / ((float)(range+2.0)/2.0));

               	dam = ((float)dam * (float)sp->shp_effic/100.0);
		if (ntargets>2)
			dam /= ((float)ntargets/2.0);

		pr("click...WHAM!  %d damage!\n", dam);
		shipdamage(targ, dam);
		putship(targ->shp_uid,targ);
	}
}

fire_torp(sp,targ,range,ntargets)
struct	shpstr *sp, *targ;
int	range, ntargets;
{
	extern	int torpedo_damage;
	int	dam;
	int	shells;
	double	hitchance;
        double	mobcost;
	struct	mchrstr *mcp;

	shells = getvar(V_SHELL, (s_char *)sp, EF_SHIP);

	if (shells < 3)
		shells += supply_commod(sp->shp_own,sp->shp_x,sp->shp_y,I_SHELL,
			3-shells);

	if (getvar(V_GUN, (s_char *)sp, EF_SHIP) == 0 || shells < 3)
		return 0;

	if (getvar(V_MILIT, (s_char *)sp, EF_SHIP) < 1)
		return 0;

	if (sp->shp_effic < 60)
		return 0;

	if (sp->shp_mobil <= 0)
		return 0;

	/* All set.. fire! */
	shells -= 3;
	putvar(V_SHELL, shells, (s_char *)sp, EF_SHIP);
	putship(sp->shp_uid,sp);

	mcp = &mchr[sp->shp_type];
	mobcost = sp->shp_effic * 0.01 * mcp->m_speed;
	mobcost = (480.0 / (mobcost + techfact(sp->shp_tech, mobcost)));

	/* Mob cost for a torp is equal to the cost of 1/2 sector of movement */
	mobcost /= 2.0;
	sp->shp_mobil -= mobcost;

	hitchance = DTORP_HITCHANCE(range, mchr[sp->shp_type].m_visib);

	pr("Captain! Torpedoes sighted!\n");

	if (chance(hitchance)) {
		pr("BOOM!...\n");
		if(sp->shp_own != 0)
		wu(0, sp->shp_own, "%s @ %s torpedoed %s\n",
		   prship(sp),
		   xyas(sp->shp_x, sp->shp_y, sp->shp_own),
		   prsub(targ));
		dam = TORP_DAMAGE();

		if (ntargets>2)
			dam /= ((float)ntargets/2.0);

		shipdamage(targ, dam);
		putship(targ->shp_uid, targ);

		if (mchr[sp->shp_type].m_flags & M_SUB)
			nreport(targ->shp_own, N_TORP_SHIP, 0, 1);
		else
			nreport(targ->shp_own, N_SHIP_TORP, player->cnum, 1);
	} else {
		pr("Missed!\n");
		if(sp->shp_own != 0)
		wu(0, sp->shp_own,
		   "%s missed %s with a torp at %s\n",
		   prship(sp), prsub(targ),
		   xyas(sp->shp_x, sp->shp_y, sp->shp_own));
	}

	return 1;
}

s_char *
prsub(sp)
	struct shpstr *sp;
{
	if (mchr[sp->shp_type].m_flags & M_SUB)
		return "sub";
	else
		return prship(sp);
}
