125994Smckusick /* 225994Smckusick * Copyright (c) 1980 Regents of the University of California. 334205Sbostic * All rights reserved. 434205Sbostic * 5*42606Sbostic * %sccs.include.redist.c% 625994Smckusick */ 725994Smckusick 811687Smckusick #ifndef lint 9*42606Sbostic static char sccsid[] = "@(#)phaser.c 5.4 (Berkeley) 06/01/90"; 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 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