1 #ifndef lint 2 static char sccsid[] = "@(#)phaser.c 4.2 (Berkeley) 05/27/83"; 3 #endif not lint 4 5 # include "trek.h" 6 # include "getpar.h" 7 8 /* factors for phaser hits; see description below */ 9 10 # define ALPHA 3.0 /* spread */ 11 # define BETA 3.0 /* franf() */ 12 # define GAMMA 0.30 /* cos(angle) */ 13 # define EPSILON 150.0 /* dist ** 2 */ 14 # define OMEGA 10.596 /* overall scaling factor */ 15 16 /* OMEGA ~= 100 * (ALPHA + 1) * (BETA + 1) / (EPSILON + 1) */ 17 18 /* 19 ** Phaser Control 20 ** 21 ** There are up to NBANKS phaser banks which may be fired 22 ** simultaneously. There are two modes, "manual" and 23 ** "automatic". In manual mode, you specify exactly which 24 ** direction you want each bank to be aimed, the number 25 ** of units to fire, and the spread angle. In automatic 26 ** mode, you give only the total number of units to fire. 27 ** 28 ** The spread is specified as a number between zero and 29 ** one, with zero being minimum spread and one being maximum 30 ** spread. You will normally want zero spread, unless your 31 ** short range scanners are out, in which case you probably 32 ** don't know exactly where the Klingons are. In that case, 33 ** you really don't have any choice except to specify a 34 ** fairly large spread. 35 ** 36 ** Phasers spread slightly, even if you specify zero spread. 37 ** 38 ** Uses trace flag 30 39 */ 40 41 struct cvntab Matab[] = 42 { 43 "m", "anual", (int (*)())1, 0, 44 "a", "utomatic", 0, 0, 45 0 46 }; 47 48 struct banks 49 { 50 int units; 51 double angle; 52 double spread; 53 }; 54 55 56 57 phaser() 58 { 59 register int i; 60 int j; 61 register struct kling *k; 62 double dx, dy; 63 double anglefactor, distfactor; 64 register struct banks *b; 65 int manual, flag, extra; 66 int hit; 67 double tot; 68 int n; 69 int hitreqd[NBANKS]; 70 struct banks bank[NBANKS]; 71 struct cvntab *ptr; 72 73 if (Ship.cond == DOCKED) 74 return(printf("Phasers cannot fire through starbase shields\n")); 75 if (damaged(PHASER)) 76 return (out(PHASER)); 77 if (Ship.shldup) 78 return (printf("Sulu: Captain, we cannot fire through shields.\n")); 79 if (Ship.cloaked) 80 { 81 printf("Sulu: Captain, surely you must realize that we cannot fire\n"); 82 printf(" phasers with the cloaking device up.\n"); 83 return; 84 } 85 86 /* decide if we want manual or automatic mode */ 87 manual = 0; 88 if (testnl()) 89 { 90 if (damaged(COMPUTER)) 91 { 92 printf(Device[COMPUTER].name); 93 manual++; 94 } 95 else 96 if (damaged(SRSCAN)) 97 { 98 printf(Device[SRSCAN].name); 99 manual++; 100 } 101 if (manual) 102 printf(" damaged, manual mode selected\n"); 103 } 104 105 if (!manual) 106 { 107 ptr = getcodpar("Manual or automatic", Matab); 108 manual = (int) ptr->value; 109 } 110 if (!manual && damaged(COMPUTER)) 111 { 112 printf("Computer damaged, manual selected\n"); 113 skiptonl(0); 114 manual++; 115 } 116 117 /* initialize the bank[] array */ 118 flag = 1; 119 for (i = 0; i < NBANKS; i++) 120 bank[i].units = 0; 121 if (manual) 122 { 123 /* collect manual mode statistics */ 124 while (flag) 125 { 126 printf("%d units available\n", Ship.energy); 127 extra = 0; 128 flag = 0; 129 for (i = 0; i < NBANKS; i++) 130 { 131 b = &bank[i]; 132 printf("\nBank %d:\n", i); 133 hit = getintpar("units"); 134 if (hit < 0) 135 return; 136 if (hit == 0) 137 break; 138 extra += hit; 139 if (extra > Ship.energy) 140 { 141 printf("available energy exceeded. "); 142 skiptonl(0); 143 flag++; 144 break; 145 } 146 b->units = hit; 147 hit = getintpar("course"); 148 if (hit < 0 || hit > 360) 149 return; 150 b->angle = hit * 0.0174532925; 151 b->spread = getfltpar("spread"); 152 if (b->spread < 0 || b->spread > 1) 153 return; 154 } 155 Ship.energy -= extra; 156 } 157 extra = 0; 158 } 159 else 160 { 161 /* automatic distribution of power */ 162 if (Etc.nkling <= 0) 163 return (printf("Sulu: But there are no Klingons in this quadrant\n")); 164 printf("Phasers locked on target. "); 165 while (flag) 166 { 167 printf("%d units available\n", Ship.energy); 168 hit = getintpar("Units to fire"); 169 if (hit <= 0) 170 return; 171 if (hit > Ship.energy) 172 { 173 printf("available energy exceeded. "); 174 skiptonl(0); 175 continue; 176 } 177 flag = 0; 178 Ship.energy -= hit; 179 extra = hit; 180 n = Etc.nkling; 181 if (n > NBANKS) 182 n = NBANKS; 183 tot = n * (n + 1) / 2; 184 for (i = 0; i < n; i++) 185 { 186 k = &Etc.klingon[i]; 187 b = &bank[i]; 188 distfactor = k->dist; 189 anglefactor = ALPHA * BETA * OMEGA / (distfactor * distfactor + EPSILON); 190 anglefactor *= GAMMA; 191 distfactor = k->power; 192 distfactor /= anglefactor; 193 hitreqd[i] = distfactor + 0.5; 194 dx = Ship.sectx - k->x; 195 dy = k->y - Ship.secty; 196 b->angle = atan2(dy, dx); 197 b->spread = 0.0; 198 b->units = ((n - i) / tot) * extra; 199 # ifdef xTRACE 200 if (Trace) 201 { 202 printf("b%d hr%d u%d df%.2f af%.2f\n", 203 i, hitreqd[i], b->units, 204 distfactor, anglefactor); 205 } 206 # endif 207 extra -= b->units; 208 hit = b->units - hitreqd[i]; 209 if (hit > 0) 210 { 211 extra += hit; 212 b->units -= hit; 213 } 214 } 215 216 /* give out any extra energy we might have around */ 217 if (extra > 0) 218 { 219 for (i = 0; i < n; i++) 220 { 221 b = &bank[i]; 222 hit = hitreqd[i] - b->units; 223 if (hit <= 0) 224 continue; 225 if (hit >= extra) 226 { 227 b->units += extra; 228 extra = 0; 229 break; 230 } 231 b->units = hitreqd[i]; 232 extra -= hit; 233 } 234 if (extra > 0) 235 printf("%d units overkill\n", extra); 236 } 237 } 238 } 239 240 # ifdef xTRACE 241 if (Trace) 242 { 243 for (i = 0; i < NBANKS; i++) 244 { 245 b = &bank[i]; 246 printf("b%d u%d", i, b->units); 247 if (b->units > 0) 248 printf(" a%.2f s%.2f\n", b->angle, b->spread); 249 else 250 printf("\n"); 251 } 252 } 253 # endif 254 255 /* actually fire the shots */ 256 Move.free = 0; 257 for (i = 0; i < NBANKS; i++) 258 { 259 b = &bank[i]; 260 if (b->units <= 0) 261 { 262 continue; 263 } 264 printf("\nPhaser bank %d fires:\n", i); 265 n = Etc.nkling; 266 k = Etc.klingon; 267 for (j = 0; j < n; j++) 268 { 269 if (b->units <= 0) 270 break; 271 /* 272 ** The formula for hit is as follows: 273 ** 274 ** zap = OMEGA * [(sigma + ALPHA) * (rho + BETA)] 275 ** / (dist ** 2 + EPSILON)] 276 ** * [cos(delta * sigma) + GAMMA] 277 ** * hit 278 ** 279 ** where sigma is the spread factor, 280 ** rho is a random number (0 -> 1), 281 ** GAMMA is a crud factor for angle (essentially 282 ** cruds up the spread factor), 283 ** delta is the difference in radians between the 284 ** angle you are shooting at and the actual 285 ** angle of the klingon, 286 ** ALPHA scales down the significance of sigma, 287 ** BETA scales down the significance of rho, 288 ** OMEGA is the magic number which makes everything 289 ** up to "* hit" between zero and one, 290 ** dist is the distance to the klingon 291 ** hit is the number of units in the bank, and 292 ** zap is the amount of the actual hit. 293 ** 294 ** Everything up through dist squared should maximize 295 ** at 1.0, so that the distance factor is never 296 ** greater than one. Conveniently, cos() is 297 ** never greater than one, but the same restric- 298 ** tion applies. 299 */ 300 distfactor = BETA + franf(); 301 distfactor *= ALPHA + b->spread; 302 distfactor *= OMEGA; 303 anglefactor = k->dist; 304 distfactor /= anglefactor * anglefactor + EPSILON; 305 distfactor *= b->units; 306 dx = Ship.sectx - k->x; 307 dy = k->y - Ship.secty; 308 anglefactor = atan2(dy, dx) - b->angle; 309 anglefactor = cos((anglefactor * b->spread) + GAMMA); 310 if (anglefactor < 0.0) 311 { 312 k++; 313 continue; 314 } 315 hit = anglefactor * distfactor + 0.5; 316 k->power -= hit; 317 printf("%d unit hit on Klingon", hit); 318 if (!damaged(SRSCAN)) 319 printf(" at %d,%d", k->x, k->y); 320 printf("\n"); 321 b->units -= hit; 322 if (k->power <= 0) 323 { 324 killk(k->x, k->y); 325 continue; 326 } 327 k++; 328 } 329 } 330 331 /* compute overkill */ 332 for (i = 0; i < NBANKS; i++) 333 extra += bank[i].units; 334 if (extra > 0) 335 printf("\n%d units expended on empty space\n", extra); 336 } 337