xref: /csrg-svn/games/trek/phaser.c (revision 25994)
1*25994Smckusick /*
2*25994Smckusick  * Copyright (c) 1980 Regents of the University of California.
3*25994Smckusick  * All rights reserved.  The Berkeley software License Agreement
4*25994Smckusick  * specifies the terms and conditions for redistribution.
5*25994Smckusick  */
6*25994Smckusick 
711687Smckusick #ifndef lint
8*25994Smckusick static char sccsid[] = "@(#)phaser.c	5.1 (Berkeley) 01/29/86";
911687Smckusick #endif not lint
1011687Smckusick 
1111687Smckusick # include	"trek.h"
1211687Smckusick # include	"getpar.h"
1311687Smckusick 
1411687Smckusick /* factors for phaser hits; see description below */
1511687Smckusick 
1611687Smckusick # define	ALPHA		3.0		/* spread */
1711687Smckusick # define	BETA		3.0		/* franf() */
1811687Smckusick # define	GAMMA		0.30		/* cos(angle) */
1911687Smckusick # define	EPSILON		150.0		/* dist ** 2 */
2011687Smckusick # define	OMEGA		10.596		/* overall scaling factor */
2111687Smckusick 
2211687Smckusick /* OMEGA ~= 100 * (ALPHA + 1) * (BETA + 1) / (EPSILON + 1) */
2311687Smckusick 
2411687Smckusick /*
2511687Smckusick **  Phaser Control
2611687Smckusick **
2711687Smckusick **	There are up to NBANKS phaser banks which may be fired
2811687Smckusick **	simultaneously.  There are two modes, "manual" and
2911687Smckusick **	"automatic".  In manual mode, you specify exactly which
3011687Smckusick **	direction you want each bank to be aimed, the number
3111687Smckusick **	of units to fire, and the spread angle.  In automatic
3211687Smckusick **	mode, you give only the total number of units to fire.
3311687Smckusick **
3411687Smckusick **	The spread is specified as a number between zero and
3511687Smckusick **	one, with zero being minimum spread and one being maximum
3611687Smckusick **	spread.  You  will normally want zero spread, unless your
3711687Smckusick **	short range scanners are out, in which case you probably
3811687Smckusick **	don't know exactly where the Klingons are.  In that case,
3911687Smckusick **	you really don't have any choice except to specify a
4011687Smckusick **	fairly large spread.
4111687Smckusick **
4211687Smckusick **	Phasers spread slightly, even if you specify zero spread.
4311687Smckusick **
4411687Smckusick **	Uses trace flag 30
4511687Smckusick */
4611687Smckusick 
4712798Slayer struct cvntab	Matab[] =
4811687Smckusick {
4912798Slayer 	"m",		"anual",		(int (*)())1,		0,
5011687Smckusick 	"a",		"utomatic",		0,		0,
5111687Smckusick 	0
5211687Smckusick };
5311687Smckusick 
5411687Smckusick struct banks
5511687Smckusick {
5611687Smckusick 	int	units;
5712798Slayer 	double	angle;
5812798Slayer 	double	spread;
5911687Smckusick };
6011687Smckusick 
6111687Smckusick 
6211687Smckusick 
6311687Smckusick phaser()
6411687Smckusick {
6511687Smckusick 	register int		i;
6611687Smckusick 	int			j;
6711687Smckusick 	register struct kling	*k;
6811687Smckusick 	double			dx, dy;
6911687Smckusick 	double			anglefactor, distfactor;
7011687Smckusick 	register struct banks	*b;
7111687Smckusick 	int			manual, flag, extra;
7211687Smckusick 	int			hit;
7311687Smckusick 	double			tot;
7411687Smckusick 	int			n;
7511687Smckusick 	int			hitreqd[NBANKS];
7611687Smckusick 	struct banks		bank[NBANKS];
7711687Smckusick 	struct cvntab		*ptr;
7811687Smckusick 
7911687Smckusick 	if (Ship.cond == DOCKED)
8011687Smckusick 		return(printf("Phasers cannot fire through starbase shields\n"));
8111687Smckusick 	if (damaged(PHASER))
8211687Smckusick 		return (out(PHASER));
8311687Smckusick 	if (Ship.shldup)
8411687Smckusick 		return (printf("Sulu: Captain, we cannot fire through shields.\n"));
8511687Smckusick 	if (Ship.cloaked)
8611687Smckusick 	{
8711687Smckusick 		printf("Sulu: Captain, surely you must realize that we cannot fire\n");
8811687Smckusick 		printf("  phasers with the cloaking device up.\n");
8911687Smckusick 		return;
9011687Smckusick 	}
9111687Smckusick 
9211687Smckusick 	/* decide if we want manual or automatic mode */
9311687Smckusick 	manual = 0;
9411687Smckusick 	if (testnl())
9511687Smckusick 	{
9611687Smckusick 		if (damaged(COMPUTER))
9711687Smckusick 		{
9811687Smckusick 			printf(Device[COMPUTER].name);
9911687Smckusick 			manual++;
10011687Smckusick 		}
10111687Smckusick 		else
10211687Smckusick 			if (damaged(SRSCAN))
10311687Smckusick 			{
10411687Smckusick 				printf(Device[SRSCAN].name);
10511687Smckusick 				manual++;
10611687Smckusick 			}
10711687Smckusick 		if (manual)
10811687Smckusick 			printf(" damaged, manual mode selected\n");
10911687Smckusick 	}
11011687Smckusick 
11111687Smckusick 	if (!manual)
11211687Smckusick 	{
11311687Smckusick 		ptr = getcodpar("Manual or automatic", Matab);
11412798Slayer 		manual = (int) ptr->value;
11511687Smckusick 	}
11611687Smckusick 	if (!manual && damaged(COMPUTER))
11711687Smckusick 	{
11811687Smckusick 		printf("Computer damaged, manual selected\n");
11911687Smckusick 		skiptonl(0);
12011687Smckusick 		manual++;
12111687Smckusick 	}
12211687Smckusick 
12311687Smckusick 	/* initialize the bank[] array */
12411687Smckusick 	flag = 1;
12511687Smckusick 	for (i = 0; i < NBANKS; i++)
12611687Smckusick 		bank[i].units = 0;
12711687Smckusick 	if (manual)
12811687Smckusick 	{
12911687Smckusick 		/* collect manual mode statistics */
13011687Smckusick 		while (flag)
13111687Smckusick 		{
13211687Smckusick 			printf("%d units available\n", Ship.energy);
13311687Smckusick 			extra = 0;
13411687Smckusick 			flag = 0;
13511687Smckusick 			for (i = 0; i < NBANKS; i++)
13611687Smckusick 			{
13711687Smckusick 				b = &bank[i];
13811687Smckusick 				printf("\nBank %d:\n", i);
13911687Smckusick 				hit = getintpar("units");
14011687Smckusick 				if (hit < 0)
14111687Smckusick 					return;
14211687Smckusick 				if (hit == 0)
14311687Smckusick 					break;
14412798Slayer 				extra += hit;
14511687Smckusick 				if (extra > Ship.energy)
14611687Smckusick 				{
14711687Smckusick 					printf("available energy exceeded.  ");
14811687Smckusick 					skiptonl(0);
14911687Smckusick 					flag++;
15011687Smckusick 					break;
15111687Smckusick 				}
15211687Smckusick 				b->units = hit;
15311687Smckusick 				hit = getintpar("course");
15411687Smckusick 				if (hit < 0 || hit > 360)
15511687Smckusick 					return;
15611687Smckusick 				b->angle = hit * 0.0174532925;
15711687Smckusick 				b->spread = getfltpar("spread");
15811687Smckusick 				if (b->spread < 0 || b->spread > 1)
15911687Smckusick 					return;
16011687Smckusick 			}
16112798Slayer 			Ship.energy -= extra;
16211687Smckusick 		}
16311687Smckusick 		extra = 0;
16411687Smckusick 	}
16511687Smckusick 	else
16611687Smckusick 	{
16711687Smckusick 		/* automatic distribution of power */
16811687Smckusick 		if (Etc.nkling <= 0)
16911687Smckusick 			return (printf("Sulu: But there are no Klingons in this quadrant\n"));
17011687Smckusick 		printf("Phasers locked on target.  ");
17111687Smckusick 		while (flag)
17211687Smckusick 		{
17311687Smckusick 			printf("%d units available\n", Ship.energy);
17411687Smckusick 			hit = getintpar("Units to fire");
17511687Smckusick 			if (hit <= 0)
17611687Smckusick 				return;
17711687Smckusick 			if (hit > Ship.energy)
17811687Smckusick 			{
17911687Smckusick 				printf("available energy exceeded.  ");
18011687Smckusick 				skiptonl(0);
18111687Smckusick 				continue;
18211687Smckusick 			}
18311687Smckusick 			flag = 0;
18412798Slayer 			Ship.energy -= hit;
18511687Smckusick 			extra = hit;
18611687Smckusick 			n = Etc.nkling;
18711687Smckusick 			if (n > NBANKS)
18811687Smckusick 				n = NBANKS;
18911687Smckusick 			tot = n * (n + 1) / 2;
19011687Smckusick 			for (i = 0; i < n; i++)
19111687Smckusick 			{
19211687Smckusick 				k = &Etc.klingon[i];
19311687Smckusick 				b = &bank[i];
19411687Smckusick 				distfactor = k->dist;
19511687Smckusick 				anglefactor = ALPHA * BETA * OMEGA / (distfactor * distfactor + EPSILON);
19612798Slayer 				anglefactor *= GAMMA;
19711687Smckusick 				distfactor = k->power;
19812798Slayer 				distfactor /= anglefactor;
19911687Smckusick 				hitreqd[i] = distfactor + 0.5;
20011687Smckusick 				dx = Ship.sectx - k->x;
20111687Smckusick 				dy = k->y - Ship.secty;
20211687Smckusick 				b->angle = atan2(dy, dx);
20311687Smckusick 				b->spread = 0.0;
20411687Smckusick 				b->units = ((n - i) / tot) * extra;
20511687Smckusick #				ifdef xTRACE
20611687Smckusick 				if (Trace)
20711687Smckusick 				{
20811687Smckusick 					printf("b%d hr%d u%d df%.2f af%.2f\n",
20911687Smckusick 						i, hitreqd[i], b->units,
21011687Smckusick 						distfactor, anglefactor);
21111687Smckusick 				}
21211687Smckusick #				endif
21312798Slayer 				extra -= b->units;
21411687Smckusick 				hit = b->units - hitreqd[i];
21511687Smckusick 				if (hit > 0)
21611687Smckusick 				{
21712798Slayer 					extra += hit;
21812798Slayer 					b->units -= hit;
21911687Smckusick 				}
22011687Smckusick 			}
22111687Smckusick 
22211687Smckusick 			/* give out any extra energy we might have around */
22311687Smckusick 			if (extra > 0)
22411687Smckusick 			{
22511687Smckusick 				for (i = 0; i < n; i++)
22611687Smckusick 				{
22711687Smckusick 					b = &bank[i];
22811687Smckusick 					hit = hitreqd[i] - b->units;
22911687Smckusick 					if (hit <= 0)
23011687Smckusick 						continue;
23111687Smckusick 					if (hit >= extra)
23211687Smckusick 					{
23312798Slayer 						b->units += extra;
23411687Smckusick 						extra = 0;
23511687Smckusick 						break;
23611687Smckusick 					}
23711687Smckusick 					b->units = hitreqd[i];
23812798Slayer 					extra -= hit;
23911687Smckusick 				}
24011687Smckusick 				if (extra > 0)
24111687Smckusick 					printf("%d units overkill\n", extra);
24211687Smckusick 			}
24311687Smckusick 		}
24411687Smckusick 	}
24511687Smckusick 
24611687Smckusick #	ifdef xTRACE
24711687Smckusick 	if (Trace)
24811687Smckusick 	{
24911687Smckusick 		for (i = 0; i < NBANKS; i++)
25011687Smckusick 		{
25111687Smckusick 			b = &bank[i];
25211687Smckusick 			printf("b%d u%d", i, b->units);
25311687Smckusick 			if (b->units > 0)
25411687Smckusick 				printf(" a%.2f s%.2f\n", b->angle, b->spread);
25511687Smckusick 			else
25611687Smckusick 				printf("\n");
25711687Smckusick 		}
25811687Smckusick 	}
25911687Smckusick #	endif
26011687Smckusick 
26111687Smckusick 	/* actually fire the shots */
26211687Smckusick 	Move.free = 0;
26311687Smckusick 	for (i = 0; i < NBANKS; i++)
26411687Smckusick 	{
26511687Smckusick 		b = &bank[i];
26611687Smckusick 		if (b->units <= 0)
26711687Smckusick 		{
26811687Smckusick 			continue;
26911687Smckusick 		}
27011687Smckusick 		printf("\nPhaser bank %d fires:\n", i);
27111687Smckusick 		n = Etc.nkling;
27211687Smckusick 		k = Etc.klingon;
27311687Smckusick 		for (j = 0; j < n; j++)
27411687Smckusick 		{
27511687Smckusick 			if (b->units <= 0)
27611687Smckusick 				break;
27711687Smckusick 			/*
27811687Smckusick 			** The formula for hit is as follows:
27911687Smckusick 			**
28011687Smckusick 			**  zap = OMEGA * [(sigma + ALPHA) * (rho + BETA)]
28111687Smckusick 			**	/ (dist ** 2 + EPSILON)]
28211687Smckusick 			**	* [cos(delta * sigma) + GAMMA]
28311687Smckusick 			**	* hit
28411687Smckusick 			**
28511687Smckusick 			** where sigma is the spread factor,
28611687Smckusick 			** rho is a random number (0 -> 1),
28711687Smckusick 			** GAMMA is a crud factor for angle (essentially
28811687Smckusick 			**	cruds up the spread factor),
28911687Smckusick 			** delta is the difference in radians between the
29011687Smckusick 			**	angle you are shooting at and the actual
29111687Smckusick 			**	angle of the klingon,
29211687Smckusick 			** ALPHA scales down the significance of sigma,
29311687Smckusick 			** BETA scales down the significance of rho,
29411687Smckusick 			** OMEGA is the magic number which makes everything
29511687Smckusick 			**	up to "* hit" between zero and one,
29611687Smckusick 			** dist is the distance to the klingon
29711687Smckusick 			** hit is the number of units in the bank, and
29811687Smckusick 			** zap is the amount of the actual hit.
29911687Smckusick 			**
30011687Smckusick 			** Everything up through dist squared should maximize
30111687Smckusick 			** at 1.0, so that the distance factor is never
30211687Smckusick 			** greater than one.  Conveniently, cos() is
30311687Smckusick 			** never greater than one, but the same restric-
30411687Smckusick 			** tion applies.
30511687Smckusick 			*/
30611687Smckusick 			distfactor = BETA + franf();
30712798Slayer 			distfactor *= ALPHA + b->spread;
30812798Slayer 			distfactor *= OMEGA;
30911687Smckusick 			anglefactor = k->dist;
31012798Slayer 			distfactor /= anglefactor * anglefactor + EPSILON;
31112798Slayer 			distfactor *= b->units;
31211687Smckusick 			dx = Ship.sectx - k->x;
31311687Smckusick 			dy = k->y - Ship.secty;
31411687Smckusick 			anglefactor = atan2(dy, dx) - b->angle;
31511687Smckusick 			anglefactor = cos((anglefactor * b->spread) + GAMMA);
31611687Smckusick 			if (anglefactor < 0.0)
31711687Smckusick 			{
31811687Smckusick 				k++;
31911687Smckusick 				continue;
32011687Smckusick 			}
32111687Smckusick 			hit = anglefactor * distfactor + 0.5;
32212798Slayer 			k->power -= hit;
32311687Smckusick 			printf("%d unit hit on Klingon", hit);
32411687Smckusick 			if (!damaged(SRSCAN))
32511687Smckusick 				printf(" at %d,%d", k->x, k->y);
32611687Smckusick 			printf("\n");
32712798Slayer 			b->units -= hit;
32811687Smckusick 			if (k->power <= 0)
32911687Smckusick 			{
33011687Smckusick 				killk(k->x, k->y);
33111687Smckusick 				continue;
33211687Smckusick 			}
33311687Smckusick 			k++;
33411687Smckusick 		}
33511687Smckusick 	}
33611687Smckusick 
33711687Smckusick 	/* compute overkill */
33811687Smckusick 	for (i = 0; i < NBANKS; i++)
33912798Slayer 		extra += bank[i].units;
34011687Smckusick 	if (extra > 0)
34111687Smckusick 		printf("\n%d units expended on empty space\n", extra);
34211687Smckusick }
343