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