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