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