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