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