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