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