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