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