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