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