xref: /openbsd/games/trek/phaser.c (revision 11da2480)
1 /*	$OpenBSD: phaser.c,v 1.10 2016/01/07 14:37:51 mestre Exp $	*/
2 /*	$NetBSD: phaser.c,v 1.4 1995/04/24 12:26:02 cgd Exp $	*/
3 
4 /*
5  * Copyright (c) 1980, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <math.h>
34 #include <stdio.h>
35 
36 #include "getpar.h"
37 #include "trek.h"
38 
39 /* factors for phaser hits; see description below */
40 
41 # define	ALPHA		3.0		/* spread */
42 # define	BETA		3.0		/* franf() */
43 # define	GAMMA		0.30		/* cos(angle) */
44 # define	EPSILON		150.0		/* dist ** 2 */
45 # define	OMEGA		10.596		/* overall scaling factor */
46 
47 /* OMEGA ~= 100 * (ALPHA + 1) * (BETA + 1) / (EPSILON + 1) */
48 
49 /*
50 **  Phaser Control
51 **
52 **	There are up to NBANKS phaser banks which may be fired
53 **	simultaneously.  There are two modes, "manual" and
54 **	"automatic".  In manual mode, you specify exactly which
55 **	direction you want each bank to be aimed, the number
56 **	of units to fire, and the spread angle.  In automatic
57 **	mode, you give only the total number of units to fire.
58 **
59 **	The spread is specified as a number between zero and
60 **	one, with zero being minimum spread and one being maximum
61 **	spread.  You  will normally want zero spread, unless your
62 **	short range scanners are out, in which case you probably
63 **	don't know exactly where the Klingons are.  In that case,
64 **	you really don't have any choice except to specify a
65 **	fairly large spread.
66 **
67 **	Phasers spread slightly, even if you specify zero spread.
68 */
69 
70 const struct cvntab	Matab[] =
71 {
72 	{ "m",		"anual",	(cmdfun)1,	0 },
73 	{ "a",		"utomatic",	(cmdfun)0,	0 },
74 	{ NULL,		NULL,		NULL,		0 }
75 };
76 
77 struct banks
78 {
79 	int	units;
80 	double	angle;
81 	double	spread;
82 };
83 
84 
85 void
phaser(int v)86 phaser(int v)
87 {
88 	int		i, j;
89 	struct kling	*k;
90 	double		dx, dy;
91 	double		anglefactor, distfactor;
92 	struct banks	*b;
93 	int		manual, flag, extra;
94 	int		hit;
95 	double		tot;
96 	int		n;
97 	int		hitreqd[NBANKS];
98 	struct banks	bank[NBANKS];
99 	const struct cvntab	*ptr;
100 
101 	if (Ship.cond == DOCKED)
102 	{
103 		printf("Phasers cannot fire through starbase shields\n");
104 		return;
105 	}
106 	if (damaged(PHASER))
107 	{
108 		out(PHASER);
109 		return;
110 	}
111 	if (Ship.shldup)
112 	{
113 		printf("Sulu: Captain, we cannot fire through shields.\n");
114 		return;
115 	}
116 	if (Ship.cloaked)
117 	{
118 		printf("Sulu: Captain, surely you must realize that we cannot fire\n");
119 		printf("  phasers with the cloaking device up.\n");
120 		return;
121 	}
122 
123 	/* decide if we want manual or automatic mode */
124 	manual = 0;
125 	if (testnl())
126 	{
127 		if (damaged(COMPUTER))
128 		{
129 			printf("%s", Device[COMPUTER].name);
130 			manual++;
131 		}
132 		else
133 			if (damaged(SRSCAN))
134 			{
135 				printf("%s", Device[SRSCAN].name);
136 				manual++;
137 			}
138 		if (manual)
139 			printf(" damaged, manual mode selected\n");
140 	}
141 
142 	if (!manual)
143 	{
144 		ptr = getcodpar("Manual or automatic", Matab);
145 		manual = (long) ptr->value;
146 	}
147 	if (!manual && damaged(COMPUTER))
148 	{
149 		printf("Computer damaged, manual selected\n");
150 		skiptonl(0);
151 		manual++;
152 	}
153 
154 	/* initialize the bank[] array */
155 	flag = 1;
156 	for (i = 0; i < NBANKS; i++)
157 		bank[i].units = 0;
158 	if (manual)
159 	{
160 		/* collect manual mode statistics */
161 		while (flag)
162 		{
163 			printf("%d units available\n", Ship.energy);
164 			extra = 0;
165 			flag = 0;
166 			for (i = 0; i < NBANKS; i++)
167 			{
168 				b = &bank[i];
169 				printf("\nBank %d:\n", i);
170 				hit = getintpar("units");
171 				if (hit < 0)
172 					return;
173 				if (hit == 0)
174 					break;
175 				extra += hit;
176 				if (extra > Ship.energy)
177 				{
178 					printf("available energy exceeded.  ");
179 					skiptonl(0);
180 					flag++;
181 					break;
182 				}
183 				b->units = hit;
184 				hit = getintpar("course");
185 				if (hit < 0 || hit > 360)
186 					return;
187 				b->angle = hit * 0.0174532925;
188 				b->spread = getfltpar("spread");
189 				if (b->spread < 0 || b->spread > 1)
190 					return;
191 			}
192 			Ship.energy -= extra;
193 		}
194 		extra = 0;
195 	}
196 	else
197 	{
198 		/* automatic distribution of power */
199 		if (Etc.nkling <= 0)
200 		{
201 			printf("Sulu: But there are no Klingons in this quadrant\n");
202 			return;
203 		}
204 		printf("Phasers locked on target.  ");
205 		while (flag)
206 		{
207 			printf("%d units available\n", Ship.energy);
208 			hit = getintpar("Units to fire");
209 			if (hit <= 0)
210 				return;
211 			if (hit > Ship.energy)
212 			{
213 				printf("available energy exceeded.  ");
214 				skiptonl(0);
215 				continue;
216 			}
217 			flag = 0;
218 			Ship.energy -= hit;
219 			extra = hit;
220 			n = Etc.nkling;
221 			if (n > NBANKS)
222 				n = NBANKS;
223 			tot = n * (n + 1) / 2;
224 			for (i = 0; i < n; i++)
225 			{
226 				k = &Etc.klingon[i];
227 				b = &bank[i];
228 				distfactor = k->dist;
229 				anglefactor = ALPHA * BETA * OMEGA / (distfactor * distfactor + EPSILON);
230 				anglefactor *= GAMMA;
231 				distfactor = k->power;
232 				distfactor /= anglefactor;
233 				hitreqd[i] = distfactor + 0.5;
234 				dx = Ship.sectx - k->x;
235 				dy = k->y - Ship.secty;
236 				b->angle = atan2(dy, dx);
237 				b->spread = 0.0;
238 				b->units = ((n - i) / tot) * extra;
239 #				ifdef xTRACE
240 				if (Trace)
241 				{
242 					printf("b%d hr%d u%d df%.2f af%.2f\n",
243 						i, hitreqd[i], b->units,
244 						distfactor, anglefactor);
245 				}
246 #				endif
247 				extra -= b->units;
248 				hit = b->units - hitreqd[i];
249 				if (hit > 0)
250 				{
251 					extra += hit;
252 					b->units -= hit;
253 				}
254 			}
255 
256 			/* give out any extra energy we might have around */
257 			if (extra > 0)
258 			{
259 				for (i = 0; i < n; i++)
260 				{
261 					b = &bank[i];
262 					hit = hitreqd[i] - b->units;
263 					if (hit <= 0)
264 						continue;
265 					if (hit >= extra)
266 					{
267 						b->units += extra;
268 						extra = 0;
269 						break;
270 					}
271 					b->units = hitreqd[i];
272 					extra -= hit;
273 				}
274 				if (extra > 0)
275 					printf("%d units overkill\n", extra);
276 			}
277 		}
278 	}
279 
280 #	ifdef xTRACE
281 	if (Trace)
282 	{
283 		for (i = 0; i < NBANKS; i++)
284 		{
285 			b = &bank[i];
286 			printf("b%d u%d", i, b->units);
287 			if (b->units > 0)
288 				printf(" a%.2f s%.2f\n", b->angle, b->spread);
289 			else
290 				printf("\n");
291 		}
292 	}
293 #	endif
294 
295 	/* actually fire the shots */
296 	Move.free = 0;
297 	for (i = 0; i < NBANKS; i++)
298 	{
299 		b = &bank[i];
300 		if (b->units <= 0)
301 		{
302 			continue;
303 		}
304 		printf("\nPhaser bank %d fires:\n", i);
305 		n = Etc.nkling;
306 		k = Etc.klingon;
307 		for (j = 0; j < n; j++)
308 		{
309 			if (b->units <= 0)
310 				break;
311 			/*
312 			** The formula for hit is as follows:
313 			**
314 			**  zap = OMEGA * [(sigma + ALPHA) * (rho + BETA)]
315 			**	/ (dist ** 2 + EPSILON)]
316 			**	* [cos(delta * sigma) + GAMMA]
317 			**	* hit
318 			**
319 			** where sigma is the spread factor,
320 			** rho is a random number (0 -> 1),
321 			** GAMMA is a crud factor for angle (essentially
322 			**	cruds up the spread factor),
323 			** delta is the difference in radians between the
324 			**	angle you are shooting at and the actual
325 			**	angle of the klingon,
326 			** ALPHA scales down the significance of sigma,
327 			** BETA scales down the significance of rho,
328 			** OMEGA is the magic number which makes everything
329 			**	up to "* hit" between zero and one,
330 			** dist is the distance to the klingon
331 			** hit is the number of units in the bank, and
332 			** zap is the amount of the actual hit.
333 			**
334 			** Everything up through dist squared should maximize
335 			** at 1.0, so that the distance factor is never
336 			** greater than one.  Conveniently, cos() is
337 			** never greater than one, but the same restric-
338 			** tion applies.
339 			*/
340 			distfactor = BETA + franf();
341 			distfactor *= ALPHA + b->spread;
342 			distfactor *= OMEGA;
343 			anglefactor = k->dist;
344 			distfactor /= anglefactor * anglefactor + EPSILON;
345 			distfactor *= b->units;
346 			dx = Ship.sectx - k->x;
347 			dy = k->y - Ship.secty;
348 			anglefactor = atan2(dy, dx) - b->angle;
349 			anglefactor = cos((anglefactor * b->spread) + GAMMA);
350 			if (anglefactor < 0.0)
351 			{
352 				k++;
353 				continue;
354 			}
355 			hit = anglefactor * distfactor + 0.5;
356 			k->power -= hit;
357 			printf("%d unit hit on Klingon", hit);
358 			if (!damaged(SRSCAN))
359 				printf(" at %d,%d", k->x, k->y);
360 			printf("\n");
361 			b->units -= hit;
362 			if (k->power <= 0)
363 			{
364 				killk(k->x, k->y);
365 				continue;
366 			}
367 			k++;
368 		}
369 	}
370 
371 	/* compute overkill */
372 	for (i = 0; i < NBANKS; i++)
373 		extra += bank[i].units;
374 	if (extra > 0)
375 		printf("\n%d units expended on empty space\n", extra);
376 }
377