Merge branch 'vendor/ZSTD' into master
[dragonfly.git] / games / trek / phaser.c
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 }