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