xref: /netbsd-src/games/trek/phaser.c (revision da121b333149705fd908f272187095dff6b9353c)
1 /*	$NetBSD: phaser.c,v 1.15 2009/08/12 08:54:54 dholland 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. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)phaser.c	8.1 (Berkeley) 5/31/93";
36 #else
37 __RCSID("$NetBSD: phaser.c,v 1.15 2009/08/12 08:54:54 dholland 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 **	Uses trace flag 30
77 */
78 
79 static struct cvntab Matab[] = {
80 	{ "m",		"anual",	(cmdfun) 1,	0 },
81 	{ "a",		"utomatic",	(cmdfun) 0,	0 },
82 	{ NULL,		NULL,		NULL,		0 }
83 };
84 
85 struct banks {
86 	int	units;
87 	double	angle;
88 	double	spread;
89 };
90 
91 
92 
93 /*ARGSUSED*/
94 void
phaser(int v __unused)95 phaser(int v __unused)
96 {
97 	int		i;
98 	int		j;
99 	struct kling	*k;
100 	double		dx, dy;
101 	double		anglefactor, distfactor;
102 	struct banks	*b;
103 	int		manual, flag, extra = 0;
104 	int		hit;
105 	double		tot;
106 	int		n;
107 	int		hitreqd[NBANKS];
108 	struct banks	bank[NBANKS];
109 	const struct cvntab	*ptr;
110 
111 	if (Ship.cond == DOCKED) {
112 		printf("Phasers cannot fire through starbase shields\n");
113 		return;
114 	}
115 	if (damaged(PHASER)) {
116 		out(PHASER);
117 		return;
118 	}
119 	if (Ship.shldup) {
120 		printf("Sulu: Captain, we cannot fire through shields.\n");
121 		return;
122 	}
123 	if (Ship.cloaked) {
124 		printf("Sulu: Captain, surely you must realize that we cannot "
125 		       "fire\n");
126 		printf("  phasers with the cloaking device up.\n");
127 		return;
128 	}
129 
130 	/* decide if we want manual or automatic mode */
131 	manual = 0;
132 	if (testnl()) {
133 		if (damaged(COMPUTER)) {
134 			printf("%s", Device[COMPUTER].name);
135 			manual++;
136 		} else if (damaged(SRSCAN)) {
137 			printf("%s", Device[SRSCAN].name);
138 			manual++;
139 		}
140 		if (manual)
141 			printf(" damaged, manual mode selected\n");
142 	}
143 
144 	if (!manual) {
145 		ptr = getcodpar("Manual or automatic", Matab);
146 		manual = (long) ptr->value;
147 	}
148 	if (!manual && damaged(COMPUTER)) {
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 		/* collect manual mode statistics */
160 		while (flag) {
161 			printf("%d units available\n", Ship.energy);
162 			extra = 0;
163 			flag = 0;
164 			for (i = 0; i < NBANKS; i++) {
165 				b = &bank[i];
166 				printf("\nBank %d:\n", i);
167 				hit = getintpar("units");
168 				if (hit < 0)
169 					return;
170 				if (hit == 0)
171 					break;
172 				extra += hit;
173 				if (extra > Ship.energy) {
174 					printf("available energy exceeded.  ");
175 					skiptonl(0);
176 					flag++;
177 					break;
178 				}
179 				b->units = hit;
180 				hit = getintpar("course");
181 				if (hit < 0 || hit > 360)
182 					return;
183 				b->angle = hit * 0.0174532925;
184 				b->spread = getfltpar("spread");
185 				if (b->spread < 0 || b->spread > 1)
186 					return;
187 			}
188 			Ship.energy -= extra;
189 		}
190 		extra = 0;
191 	} else {
192 		/* automatic distribution of power */
193 		if (Etc.nkling <= 0) {
194 			printf("Sulu: But there are no Klingons in this "
195 			       "quadrant\n");
196 			return;
197 		}
198 		printf("Phasers locked on target.  ");
199 		while (flag) {
200 			printf("%d units available\n", Ship.energy);
201 			hit = getintpar("Units to fire");
202 			if (hit <= 0)
203 				return;
204 			if (hit > Ship.energy) {
205 				printf("available energy exceeded.  ");
206 				skiptonl(0);
207 				continue;
208 			}
209 			flag = 0;
210 			Ship.energy -= hit;
211 			extra = hit;
212 			n = Etc.nkling;
213 			if (n > NBANKS)
214 				n = NBANKS;
215 			tot = n * (n + 1) / 2;
216 			for (i = 0; i < n; i++) {
217 				k = &Etc.klingon[i];
218 				b = &bank[i];
219 				distfactor = k->dist;
220 				anglefactor = ALPHA * BETA * OMEGA /
221 					(distfactor * distfactor + EPSILON);
222 				anglefactor *= GAMMA;
223 				distfactor = k->power;
224 				distfactor /= anglefactor;
225 				hitreqd[i] = distfactor + 0.5;
226 				dx = Ship.sectx - k->x;
227 				dy = k->y - Ship.secty;
228 				b->angle = atan2(dy, dx);
229 				b->spread = 0.0;
230 				b->units = ((n - i) / tot) * extra;
231 #ifdef xTRACE
232 				if (Trace) {
233 					printf("b%d hr%d u%d df%.2f af%.2f\n",
234 						i, hitreqd[i], b->units,
235 						distfactor, anglefactor);
236 				}
237 #endif
238 				extra -= b->units;
239 				hit = b->units - hitreqd[i];
240 				if (hit > 0) {
241 					extra += hit;
242 					b->units -= hit;
243 				}
244 			}
245 
246 			/* give out any extra energy we might have around */
247 			if (extra > 0) {
248 				for (i = 0; i < n; i++) {
249 					b = &bank[i];
250 					hit = hitreqd[i] - b->units;
251 					if (hit <= 0)
252 						continue;
253 					if (hit >= extra) {
254 						b->units += extra;
255 						extra = 0;
256 						break;
257 					}
258 					b->units = hitreqd[i];
259 					extra -= hit;
260 				}
261 				if (extra > 0)
262 					printf("%d units overkill\n", extra);
263 			}
264 		}
265 	}
266 
267 #ifdef xTRACE
268 	if (Trace) {
269 		for (i = 0; i < NBANKS; i++) {
270 			b = &bank[i];
271 			printf("b%d u%d", i, b->units);
272 			if (b->units > 0)
273 				printf(" a%.2f s%.2f\n", b->angle, b->spread);
274 			else
275 				printf("\n");
276 		}
277 	}
278 #endif
279 
280 	/* actually fire the shots */
281 	Move.free = 0;
282 	for (i = 0; i < NBANKS; i++) {
283 		b = &bank[i];
284 		if (b->units <= 0) {
285 			continue;
286 		}
287 		printf("\nPhaser bank %d fires:\n", i);
288 		n = Etc.nkling;
289 		k = Etc.klingon;
290 		for (j = 0; j < n; j++) {
291 			if (b->units <= 0)
292 				break;
293 			/*
294 			** The formula for hit is as follows:
295 			**
296 			**  zap = OMEGA * [(sigma + ALPHA) * (rho + BETA)]
297 			**	/ (dist ** 2 + EPSILON)]
298 			**	* [cos(delta * sigma) + GAMMA]
299 			**	* hit
300 			**
301 			** where sigma is the spread factor,
302 			** rho is a random number (0 -> 1),
303 			** GAMMA is a crud factor for angle (essentially
304 			**	cruds up the spread factor),
305 			** delta is the difference in radians between the
306 			**	angle you are shooting at and the actual
307 			**	angle of the klingon,
308 			** ALPHA scales down the significance of sigma,
309 			** BETA scales down the significance of rho,
310 			** OMEGA is the magic number which makes everything
311 			**	up to "* hit" between zero and one,
312 			** dist is the distance to the klingon
313 			** hit is the number of units in the bank, and
314 			** zap is the amount of the actual hit.
315 			**
316 			** Everything up through dist squared should maximize
317 			** at 1.0, so that the distance factor is never
318 			** greater than one.  Conveniently, cos() is
319 			** never greater than one, but the same restric-
320 			** tion applies.
321 			*/
322 			distfactor = BETA + franf();
323 			distfactor *= ALPHA + b->spread;
324 			distfactor *= OMEGA;
325 			anglefactor = k->dist;
326 			distfactor /= anglefactor * anglefactor + EPSILON;
327 			distfactor *= b->units;
328 			dx = Ship.sectx - k->x;
329 			dy = k->y - Ship.secty;
330 			anglefactor = atan2(dy, dx) - b->angle;
331 			anglefactor = cos((anglefactor * b->spread) + GAMMA);
332 			if (anglefactor < 0.0) {
333 				k++;
334 				continue;
335 			}
336 			hit = anglefactor * distfactor + 0.5;
337 			k->power -= hit;
338 			printf("%d unit hit on Klingon", hit);
339 			if (!damaged(SRSCAN))
340 				printf(" at %d,%d", k->x, k->y);
341 			printf("\n");
342 			b->units -= hit;
343 			if (k->power <= 0) {
344 				killk(k->x, k->y);
345 				continue;
346 			}
347 			k++;
348 		}
349 	}
350 
351 	/* compute overkill */
352 	for (i = 0; i < NBANKS; i++)
353 		extra += bank[i].units;
354 	if (extra > 0)
355 		printf("\n%d units expended on empty space\n", extra);
356 }
357