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