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