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