xref: /original-bsd/games/trek/phaser.c (revision f82e54c4)
1 #ifndef lint
2 static char sccsid[] = "@(#)phaser.c	4.2	(Berkeley)	05/27/83";
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 
41 struct cvntab	Matab[] =
42 {
43 	"m",		"anual",		(int (*)())1,		0,
44 	"a",		"utomatic",		0,		0,
45 	0
46 };
47 
48 struct banks
49 {
50 	int	units;
51 	double	angle;
52 	double	spread;
53 };
54 
55 
56 
57 phaser()
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);
108 		manual = (int) ptr->value;
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;
138 				extra += hit;
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 			}
155 			Ship.energy -= extra;
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;
178 			Ship.energy -= hit;
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);
190 				anglefactor *= GAMMA;
191 				distfactor = k->power;
192 				distfactor /= anglefactor;
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
207 				extra -= b->units;
208 				hit = b->units - hitreqd[i];
209 				if (hit > 0)
210 				{
211 					extra += hit;
212 					b->units -= hit;
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 					{
227 						b->units += extra;
228 						extra = 0;
229 						break;
230 					}
231 					b->units = hitreqd[i];
232 					extra -= hit;
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();
301 			distfactor *= ALPHA + b->spread;
302 			distfactor *= OMEGA;
303 			anglefactor = k->dist;
304 			distfactor /= anglefactor * anglefactor + EPSILON;
305 			distfactor *= b->units;
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;
316 			k->power -= hit;
317 			printf("%d unit hit on Klingon", hit);
318 			if (!damaged(SRSCAN))
319 				printf(" at %d,%d", k->x, k->y);
320 			printf("\n");
321 			b->units -= hit;
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++)
333 		extra += bank[i].units;
334 	if (extra > 0)
335 		printf("\n%d units expended on empty space\n", extra);
336 }
337