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