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