xref: /netbsd/games/trek/phaser.c (revision bf9ec67e)
1 /*	$NetBSD: phaser.c,v 1.8 2000/07/10 10:19:27 itojun Exp $	*/
2 
3 /*
4  * Copyright (c) 1980, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)phaser.c	8.1 (Berkeley) 5/31/93";
40 #else
41 __RCSID("$NetBSD: phaser.c,v 1.8 2000/07/10 10:19:27 itojun 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 **	Uses trace flag 30
81 */
82 
83 struct cvntab	Matab[] =
84 {
85 	{ "m",		"anual",	(cmdfun) 1,	0 },
86 	{ "a",		"utomatic",	(cmdfun) 0,	0 },
87 	{ NULL,		NULL,		NULL,		0 }
88 };
89 
90 struct banks
91 {
92 	int	units;
93 	double	angle;
94 	double	spread;
95 };
96 
97 
98 
99 /*ARGSUSED*/
100 void
101 phaser(v)
102 	int v __attribute__((__unused__));
103 {
104 	int		i;
105 	int		j;
106 	struct kling	*k;
107 	double		dx, dy;
108 	double		anglefactor, distfactor;
109 	struct banks	*b;
110 	int		manual, flag, extra = 0;
111 	int		hit;
112 	double		tot;
113 	int		n;
114 	int		hitreqd[NBANKS];
115 	struct banks	bank[NBANKS];
116 	const struct cvntab	*ptr;
117 
118 	if (Ship.cond == DOCKED) {
119 		printf("Phasers cannot fire through starbase shields\n");
120 		return;
121 	}
122 	if (damaged(PHASER)) {
123 		out(PHASER);
124 		return;
125 	}
126 	if (Ship.shldup) {
127 		printf("Sulu: Captain, we cannot fire through shields.\n");
128 		return;
129 	}
130 	if (Ship.cloaked)
131 	{
132 		printf("Sulu: Captain, surely you must realize that we cannot fire\n");
133 		printf("  phasers with the cloaking device up.\n");
134 		return;
135 	}
136 
137 	/* decide if we want manual or automatic mode */
138 	manual = 0;
139 	if (testnl())
140 	{
141 		if (damaged(COMPUTER))
142 		{
143 			printf("%s", Device[COMPUTER].name);
144 			manual++;
145 		}
146 		else
147 			if (damaged(SRSCAN))
148 			{
149 				printf("%s", Device[SRSCAN].name);
150 				manual++;
151 			}
152 		if (manual)
153 			printf(" damaged, manual mode selected\n");
154 	}
155 
156 	if (!manual)
157 	{
158 		ptr = getcodpar("Manual or automatic", Matab);
159 		manual = (long) ptr->value;
160 	}
161 	if (!manual && damaged(COMPUTER))
162 	{
163 		printf("Computer damaged, manual selected\n");
164 		skiptonl(0);
165 		manual++;
166 	}
167 
168 	/* initialize the bank[] array */
169 	flag = 1;
170 	for (i = 0; i < NBANKS; i++)
171 		bank[i].units = 0;
172 	if (manual)
173 	{
174 		/* collect manual mode statistics */
175 		while (flag)
176 		{
177 			printf("%d units available\n", Ship.energy);
178 			extra = 0;
179 			flag = 0;
180 			for (i = 0; i < NBANKS; i++)
181 			{
182 				b = &bank[i];
183 				printf("\nBank %d:\n", i);
184 				hit = getintpar("units");
185 				if (hit < 0)
186 					return;
187 				if (hit == 0)
188 					break;
189 				extra += hit;
190 				if (extra > Ship.energy)
191 				{
192 					printf("available energy exceeded.  ");
193 					skiptonl(0);
194 					flag++;
195 					break;
196 				}
197 				b->units = hit;
198 				hit = getintpar("course");
199 				if (hit < 0 || hit > 360)
200 					return;
201 				b->angle = hit * 0.0174532925;
202 				b->spread = getfltpar("spread");
203 				if (b->spread < 0 || b->spread > 1)
204 					return;
205 			}
206 			Ship.energy -= extra;
207 		}
208 		extra = 0;
209 	}
210 	else
211 	{
212 		/* automatic distribution of power */
213 		if (Etc.nkling <= 0) {
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