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