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