125994Smckusick /*
2*60859Sbostic * Copyright (c) 1980, 1993
3*60859Sbostic * The Regents of the University of California. All rights reserved.
434205Sbostic *
542606Sbostic * %sccs.include.redist.c%
625994Smckusick */
725994Smckusick
811687Smckusick #ifndef lint
9*60859Sbostic static char sccsid[] = "@(#)phaser.c 8.1 (Berkeley) 05/31/93";
1034205Sbostic #endif /* not lint */
1111687Smckusick
1211687Smckusick # include "trek.h"
1311687Smckusick # include "getpar.h"
1411687Smckusick
1511687Smckusick /* factors for phaser hits; see description below */
1611687Smckusick
1711687Smckusick # define ALPHA 3.0 /* spread */
1811687Smckusick # define BETA 3.0 /* franf() */
1911687Smckusick # define GAMMA 0.30 /* cos(angle) */
2011687Smckusick # define EPSILON 150.0 /* dist ** 2 */
2111687Smckusick # define OMEGA 10.596 /* overall scaling factor */
2211687Smckusick
2311687Smckusick /* OMEGA ~= 100 * (ALPHA + 1) * (BETA + 1) / (EPSILON + 1) */
2411687Smckusick
2511687Smckusick /*
2611687Smckusick ** Phaser Control
2711687Smckusick **
2811687Smckusick ** There are up to NBANKS phaser banks which may be fired
2911687Smckusick ** simultaneously. There are two modes, "manual" and
3011687Smckusick ** "automatic". In manual mode, you specify exactly which
3111687Smckusick ** direction you want each bank to be aimed, the number
3211687Smckusick ** of units to fire, and the spread angle. In automatic
3311687Smckusick ** mode, you give only the total number of units to fire.
3411687Smckusick **
3511687Smckusick ** The spread is specified as a number between zero and
3611687Smckusick ** one, with zero being minimum spread and one being maximum
3711687Smckusick ** spread. You will normally want zero spread, unless your
3811687Smckusick ** short range scanners are out, in which case you probably
3911687Smckusick ** don't know exactly where the Klingons are. In that case,
4011687Smckusick ** you really don't have any choice except to specify a
4111687Smckusick ** fairly large spread.
4211687Smckusick **
4311687Smckusick ** Phasers spread slightly, even if you specify zero spread.
4411687Smckusick **
4511687Smckusick ** Uses trace flag 30
4611687Smckusick */
4711687Smckusick
4812798Slayer struct cvntab Matab[] =
4911687Smckusick {
5012798Slayer "m", "anual", (int (*)())1, 0,
5111687Smckusick "a", "utomatic", 0, 0,
5211687Smckusick 0
5311687Smckusick };
5411687Smckusick
5511687Smckusick struct banks
5611687Smckusick {
5711687Smckusick int units;
5812798Slayer double angle;
5912798Slayer double spread;
6011687Smckusick };
6111687Smckusick
6211687Smckusick
6311687Smckusick
phaser()6411687Smckusick phaser()
6511687Smckusick {
6611687Smckusick register int i;
6711687Smckusick int j;
6811687Smckusick register struct kling *k;
6911687Smckusick double dx, dy;
7011687Smckusick double anglefactor, distfactor;
7111687Smckusick register struct banks *b;
7211687Smckusick int manual, flag, extra;
7311687Smckusick int hit;
7411687Smckusick double tot;
7511687Smckusick int n;
7611687Smckusick int hitreqd[NBANKS];
7711687Smckusick struct banks bank[NBANKS];
7811687Smckusick struct cvntab *ptr;
7911687Smckusick
8011687Smckusick if (Ship.cond == DOCKED)
8111687Smckusick return(printf("Phasers cannot fire through starbase shields\n"));
8211687Smckusick if (damaged(PHASER))
8311687Smckusick return (out(PHASER));
8411687Smckusick if (Ship.shldup)
8511687Smckusick return (printf("Sulu: Captain, we cannot fire through shields.\n"));
8611687Smckusick if (Ship.cloaked)
8711687Smckusick {
8811687Smckusick printf("Sulu: Captain, surely you must realize that we cannot fire\n");
8911687Smckusick printf(" phasers with the cloaking device up.\n");
9011687Smckusick return;
9111687Smckusick }
9211687Smckusick
9311687Smckusick /* decide if we want manual or automatic mode */
9411687Smckusick manual = 0;
9511687Smckusick if (testnl())
9611687Smckusick {
9711687Smckusick if (damaged(COMPUTER))
9811687Smckusick {
9911687Smckusick printf(Device[COMPUTER].name);
10011687Smckusick manual++;
10111687Smckusick }
10211687Smckusick else
10311687Smckusick if (damaged(SRSCAN))
10411687Smckusick {
10511687Smckusick printf(Device[SRSCAN].name);
10611687Smckusick manual++;
10711687Smckusick }
10811687Smckusick if (manual)
10911687Smckusick printf(" damaged, manual mode selected\n");
11011687Smckusick }
11111687Smckusick
11211687Smckusick if (!manual)
11311687Smckusick {
11411687Smckusick ptr = getcodpar("Manual or automatic", Matab);
11512798Slayer manual = (int) ptr->value;
11611687Smckusick }
11711687Smckusick if (!manual && damaged(COMPUTER))
11811687Smckusick {
11911687Smckusick printf("Computer damaged, manual selected\n");
12011687Smckusick skiptonl(0);
12111687Smckusick manual++;
12211687Smckusick }
12311687Smckusick
12411687Smckusick /* initialize the bank[] array */
12511687Smckusick flag = 1;
12611687Smckusick for (i = 0; i < NBANKS; i++)
12711687Smckusick bank[i].units = 0;
12811687Smckusick if (manual)
12911687Smckusick {
13011687Smckusick /* collect manual mode statistics */
13111687Smckusick while (flag)
13211687Smckusick {
13311687Smckusick printf("%d units available\n", Ship.energy);
13411687Smckusick extra = 0;
13511687Smckusick flag = 0;
13611687Smckusick for (i = 0; i < NBANKS; i++)
13711687Smckusick {
13811687Smckusick b = &bank[i];
13911687Smckusick printf("\nBank %d:\n", i);
14011687Smckusick hit = getintpar("units");
14111687Smckusick if (hit < 0)
14211687Smckusick return;
14311687Smckusick if (hit == 0)
14411687Smckusick break;
14512798Slayer extra += hit;
14611687Smckusick if (extra > Ship.energy)
14711687Smckusick {
14811687Smckusick printf("available energy exceeded. ");
14911687Smckusick skiptonl(0);
15011687Smckusick flag++;
15111687Smckusick break;
15211687Smckusick }
15311687Smckusick b->units = hit;
15411687Smckusick hit = getintpar("course");
15511687Smckusick if (hit < 0 || hit > 360)
15611687Smckusick return;
15711687Smckusick b->angle = hit * 0.0174532925;
15811687Smckusick b->spread = getfltpar("spread");
15911687Smckusick if (b->spread < 0 || b->spread > 1)
16011687Smckusick return;
16111687Smckusick }
16212798Slayer Ship.energy -= extra;
16311687Smckusick }
16411687Smckusick extra = 0;
16511687Smckusick }
16611687Smckusick else
16711687Smckusick {
16811687Smckusick /* automatic distribution of power */
16911687Smckusick if (Etc.nkling <= 0)
17011687Smckusick return (printf("Sulu: But there are no Klingons in this quadrant\n"));
17111687Smckusick printf("Phasers locked on target. ");
17211687Smckusick while (flag)
17311687Smckusick {
17411687Smckusick printf("%d units available\n", Ship.energy);
17511687Smckusick hit = getintpar("Units to fire");
17611687Smckusick if (hit <= 0)
17711687Smckusick return;
17811687Smckusick if (hit > Ship.energy)
17911687Smckusick {
18011687Smckusick printf("available energy exceeded. ");
18111687Smckusick skiptonl(0);
18211687Smckusick continue;
18311687Smckusick }
18411687Smckusick flag = 0;
18512798Slayer Ship.energy -= hit;
18611687Smckusick extra = hit;
18711687Smckusick n = Etc.nkling;
18811687Smckusick if (n > NBANKS)
18911687Smckusick n = NBANKS;
19011687Smckusick tot = n * (n + 1) / 2;
19111687Smckusick for (i = 0; i < n; i++)
19211687Smckusick {
19311687Smckusick k = &Etc.klingon[i];
19411687Smckusick b = &bank[i];
19511687Smckusick distfactor = k->dist;
19611687Smckusick anglefactor = ALPHA * BETA * OMEGA / (distfactor * distfactor + EPSILON);
19712798Slayer anglefactor *= GAMMA;
19811687Smckusick distfactor = k->power;
19912798Slayer distfactor /= anglefactor;
20011687Smckusick hitreqd[i] = distfactor + 0.5;
20111687Smckusick dx = Ship.sectx - k->x;
20211687Smckusick dy = k->y - Ship.secty;
20311687Smckusick b->angle = atan2(dy, dx);
20411687Smckusick b->spread = 0.0;
20511687Smckusick b->units = ((n - i) / tot) * extra;
20611687Smckusick # ifdef xTRACE
20711687Smckusick if (Trace)
20811687Smckusick {
20911687Smckusick printf("b%d hr%d u%d df%.2f af%.2f\n",
21011687Smckusick i, hitreqd[i], b->units,
21111687Smckusick distfactor, anglefactor);
21211687Smckusick }
21311687Smckusick # endif
21412798Slayer extra -= b->units;
21511687Smckusick hit = b->units - hitreqd[i];
21611687Smckusick if (hit > 0)
21711687Smckusick {
21812798Slayer extra += hit;
21912798Slayer b->units -= hit;
22011687Smckusick }
22111687Smckusick }
22211687Smckusick
22311687Smckusick /* give out any extra energy we might have around */
22411687Smckusick if (extra > 0)
22511687Smckusick {
22611687Smckusick for (i = 0; i < n; i++)
22711687Smckusick {
22811687Smckusick b = &bank[i];
22911687Smckusick hit = hitreqd[i] - b->units;
23011687Smckusick if (hit <= 0)
23111687Smckusick continue;
23211687Smckusick if (hit >= extra)
23311687Smckusick {
23412798Slayer b->units += extra;
23511687Smckusick extra = 0;
23611687Smckusick break;
23711687Smckusick }
23811687Smckusick b->units = hitreqd[i];
23912798Slayer extra -= hit;
24011687Smckusick }
24111687Smckusick if (extra > 0)
24211687Smckusick printf("%d units overkill\n", extra);
24311687Smckusick }
24411687Smckusick }
24511687Smckusick }
24611687Smckusick
24711687Smckusick # ifdef xTRACE
24811687Smckusick if (Trace)
24911687Smckusick {
25011687Smckusick for (i = 0; i < NBANKS; i++)
25111687Smckusick {
25211687Smckusick b = &bank[i];
25311687Smckusick printf("b%d u%d", i, b->units);
25411687Smckusick if (b->units > 0)
25511687Smckusick printf(" a%.2f s%.2f\n", b->angle, b->spread);
25611687Smckusick else
25711687Smckusick printf("\n");
25811687Smckusick }
25911687Smckusick }
26011687Smckusick # endif
26111687Smckusick
26211687Smckusick /* actually fire the shots */
26311687Smckusick Move.free = 0;
26411687Smckusick for (i = 0; i < NBANKS; i++)
26511687Smckusick {
26611687Smckusick b = &bank[i];
26711687Smckusick if (b->units <= 0)
26811687Smckusick {
26911687Smckusick continue;
27011687Smckusick }
27111687Smckusick printf("\nPhaser bank %d fires:\n", i);
27211687Smckusick n = Etc.nkling;
27311687Smckusick k = Etc.klingon;
27411687Smckusick for (j = 0; j < n; j++)
27511687Smckusick {
27611687Smckusick if (b->units <= 0)
27711687Smckusick break;
27811687Smckusick /*
27911687Smckusick ** The formula for hit is as follows:
28011687Smckusick **
28111687Smckusick ** zap = OMEGA * [(sigma + ALPHA) * (rho + BETA)]
28211687Smckusick ** / (dist ** 2 + EPSILON)]
28311687Smckusick ** * [cos(delta * sigma) + GAMMA]
28411687Smckusick ** * hit
28511687Smckusick **
28611687Smckusick ** where sigma is the spread factor,
28711687Smckusick ** rho is a random number (0 -> 1),
28811687Smckusick ** GAMMA is a crud factor for angle (essentially
28911687Smckusick ** cruds up the spread factor),
29011687Smckusick ** delta is the difference in radians between the
29111687Smckusick ** angle you are shooting at and the actual
29211687Smckusick ** angle of the klingon,
29311687Smckusick ** ALPHA scales down the significance of sigma,
29411687Smckusick ** BETA scales down the significance of rho,
29511687Smckusick ** OMEGA is the magic number which makes everything
29611687Smckusick ** up to "* hit" between zero and one,
29711687Smckusick ** dist is the distance to the klingon
29811687Smckusick ** hit is the number of units in the bank, and
29911687Smckusick ** zap is the amount of the actual hit.
30011687Smckusick **
30111687Smckusick ** Everything up through dist squared should maximize
30211687Smckusick ** at 1.0, so that the distance factor is never
30311687Smckusick ** greater than one. Conveniently, cos() is
30411687Smckusick ** never greater than one, but the same restric-
30511687Smckusick ** tion applies.
30611687Smckusick */
30711687Smckusick distfactor = BETA + franf();
30812798Slayer distfactor *= ALPHA + b->spread;
30912798Slayer distfactor *= OMEGA;
31011687Smckusick anglefactor = k->dist;
31112798Slayer distfactor /= anglefactor * anglefactor + EPSILON;
31212798Slayer distfactor *= b->units;
31311687Smckusick dx = Ship.sectx - k->x;
31411687Smckusick dy = k->y - Ship.secty;
31511687Smckusick anglefactor = atan2(dy, dx) - b->angle;
31611687Smckusick anglefactor = cos((anglefactor * b->spread) + GAMMA);
31711687Smckusick if (anglefactor < 0.0)
31811687Smckusick {
31911687Smckusick k++;
32011687Smckusick continue;
32111687Smckusick }
32211687Smckusick hit = anglefactor * distfactor + 0.5;
32312798Slayer k->power -= hit;
32411687Smckusick printf("%d unit hit on Klingon", hit);
32511687Smckusick if (!damaged(SRSCAN))
32611687Smckusick printf(" at %d,%d", k->x, k->y);
32711687Smckusick printf("\n");
32812798Slayer b->units -= hit;
32911687Smckusick if (k->power <= 0)
33011687Smckusick {
33111687Smckusick killk(k->x, k->y);
33211687Smckusick continue;
33311687Smckusick }
33411687Smckusick k++;
33511687Smckusick }
33611687Smckusick }
33711687Smckusick
33811687Smckusick /* compute overkill */
33911687Smckusick for (i = 0; i < NBANKS; i++)
34012798Slayer extra += bank[i].units;
34111687Smckusick if (extra > 0)
34211687Smckusick printf("\n%d units expended on empty space\n", extra);
34311687Smckusick }
344