xref: /openbsd-src/games/trek/phaser.c (revision 11da2480c68e9717c510fd663096c44c53785231)
1*11da2480Smestre /*	$OpenBSD: phaser.c,v 1.10 2016/01/07 14:37:51 mestre Exp $	*/
2df930be7Sderaadt /*	$NetBSD: phaser.c,v 1.4 1995/04/24 12:26:02 cgd Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1980, 1993
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
9df930be7Sderaadt  * modification, are permitted provided that the following conditions
10df930be7Sderaadt  * are met:
11df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
12df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
13df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
14df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
15df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
167a09557bSmillert  * 3. Neither the name of the University nor the names of its contributors
17df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
18df930be7Sderaadt  *    without specific prior written permission.
19df930be7Sderaadt  *
20df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30df930be7Sderaadt  * SUCH DAMAGE.
31df930be7Sderaadt  */
32df930be7Sderaadt 
331ed0e75dSpjanzen #include <math.h>
3448b4d137Smestre #include <stdio.h>
3548b4d137Smestre 
36df930be7Sderaadt #include "getpar.h"
3748b4d137Smestre #include "trek.h"
38df930be7Sderaadt 
39df930be7Sderaadt /* factors for phaser hits; see description below */
40df930be7Sderaadt 
41df930be7Sderaadt # define	ALPHA		3.0		/* spread */
42df930be7Sderaadt # define	BETA		3.0		/* franf() */
43df930be7Sderaadt # define	GAMMA		0.30		/* cos(angle) */
44df930be7Sderaadt # define	EPSILON		150.0		/* dist ** 2 */
45df930be7Sderaadt # define	OMEGA		10.596		/* overall scaling factor */
46df930be7Sderaadt 
47df930be7Sderaadt /* OMEGA ~= 100 * (ALPHA + 1) * (BETA + 1) / (EPSILON + 1) */
48df930be7Sderaadt 
49df930be7Sderaadt /*
50df930be7Sderaadt **  Phaser Control
51df930be7Sderaadt **
52df930be7Sderaadt **	There are up to NBANKS phaser banks which may be fired
53df930be7Sderaadt **	simultaneously.  There are two modes, "manual" and
54df930be7Sderaadt **	"automatic".  In manual mode, you specify exactly which
55df930be7Sderaadt **	direction you want each bank to be aimed, the number
56df930be7Sderaadt **	of units to fire, and the spread angle.  In automatic
57df930be7Sderaadt **	mode, you give only the total number of units to fire.
58df930be7Sderaadt **
59df930be7Sderaadt **	The spread is specified as a number between zero and
60df930be7Sderaadt **	one, with zero being minimum spread and one being maximum
61df930be7Sderaadt **	spread.  You  will normally want zero spread, unless your
62df930be7Sderaadt **	short range scanners are out, in which case you probably
63df930be7Sderaadt **	don't know exactly where the Klingons are.  In that case,
64df930be7Sderaadt **	you really don't have any choice except to specify a
65df930be7Sderaadt **	fairly large spread.
66df930be7Sderaadt **
67df930be7Sderaadt **	Phasers spread slightly, even if you specify zero spread.
68df930be7Sderaadt */
69df930be7Sderaadt 
704c24da34Spjanzen const struct cvntab	Matab[] =
71df930be7Sderaadt {
721ed0e75dSpjanzen 	{ "m",		"anual",	(cmdfun)1,	0 },
731ed0e75dSpjanzen 	{ "a",		"utomatic",	(cmdfun)0,	0 },
741ed0e75dSpjanzen 	{ NULL,		NULL,		NULL,		0 }
75df930be7Sderaadt };
76df930be7Sderaadt 
77df930be7Sderaadt struct banks
78df930be7Sderaadt {
79df930be7Sderaadt 	int	units;
80df930be7Sderaadt 	double	angle;
81df930be7Sderaadt 	double	spread;
82df930be7Sderaadt };
83df930be7Sderaadt 
84df930be7Sderaadt 
851ed0e75dSpjanzen void
phaser(int v)86*11da2480Smestre phaser(int v)
87df930be7Sderaadt {
8897419aa0Spjanzen 	int		i, j;
8997419aa0Spjanzen 	struct kling	*k;
90df930be7Sderaadt 	double		dx, dy;
91df930be7Sderaadt 	double		anglefactor, distfactor;
9297419aa0Spjanzen 	struct banks	*b;
93df930be7Sderaadt 	int		manual, flag, extra;
94df930be7Sderaadt 	int		hit;
95df930be7Sderaadt 	double		tot;
96df930be7Sderaadt 	int		n;
97df930be7Sderaadt 	int		hitreqd[NBANKS];
98df930be7Sderaadt 	struct banks	bank[NBANKS];
994c24da34Spjanzen 	const struct cvntab	*ptr;
100df930be7Sderaadt 
101df930be7Sderaadt 	if (Ship.cond == DOCKED)
1021ed0e75dSpjanzen 	{
1031ed0e75dSpjanzen 		printf("Phasers cannot fire through starbase shields\n");
1041ed0e75dSpjanzen 		return;
1051ed0e75dSpjanzen 	}
106df930be7Sderaadt 	if (damaged(PHASER))
1071ed0e75dSpjanzen 	{
1081ed0e75dSpjanzen 		out(PHASER);
1091ed0e75dSpjanzen 		return;
1101ed0e75dSpjanzen 	}
111df930be7Sderaadt 	if (Ship.shldup)
1121ed0e75dSpjanzen 	{
1131ed0e75dSpjanzen 		printf("Sulu: Captain, we cannot fire through shields.\n");
1141ed0e75dSpjanzen 		return;
1151ed0e75dSpjanzen 	}
116df930be7Sderaadt 	if (Ship.cloaked)
117df930be7Sderaadt 	{
118df930be7Sderaadt 		printf("Sulu: Captain, surely you must realize that we cannot fire\n");
119df930be7Sderaadt 		printf("  phasers with the cloaking device up.\n");
120df930be7Sderaadt 		return;
121df930be7Sderaadt 	}
122df930be7Sderaadt 
123df930be7Sderaadt 	/* decide if we want manual or automatic mode */
124df930be7Sderaadt 	manual = 0;
125df930be7Sderaadt 	if (testnl())
126df930be7Sderaadt 	{
127df930be7Sderaadt 		if (damaged(COMPUTER))
128df930be7Sderaadt 		{
129cff62e3cSpjanzen 			printf("%s", Device[COMPUTER].name);
130df930be7Sderaadt 			manual++;
131df930be7Sderaadt 		}
132df930be7Sderaadt 		else
133df930be7Sderaadt 			if (damaged(SRSCAN))
134df930be7Sderaadt 			{
135cff62e3cSpjanzen 				printf("%s", Device[SRSCAN].name);
136df930be7Sderaadt 				manual++;
137df930be7Sderaadt 			}
138df930be7Sderaadt 		if (manual)
139df930be7Sderaadt 			printf(" damaged, manual mode selected\n");
140df930be7Sderaadt 	}
141df930be7Sderaadt 
142df930be7Sderaadt 	if (!manual)
143df930be7Sderaadt 	{
144df930be7Sderaadt 		ptr = getcodpar("Manual or automatic", Matab);
145df930be7Sderaadt 		manual = (long) ptr->value;
146df930be7Sderaadt 	}
147df930be7Sderaadt 	if (!manual && damaged(COMPUTER))
148df930be7Sderaadt 	{
149df930be7Sderaadt 		printf("Computer damaged, manual selected\n");
150df930be7Sderaadt 		skiptonl(0);
151df930be7Sderaadt 		manual++;
152df930be7Sderaadt 	}
153df930be7Sderaadt 
154df930be7Sderaadt 	/* initialize the bank[] array */
155df930be7Sderaadt 	flag = 1;
156df930be7Sderaadt 	for (i = 0; i < NBANKS; i++)
157df930be7Sderaadt 		bank[i].units = 0;
158df930be7Sderaadt 	if (manual)
159df930be7Sderaadt 	{
160df930be7Sderaadt 		/* collect manual mode statistics */
161df930be7Sderaadt 		while (flag)
162df930be7Sderaadt 		{
163df930be7Sderaadt 			printf("%d units available\n", Ship.energy);
164df930be7Sderaadt 			extra = 0;
165df930be7Sderaadt 			flag = 0;
166df930be7Sderaadt 			for (i = 0; i < NBANKS; i++)
167df930be7Sderaadt 			{
168df930be7Sderaadt 				b = &bank[i];
169df930be7Sderaadt 				printf("\nBank %d:\n", i);
170df930be7Sderaadt 				hit = getintpar("units");
171df930be7Sderaadt 				if (hit < 0)
172df930be7Sderaadt 					return;
173df930be7Sderaadt 				if (hit == 0)
174df930be7Sderaadt 					break;
175df930be7Sderaadt 				extra += hit;
176df930be7Sderaadt 				if (extra > Ship.energy)
177df930be7Sderaadt 				{
178df930be7Sderaadt 					printf("available energy exceeded.  ");
179df930be7Sderaadt 					skiptonl(0);
180df930be7Sderaadt 					flag++;
181df930be7Sderaadt 					break;
182df930be7Sderaadt 				}
183df930be7Sderaadt 				b->units = hit;
184df930be7Sderaadt 				hit = getintpar("course");
185df930be7Sderaadt 				if (hit < 0 || hit > 360)
186df930be7Sderaadt 					return;
187df930be7Sderaadt 				b->angle = hit * 0.0174532925;
188df930be7Sderaadt 				b->spread = getfltpar("spread");
189df930be7Sderaadt 				if (b->spread < 0 || b->spread > 1)
190df930be7Sderaadt 					return;
191df930be7Sderaadt 			}
192df930be7Sderaadt 			Ship.energy -= extra;
193df930be7Sderaadt 		}
194df930be7Sderaadt 		extra = 0;
195df930be7Sderaadt 	}
196df930be7Sderaadt 	else
197df930be7Sderaadt 	{
198df930be7Sderaadt 		/* automatic distribution of power */
199df930be7Sderaadt 		if (Etc.nkling <= 0)
2001ed0e75dSpjanzen 		{
2011ed0e75dSpjanzen 			printf("Sulu: But there are no Klingons in this quadrant\n");
2021ed0e75dSpjanzen 			return;
2031ed0e75dSpjanzen 		}
204df930be7Sderaadt 		printf("Phasers locked on target.  ");
205df930be7Sderaadt 		while (flag)
206df930be7Sderaadt 		{
207df930be7Sderaadt 			printf("%d units available\n", Ship.energy);
208df930be7Sderaadt 			hit = getintpar("Units to fire");
209df930be7Sderaadt 			if (hit <= 0)
210df930be7Sderaadt 				return;
211df930be7Sderaadt 			if (hit > Ship.energy)
212df930be7Sderaadt 			{
213df930be7Sderaadt 				printf("available energy exceeded.  ");
214df930be7Sderaadt 				skiptonl(0);
215df930be7Sderaadt 				continue;
216df930be7Sderaadt 			}
217df930be7Sderaadt 			flag = 0;
218df930be7Sderaadt 			Ship.energy -= hit;
219df930be7Sderaadt 			extra = hit;
220df930be7Sderaadt 			n = Etc.nkling;
221df930be7Sderaadt 			if (n > NBANKS)
222df930be7Sderaadt 				n = NBANKS;
223df930be7Sderaadt 			tot = n * (n + 1) / 2;
224df930be7Sderaadt 			for (i = 0; i < n; i++)
225df930be7Sderaadt 			{
226df930be7Sderaadt 				k = &Etc.klingon[i];
227df930be7Sderaadt 				b = &bank[i];
228df930be7Sderaadt 				distfactor = k->dist;
229df930be7Sderaadt 				anglefactor = ALPHA * BETA * OMEGA / (distfactor * distfactor + EPSILON);
230df930be7Sderaadt 				anglefactor *= GAMMA;
231df930be7Sderaadt 				distfactor = k->power;
232df930be7Sderaadt 				distfactor /= anglefactor;
233df930be7Sderaadt 				hitreqd[i] = distfactor + 0.5;
234df930be7Sderaadt 				dx = Ship.sectx - k->x;
235df930be7Sderaadt 				dy = k->y - Ship.secty;
236df930be7Sderaadt 				b->angle = atan2(dy, dx);
237df930be7Sderaadt 				b->spread = 0.0;
238df930be7Sderaadt 				b->units = ((n - i) / tot) * extra;
239df930be7Sderaadt #				ifdef xTRACE
240df930be7Sderaadt 				if (Trace)
241df930be7Sderaadt 				{
242df930be7Sderaadt 					printf("b%d hr%d u%d df%.2f af%.2f\n",
243df930be7Sderaadt 						i, hitreqd[i], b->units,
244df930be7Sderaadt 						distfactor, anglefactor);
245df930be7Sderaadt 				}
246df930be7Sderaadt #				endif
247df930be7Sderaadt 				extra -= b->units;
248df930be7Sderaadt 				hit = b->units - hitreqd[i];
249df930be7Sderaadt 				if (hit > 0)
250df930be7Sderaadt 				{
251df930be7Sderaadt 					extra += hit;
252df930be7Sderaadt 					b->units -= hit;
253df930be7Sderaadt 				}
254df930be7Sderaadt 			}
255df930be7Sderaadt 
256df930be7Sderaadt 			/* give out any extra energy we might have around */
257df930be7Sderaadt 			if (extra > 0)
258df930be7Sderaadt 			{
259df930be7Sderaadt 				for (i = 0; i < n; i++)
260df930be7Sderaadt 				{
261df930be7Sderaadt 					b = &bank[i];
262df930be7Sderaadt 					hit = hitreqd[i] - b->units;
263df930be7Sderaadt 					if (hit <= 0)
264df930be7Sderaadt 						continue;
265df930be7Sderaadt 					if (hit >= extra)
266df930be7Sderaadt 					{
267df930be7Sderaadt 						b->units += extra;
268df930be7Sderaadt 						extra = 0;
269df930be7Sderaadt 						break;
270df930be7Sderaadt 					}
271df930be7Sderaadt 					b->units = hitreqd[i];
272df930be7Sderaadt 					extra -= hit;
273df930be7Sderaadt 				}
274df930be7Sderaadt 				if (extra > 0)
275df930be7Sderaadt 					printf("%d units overkill\n", extra);
276df930be7Sderaadt 			}
277df930be7Sderaadt 		}
278df930be7Sderaadt 	}
279df930be7Sderaadt 
280df930be7Sderaadt #	ifdef xTRACE
281df930be7Sderaadt 	if (Trace)
282df930be7Sderaadt 	{
283df930be7Sderaadt 		for (i = 0; i < NBANKS; i++)
284df930be7Sderaadt 		{
285df930be7Sderaadt 			b = &bank[i];
286df930be7Sderaadt 			printf("b%d u%d", i, b->units);
287df930be7Sderaadt 			if (b->units > 0)
288df930be7Sderaadt 				printf(" a%.2f s%.2f\n", b->angle, b->spread);
289df930be7Sderaadt 			else
290df930be7Sderaadt 				printf("\n");
291df930be7Sderaadt 		}
292df930be7Sderaadt 	}
293df930be7Sderaadt #	endif
294df930be7Sderaadt 
295df930be7Sderaadt 	/* actually fire the shots */
296df930be7Sderaadt 	Move.free = 0;
297df930be7Sderaadt 	for (i = 0; i < NBANKS; i++)
298df930be7Sderaadt 	{
299df930be7Sderaadt 		b = &bank[i];
300df930be7Sderaadt 		if (b->units <= 0)
301df930be7Sderaadt 		{
302df930be7Sderaadt 			continue;
303df930be7Sderaadt 		}
304df930be7Sderaadt 		printf("\nPhaser bank %d fires:\n", i);
305df930be7Sderaadt 		n = Etc.nkling;
306df930be7Sderaadt 		k = Etc.klingon;
307df930be7Sderaadt 		for (j = 0; j < n; j++)
308df930be7Sderaadt 		{
309df930be7Sderaadt 			if (b->units <= 0)
310df930be7Sderaadt 				break;
311df930be7Sderaadt 			/*
312df930be7Sderaadt 			** The formula for hit is as follows:
313df930be7Sderaadt 			**
314df930be7Sderaadt 			**  zap = OMEGA * [(sigma + ALPHA) * (rho + BETA)]
315df930be7Sderaadt 			**	/ (dist ** 2 + EPSILON)]
316df930be7Sderaadt 			**	* [cos(delta * sigma) + GAMMA]
317df930be7Sderaadt 			**	* hit
318df930be7Sderaadt 			**
319df930be7Sderaadt 			** where sigma is the spread factor,
320df930be7Sderaadt 			** rho is a random number (0 -> 1),
321df930be7Sderaadt 			** GAMMA is a crud factor for angle (essentially
322df930be7Sderaadt 			**	cruds up the spread factor),
323df930be7Sderaadt 			** delta is the difference in radians between the
324df930be7Sderaadt 			**	angle you are shooting at and the actual
325df930be7Sderaadt 			**	angle of the klingon,
326df930be7Sderaadt 			** ALPHA scales down the significance of sigma,
327df930be7Sderaadt 			** BETA scales down the significance of rho,
328df930be7Sderaadt 			** OMEGA is the magic number which makes everything
329df930be7Sderaadt 			**	up to "* hit" between zero and one,
330df930be7Sderaadt 			** dist is the distance to the klingon
331df930be7Sderaadt 			** hit is the number of units in the bank, and
332df930be7Sderaadt 			** zap is the amount of the actual hit.
333df930be7Sderaadt 			**
334df930be7Sderaadt 			** Everything up through dist squared should maximize
335df930be7Sderaadt 			** at 1.0, so that the distance factor is never
336df930be7Sderaadt 			** greater than one.  Conveniently, cos() is
337df930be7Sderaadt 			** never greater than one, but the same restric-
338df930be7Sderaadt 			** tion applies.
339df930be7Sderaadt 			*/
340df930be7Sderaadt 			distfactor = BETA + franf();
341df930be7Sderaadt 			distfactor *= ALPHA + b->spread;
342df930be7Sderaadt 			distfactor *= OMEGA;
343df930be7Sderaadt 			anglefactor = k->dist;
344df930be7Sderaadt 			distfactor /= anglefactor * anglefactor + EPSILON;
345df930be7Sderaadt 			distfactor *= b->units;
346df930be7Sderaadt 			dx = Ship.sectx - k->x;
347df930be7Sderaadt 			dy = k->y - Ship.secty;
348df930be7Sderaadt 			anglefactor = atan2(dy, dx) - b->angle;
349df930be7Sderaadt 			anglefactor = cos((anglefactor * b->spread) + GAMMA);
350df930be7Sderaadt 			if (anglefactor < 0.0)
351df930be7Sderaadt 			{
352df930be7Sderaadt 				k++;
353df930be7Sderaadt 				continue;
354df930be7Sderaadt 			}
355df930be7Sderaadt 			hit = anglefactor * distfactor + 0.5;
356df930be7Sderaadt 			k->power -= hit;
357df930be7Sderaadt 			printf("%d unit hit on Klingon", hit);
358df930be7Sderaadt 			if (!damaged(SRSCAN))
359df930be7Sderaadt 				printf(" at %d,%d", k->x, k->y);
360df930be7Sderaadt 			printf("\n");
361df930be7Sderaadt 			b->units -= hit;
362df930be7Sderaadt 			if (k->power <= 0)
363df930be7Sderaadt 			{
364df930be7Sderaadt 				killk(k->x, k->y);
365df930be7Sderaadt 				continue;
366df930be7Sderaadt 			}
367df930be7Sderaadt 			k++;
368df930be7Sderaadt 		}
369df930be7Sderaadt 	}
370df930be7Sderaadt 
371df930be7Sderaadt 	/* compute overkill */
372df930be7Sderaadt 	for (i = 0; i < NBANKS; i++)
373df930be7Sderaadt 		extra += bank[i].units;
374df930be7Sderaadt 	if (extra > 0)
375df930be7Sderaadt 		printf("\n%d units expended on empty space\n", extra);
376df930be7Sderaadt }
377