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