add Berkeley copyright
[unix-history] / usr / src / games / trek / phaser.c
CommitLineData
9758240b
KM
1/*
2 * Copyright (c) 1980 Regents of the University of California.
e9fb6bea
KB
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
d99e6414
KB
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
9758240b
KM
16 */
17
c7124754 18#ifndef lint
d99e6414 19static char sccsid[] = "@(#)phaser.c 5.3 (Berkeley) %G%";
e9fb6bea 20#endif /* not lint */
c7124754
KM
21
22# include "trek.h"
23# include "getpar.h"
24
25/* factors for phaser hits; see description below */
26
27# define ALPHA 3.0 /* spread */
28# define BETA 3.0 /* franf() */
29# define GAMMA 0.30 /* cos(angle) */
30# define EPSILON 150.0 /* dist ** 2 */
31# define OMEGA 10.596 /* overall scaling factor */
32
33/* OMEGA ~= 100 * (ALPHA + 1) * (BETA + 1) / (EPSILON + 1) */
34
35/*
36** Phaser Control
37**
38** There are up to NBANKS phaser banks which may be fired
39** simultaneously. There are two modes, "manual" and
40** "automatic". In manual mode, you specify exactly which
41** direction you want each bank to be aimed, the number
42** of units to fire, and the spread angle. In automatic
43** mode, you give only the total number of units to fire.
44**
45** The spread is specified as a number between zero and
46** one, with zero being minimum spread and one being maximum
47** spread. You will normally want zero spread, unless your
48** short range scanners are out, in which case you probably
49** don't know exactly where the Klingons are. In that case,
50** you really don't have any choice except to specify a
51** fairly large spread.
52**
53** Phasers spread slightly, even if you specify zero spread.
54**
55** Uses trace flag 30
56*/
57
401a08c7 58struct cvntab Matab[] =
c7124754 59{
401a08c7 60 "m", "anual", (int (*)())1, 0,
c7124754
KM
61 "a", "utomatic", 0, 0,
62 0
63};
64
65struct banks
66{
67 int units;
401a08c7
KL
68 double angle;
69 double spread;
c7124754
KM
70};
71
72
73
74phaser()
75{
76 register int i;
77 int j;
78 register struct kling *k;
79 double dx, dy;
80 double anglefactor, distfactor;
81 register struct banks *b;
82 int manual, flag, extra;
83 int hit;
84 double tot;
85 int n;
86 int hitreqd[NBANKS];
87 struct banks bank[NBANKS];
88 struct cvntab *ptr;
89
90 if (Ship.cond == DOCKED)
91 return(printf("Phasers cannot fire through starbase shields\n"));
92 if (damaged(PHASER))
93 return (out(PHASER));
94 if (Ship.shldup)
95 return (printf("Sulu: Captain, we cannot fire through shields.\n"));
96 if (Ship.cloaked)
97 {
98 printf("Sulu: Captain, surely you must realize that we cannot fire\n");
99 printf(" phasers with the cloaking device up.\n");
100 return;
101 }
102
103 /* decide if we want manual or automatic mode */
104 manual = 0;
105 if (testnl())
106 {
107 if (damaged(COMPUTER))
108 {
109 printf(Device[COMPUTER].name);
110 manual++;
111 }
112 else
113 if (damaged(SRSCAN))
114 {
115 printf(Device[SRSCAN].name);
116 manual++;
117 }
118 if (manual)
119 printf(" damaged, manual mode selected\n");
120 }
121
122 if (!manual)
123 {
124 ptr = getcodpar("Manual or automatic", Matab);
401a08c7 125 manual = (int) ptr->value;
c7124754
KM
126 }
127 if (!manual && damaged(COMPUTER))
128 {
129 printf("Computer damaged, manual selected\n");
130 skiptonl(0);
131 manual++;
132 }
133
134 /* initialize the bank[] array */
135 flag = 1;
136 for (i = 0; i < NBANKS; i++)
137 bank[i].units = 0;
138 if (manual)
139 {
140 /* collect manual mode statistics */
141 while (flag)
142 {
143 printf("%d units available\n", Ship.energy);
144 extra = 0;
145 flag = 0;
146 for (i = 0; i < NBANKS; i++)
147 {
148 b = &bank[i];
149 printf("\nBank %d:\n", i);
150 hit = getintpar("units");
151 if (hit < 0)
152 return;
153 if (hit == 0)
154 break;
401a08c7 155 extra += hit;
c7124754
KM
156 if (extra > Ship.energy)
157 {
158 printf("available energy exceeded. ");
159 skiptonl(0);
160 flag++;
161 break;
162 }
163 b->units = hit;
164 hit = getintpar("course");
165 if (hit < 0 || hit > 360)
166 return;
167 b->angle = hit * 0.0174532925;
168 b->spread = getfltpar("spread");
169 if (b->spread < 0 || b->spread > 1)
170 return;
171 }
401a08c7 172 Ship.energy -= extra;
c7124754
KM
173 }
174 extra = 0;
175 }
176 else
177 {
178 /* automatic distribution of power */
179 if (Etc.nkling <= 0)
180 return (printf("Sulu: But there are no Klingons in this quadrant\n"));
181 printf("Phasers locked on target. ");
182 while (flag)
183 {
184 printf("%d units available\n", Ship.energy);
185 hit = getintpar("Units to fire");
186 if (hit <= 0)
187 return;
188 if (hit > Ship.energy)
189 {
190 printf("available energy exceeded. ");
191 skiptonl(0);
192 continue;
193 }
194 flag = 0;
401a08c7 195 Ship.energy -= hit;
c7124754
KM
196 extra = hit;
197 n = Etc.nkling;
198 if (n > NBANKS)
199 n = NBANKS;
200 tot = n * (n + 1) / 2;
201 for (i = 0; i < n; i++)
202 {
203 k = &Etc.klingon[i];
204 b = &bank[i];
205 distfactor = k->dist;
206 anglefactor = ALPHA * BETA * OMEGA / (distfactor * distfactor + EPSILON);
401a08c7 207 anglefactor *= GAMMA;
c7124754 208 distfactor = k->power;
401a08c7 209 distfactor /= anglefactor;
c7124754
KM
210 hitreqd[i] = distfactor + 0.5;
211 dx = Ship.sectx - k->x;
212 dy = k->y - Ship.secty;
213 b->angle = atan2(dy, dx);
214 b->spread = 0.0;
215 b->units = ((n - i) / tot) * extra;
216# ifdef xTRACE
217 if (Trace)
218 {
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
401a08c7 224 extra -= b->units;
c7124754
KM
225 hit = b->units - hitreqd[i];
226 if (hit > 0)
227 {
401a08c7
KL
228 extra += hit;
229 b->units -= hit;
c7124754
KM
230 }
231 }
232
233 /* give out any extra energy we might have around */
234 if (extra > 0)
235 {
236 for (i = 0; i < n; i++)
237 {
238 b = &bank[i];
239 hit = hitreqd[i] - b->units;
240 if (hit <= 0)
241 continue;
242 if (hit >= extra)
243 {
401a08c7 244 b->units += extra;
c7124754
KM
245 extra = 0;
246 break;
247 }
248 b->units = hitreqd[i];
401a08c7 249 extra -= hit;
c7124754
KM
250 }
251 if (extra > 0)
252 printf("%d units overkill\n", extra);
253 }
254 }
255 }
256
257# ifdef xTRACE
258 if (Trace)
259 {
260 for (i = 0; i < NBANKS; i++)
261 {
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 {
276 b = &bank[i];
277 if (b->units <= 0)
278 {
279 continue;
280 }
281 printf("\nPhaser bank %d fires:\n", i);
282 n = Etc.nkling;
283 k = Etc.klingon;
284 for (j = 0; j < n; j++)
285 {
286 if (b->units <= 0)
287 break;
288 /*
289 ** The formula for hit is as follows:
290 **
291 ** zap = OMEGA * [(sigma + ALPHA) * (rho + BETA)]
292 ** / (dist ** 2 + EPSILON)]
293 ** * [cos(delta * sigma) + GAMMA]
294 ** * hit
295 **
296 ** where sigma is the spread factor,
297 ** rho is a random number (0 -> 1),
298 ** GAMMA is a crud factor for angle (essentially
299 ** cruds up the spread factor),
300 ** delta is the difference in radians between the
301 ** angle you are shooting at and the actual
302 ** angle of the klingon,
303 ** ALPHA scales down the significance of sigma,
304 ** BETA scales down the significance of rho,
305 ** OMEGA is the magic number which makes everything
306 ** up to "* hit" between zero and one,
307 ** dist is the distance to the klingon
308 ** hit is the number of units in the bank, and
309 ** zap is the amount of the actual hit.
310 **
311 ** Everything up through dist squared should maximize
312 ** at 1.0, so that the distance factor is never
313 ** greater than one. Conveniently, cos() is
314 ** never greater than one, but the same restric-
315 ** tion applies.
316 */
317 distfactor = BETA + franf();
401a08c7
KL
318 distfactor *= ALPHA + b->spread;
319 distfactor *= OMEGA;
c7124754 320 anglefactor = k->dist;
401a08c7
KL
321 distfactor /= anglefactor * anglefactor + EPSILON;
322 distfactor *= b->units;
c7124754
KM
323 dx = Ship.sectx - k->x;
324 dy = k->y - Ship.secty;
325 anglefactor = atan2(dy, dx) - b->angle;
326 anglefactor = cos((anglefactor * b->spread) + GAMMA);
327 if (anglefactor < 0.0)
328 {
329 k++;
330 continue;
331 }
332 hit = anglefactor * distfactor + 0.5;
401a08c7 333 k->power -= hit;
c7124754
KM
334 printf("%d unit hit on Klingon", hit);
335 if (!damaged(SRSCAN))
336 printf(" at %d,%d", k->x, k->y);
337 printf("\n");
401a08c7 338 b->units -= hit;
c7124754
KM
339 if (k->power <= 0)
340 {
341 killk(k->x, k->y);
342 continue;
343 }
344 k++;
345 }
346 }
347
348 /* compute overkill */
349 for (i = 0; i < NBANKS; i++)
401a08c7 350 extra += bank[i].units;
c7124754
KM
351 if (extra > 0)
352 printf("\n%d units expended on empty space\n", extra);
353}