xref: /original-bsd/games/trek/phaser.c (revision fbb2a877)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
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.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)phaser.c	5.3 (Berkeley) 06/18/88";
20 #endif /* not lint */
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 
58 struct cvntab	Matab[] =
59 {
60 	"m",		"anual",		(int (*)())1,		0,
61 	"a",		"utomatic",		0,		0,
62 	0
63 };
64 
65 struct banks
66 {
67 	int	units;
68 	double	angle;
69 	double	spread;
70 };
71 
72 
73 
74 phaser()
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);
125 		manual = (int) ptr->value;
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;
155 				extra += hit;
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 			}
172 			Ship.energy -= extra;
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;
195 			Ship.energy -= hit;
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);
207 				anglefactor *= GAMMA;
208 				distfactor = k->power;
209 				distfactor /= anglefactor;
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
224 				extra -= b->units;
225 				hit = b->units - hitreqd[i];
226 				if (hit > 0)
227 				{
228 					extra += hit;
229 					b->units -= hit;
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 					{
244 						b->units += extra;
245 						extra = 0;
246 						break;
247 					}
248 					b->units = hitreqd[i];
249 					extra -= hit;
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();
318 			distfactor *= ALPHA + b->spread;
319 			distfactor *= OMEGA;
320 			anglefactor = k->dist;
321 			distfactor /= anglefactor * anglefactor + EPSILON;
322 			distfactor *= b->units;
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;
333 			k->power -= hit;
334 			printf("%d unit hit on Klingon", hit);
335 			if (!damaged(SRSCAN))
336 				printf(" at %d,%d", k->x, k->y);
337 			printf("\n");
338 			b->units -= hit;
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++)
350 		extra += bank[i].units;
351 	if (extra > 0)
352 		printf("\n%d units expended on empty space\n", extra);
353 }
354