Rewrite to get ride of FORTRANish galaxy and newstuff arrays. It's
[super-star-trek.git] / battle.c
1 #include <unistd.h>
2 #include "sst.h"
3
4 void doshield(int i) 
5 {
6     int key;
7     enum {NONE, SHUP, SHDN, NRG} action = NONE;
8
9     ididit = 0;
10
11     if (i == 2) action = SHUP;
12     else {
13         key = scan();
14         if (key == IHALPHA) {
15             if (isit("transfer"))
16                 action = NRG;
17             else {
18                 chew();
19                 if (game.damage[DSHIELD]) {
20                     prout("Shields damaged and down.");
21                     return;
22                 }
23                 if (isit("up"))
24                     action = SHUP;
25                 else if (isit("down"))
26                     action = SHDN;
27             }
28         }
29         if (action==NONE) {
30             proutn("Do you wish to change shield energy? ");
31             if (ja()) {
32                 proutn("Energy to transfer to shields- ");
33                 action = NRG;
34             }
35             else if (game.damage[DSHIELD]) {
36                 prout("Shields damaged and down.");
37                 return;
38             }
39             else if (shldup) {
40                 proutn("Shields are up. Do you want them down? ");
41                 if (ja()) action = SHDN;
42                 else {
43                     chew();
44                     return;
45                 }
46             }
47             else {
48                 proutn("Shields are down. Do you want them up? ");
49                 if (ja()) action = SHUP;
50                 else {
51                     chew();
52                     return;
53                 }
54             }
55         }
56     }
57     switch (action) {
58     case SHUP: /* raise shields */
59         if (shldup) {
60             prout("Shields already up.");
61             return;
62         }
63         shldup = 1;
64         shldchg = 1;
65         if (condit != IHDOCKED) energy -= 50.0;
66         prout("Shields raised.");
67         if (energy <= 0) {
68             skip(1);
69             prout("Shields raising uses up last of energy.");
70             finish(FNRG);
71             return;
72         }
73         ididit=1;
74         return;
75     case SHDN:
76         if (shldup==0) {
77             prout("Shields already down.");
78             return;
79         }
80         shldup=0;
81         shldchg=1;
82         prout("Shields lowered.");
83         ididit=1;
84         return;
85     case NRG:
86         while (scan() != IHREAL) {
87             chew();
88             proutn("Energy to transfer to shields- ");
89         }
90         chew();
91         if (aaitem==0) return;
92         if (aaitem > energy) {
93             prout("Insufficient ship energy.");
94             return;
95         }
96         ididit = 1;
97         if (shield+aaitem >= inshld) {
98             prout("Shield energy maximized.");
99             if (shield+aaitem > inshld) {
100                 prout("Excess energy requested returned to ship energy");
101             }
102             energy -= inshld-shield;
103             shield = inshld;
104             return;
105         }
106         if (aaitem < 0.0 && energy-aaitem > inenrg) {
107             /* Prevent shield drain loophole */
108             skip(1);
109             prout("Engineering to bridge--");
110             prout("  Scott here. Power circuit problem, Captain.");
111             prout("  I can't drain the shields.");
112             ididit = 0;
113             return;
114         }
115         if (shield+aaitem < 0) {
116             prout("All shield energy transferred to ship.");
117             energy += shield;
118             shield = 0.0;
119             return;
120         }
121         proutn("Scotty- \"");
122         if (aaitem > 0)
123             prout("Transferring energy to shields.\"");
124         else
125             prout("Draining energy from shields.\"");
126         shield += aaitem;
127         energy -= aaitem;
128         return;
129     case NONE:; /* avoid gcc warning */
130     }
131 }
132
133 void ram(int ibumpd, int ienm, int ix, int iy)
134 {
135     double type = 1.0, extradm;
136     int icas, l;
137         
138     prouts("***RED ALERT!  RED ALERT!");
139     skip(1);
140     prout("***COLLISION IMMINENT.");
141     skip(2);
142     proutn("***");
143     crmshp();
144     switch (ienm) {
145     case IHR: type = 1.5; break;
146     case IHC: type = 2.0; break;
147     case IHS: type = 2.5; break;
148     case IHT: type = 0.5; break;
149     case IHQUEST: type = 4.0; break;
150     }
151     proutn(ibumpd ? " rammed by " : " rams ");
152     crmena(0, ienm, 2, ix, iy);
153     if (ibumpd) proutn(" (original position)");
154     skip(1);
155     deadkl(ix, iy, ienm, sectx, secty);
156     proutn("***");
157     crmshp();
158     prout(" heavily damaged.");
159     icas = 10.0+20.0*Rand();
160     prout("***Sickbay reports %d casualties", icas);
161     casual += icas;
162     for (l=0; l < NDEVICES; l++) {
163         if (l == DDRAY) 
164             continue; // Don't damage deathray 
165         if (game.damage[l] < 0) 
166             continue;
167         extradm = (10.0*type*Rand()+1.0)*damfac;
168         game.damage[l] += Time + extradm; /* Damage for at least time of travel! */
169     }
170     shldup = 0;
171     if (game.state.remkl) {
172         pause_game(2);
173         dreprt();
174     }
175     else finish(FWON);
176     return;
177 }
178
179 void torpedo(double course, double r, int inx, int iny, double *hit, int wait, int i, int n) 
180 {
181     int l, iquad=0, ix=0, iy=0, jx=0, jy=0, shoved=0, ll;
182         
183     double ac=course + 0.25*r;
184     double angle = (15.0-ac)*0.5235988;
185     double bullseye = (15.0 - course)*0.5235988;
186     double deltax=-sin(angle), deltay=cos(angle), x=inx, y=iny, bigger;
187     double ang, temp, xx, yy, kp, h1;
188
189     bigger = fabs(deltax);
190     if (fabs(deltay) > bigger) bigger = fabs(deltay);
191     deltax /= bigger;
192     deltay /= bigger;
193     if (game.damage[DSRSENS]==0 || condit==IHDOCKED) 
194         setwnd(srscan_window);
195     else 
196         setwnd(message_window);
197     /* Loop to move a single torpedo */
198     for (l=1; l <= 15; l++) {
199         x += deltax;
200         ix = x + 0.5;
201         if (ix < 1 || ix > QUADSIZE) break;
202         y += deltay;
203         iy = y + 0.5;
204         if (iy < 1 || iy > QUADSIZE) break;
205         iquad=game.quad[ix][iy];
206         tracktorpedo(x, y, ix, iy, wait, l, i, n, iquad);
207         wait = 1;
208         if (iquad==IHDOT) continue;
209         /* hit something */
210         setwnd(message_window);
211         skip(1);        /* start new line after text track */
212         switch(iquad) {
213         case IHE: /* Hit our ship */
214         case IHF:
215             skip(1);
216             proutn("Torpedo hits ");
217             crmshp();
218             prout(".");
219             *hit = 700.0 + 100.0*Rand() -
220                 1000.0*sqrt(square(ix-inx)+square(iy-iny))*
221                 fabs(sin(bullseye-angle));
222             *hit = fabs(*hit);
223             newcnd(); /* we're blown out of dock */
224             /* We may be displaced. */
225             if (landed==1 || condit==IHDOCKED) return; /* Cheat if on a planet */
226             ang = angle + 2.5*(Rand()-0.5);
227             temp = fabs(sin(ang));
228             if (fabs(cos(ang)) > temp) temp = fabs(cos(ang));
229             xx = -sin(ang)/temp;
230             yy = cos(ang)/temp;
231             jx=ix+xx+0.5;
232             jy=iy+yy+0.5;
233             if (jx<1 || jx>QUADSIZE || jy<1 ||jy > QUADSIZE) return;
234             if (game.quad[jx][jy]==IHBLANK) {
235                 finish(FHOLE);
236                 return;
237             }
238             if (game.quad[jx][jy]!=IHDOT) {
239                 /* can't move into object */
240                 return;
241             }
242             sectx = jx;
243             secty = jy;
244             crmshp();
245             shoved = 1;
246             break;
247                                           
248         case IHC: /* Hit a commander */
249         case IHS:
250             if (Rand() <= 0.05) {
251                 crmena(1, iquad, 2, ix, iy);
252                 prout(" uses anti-photon device;");
253                 prout("   torpedo neutralized.");
254                 return;
255             }
256         case IHR: /* Hit a regular enemy */
257         case IHK:
258             /* find the enemy */
259             for (ll=1; ll <= nenhere; ll++)
260                 if (ix==game.kx[ll] && iy==game.ky[ll]) break;
261             kp = fabs(game.kpower[ll]);
262             h1 = 700.0 + 100.0*Rand() -
263                 1000.0*sqrt(square(ix-inx)+square(iy-iny))*
264                 fabs(sin(bullseye-angle));
265             h1 = fabs(h1);
266             if (kp < h1) h1 = kp;
267             game.kpower[ll] -= (game.kpower[ll]<0 ? -h1 : h1);
268             if (game.kpower[ll] == 0) {
269                 deadkl(ix, iy, iquad, ix, iy);
270                 return;
271             }
272             crmena(1, iquad, 2, ix, iy);
273             /* If enemy damaged but not destroyed, try to displace */
274             ang = angle + 2.5*(Rand()-0.5);
275             temp = fabs(sin(ang));
276             if (fabs(cos(ang)) > temp) temp = fabs(cos(ang));
277             xx = -sin(ang)/temp;
278             yy = cos(ang)/temp;
279             jx=ix+xx+0.5;
280             jy=iy+yy+0.5;
281             if (jx<1 || jx>QUADSIZE || jy<1 ||jy > QUADSIZE) {
282                 prout(" damaged but not destroyed.");
283                 return;
284             }
285             if (game.quad[jx][jy]==IHBLANK) {
286                 prout(" buffeted into black hole.");
287                 deadkl(ix, iy, iquad, jx, jy);
288                 return;
289             }
290             if (game.quad[jx][jy]!=IHDOT) {
291                 /* can't move into object */
292                 prout(" damaged but not destroyed.");
293                 return;
294             }
295             proutn(" damaged--");
296             game.kx[ll] = jx;
297             game.ky[ll] = jy;
298             shoved = 1;
299             break;
300         case IHB: /* Hit a base */
301             skip(1);
302             prout("***STARBASE DESTROYED..");
303             for (ll=1; ll<=game.state.rembase; ll++) {
304                 if (game.state.baseqx[ll]==quadx && game.state.baseqy[ll]==quady) {
305                     game.state.baseqx[ll]=game.state.baseqx[game.state.rembase];
306                     game.state.baseqy[ll]=game.state.baseqy[game.state.rembase];
307                     break;
308                 }
309             }
310             game.quad[ix][iy]=IHDOT;
311             game.state.rembase--;
312             basex=basey=0;
313             game.state.galaxy[quadx][quady].starbase--;
314             game.state.chart[quadx][quady].starbase--;
315             game.state.basekl++;
316             newcnd();
317             return;
318         case IHP: /* Hit a planet */
319             crmena(1, iquad, 2, ix, iy);
320             prout(" destroyed.");
321             game.state.nplankl++;
322             game.state.galaxy[quadx][quady].planets--;
323             DESTROY(&game.state.plnets[iplnet]);
324             iplnet = 0;
325             plnetx = plnety = 0;
326             game.quad[ix][iy] = IHDOT;
327             if (landed==1) {
328                 /* captain perishes on planet */
329                 finish(FDPLANET);
330             }
331             return;
332         case IHSTAR: /* Hit a star */
333             if (Rand() > 0.10) {
334                 nova(ix, iy);
335                 return;
336             }
337             crmena(1, IHSTAR, 2, ix, iy);
338             prout(" unaffected by photon blast.");
339             return;
340         case IHQUEST: /* Hit a thingy */
341             if (!(game.options & OPTION_THINGY) || Rand()>0.7) {
342                 skip(1);
343                 prouts("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!");
344                 skip(1);
345                 prouts("    HACK!     HACK!    HACK!        *CHOKE!*  ");
346                 skip(1);
347                 proutn("Mr. Spock-");
348                 prouts("  \"Fascinating!\"");
349                 skip(1);
350                 deadkl(ix, iy, iquad, ix, iy);
351             } else {
352                 /*
353                  * Stas Sergeev added the possibility that
354                  * you can shove the Thingy abd piss it off.
355                  * It then becomes an enemy and may fire at you.
356                  */
357                 iqengry=1;
358                 shoved=1;
359             }
360             return;
361         case IHBLANK: /* Black hole */
362             skip(1);
363             crmena(1, IHBLANK, 2, ix, iy);
364             prout(" swallows torpedo.");
365             return;
366         case IHWEB: /* hit the web */
367             skip(1);
368             prout("***Torpedo absorbed by Tholian web.");
369             return;
370         case IHT:  /* Hit a Tholian */
371             h1 = 700.0 + 100.0*Rand() -
372                 1000.0*sqrt(square(ix-inx)+square(iy-iny))*
373                 fabs(sin(bullseye-angle));
374             h1 = fabs(h1);
375             if (h1 >= 600) {
376                 game.quad[ix][iy] = IHDOT;
377                 ithere = 0;
378                 ithx = ithy = 0;
379                 deadkl(ix, iy, iquad, ix, iy);
380                 return;
381             }
382             skip(1);
383             crmena(1, IHT, 2, ix, iy);
384             if (Rand() > 0.05) {
385                 prout(" survives photon blast.");
386                 return;
387             }
388             prout(" disappears.");
389             game.quad[ix][iy] = IHWEB;
390             ithere = ithx = ithy = 0;
391             nenhere--;
392             {
393                 int dum, my;
394                 dropin(IHBLANK, &dum, &my);
395             }
396             return;
397                                         
398         default: /* Problem! */
399             skip(1);
400             proutn("Don't know how to handle collision with ");
401             crmena(1, iquad, 2, ix, iy);
402             skip(1);
403             return;
404         }
405         break;
406     }
407     if(curwnd!=message_window) {
408         setwnd(message_window);
409     }
410     if (shoved) {
411         game.quad[jx][jy]=iquad;
412         game.quad[ix][iy]=IHDOT;
413         prout(" displaced by blast to %s ", cramlc(sector, jx, jy));
414         for (ll=1; ll<=nenhere; ll++)
415             game.kdist[ll] = game.kavgd[ll] = sqrt(square(sectx-game.kx[ll])+square(secty-game.ky[ll]));
416         sortkl();
417         return;
418     }
419     skip(1);
420     prout("Torpedo missed.");
421     return;
422 }
423
424 static void fry(double hit) 
425 {
426     double ncrit, extradm;
427     int ktr=1, l, ll, j, cdam[NDEVICES];
428
429     /* a critical hit occured */
430     if (hit < (275.0-25.0*skill)*(1.0+0.5*Rand())) return;
431
432     ncrit = 1.0 + hit/(500.0+100.0*Rand());
433     proutn("***CRITICAL HIT--");
434     /* Select devices and cause damage */
435     for (l = 0; l < ncrit && 0 < NDEVICES; l++) {
436         do {
437             j = NDEVICES*Rand();
438             /* Cheat to prevent shuttle damage unless on ship */
439         } while 
440               (game.damage[j]<0.0 || (j==DSHUTTL && iscraft!=1) || j==DDRAY);
441         cdam[l] = j;
442         extradm = (hit*damfac)/(ncrit*(75.0+25.0*Rand()));
443         game.damage[j] += extradm;
444         if (l > 0) {
445             for (ll=2; ll<=l && j != cdam[ll-1]; ll++) ;
446             if (ll<=l) continue;
447             ktr += 1;
448             if (ktr==3) skip(1);
449             proutn(" and ");
450         }
451         proutn(device[j]);
452     }
453     prout(" damaged.");
454     if (game.damage[DSHIELD] && shldup) {
455         prout("***Shields knocked down.");
456         shldup=0;
457     }
458 }
459
460 void attack(int torps_ok) 
461 {
462     /* torps_ok == 0 forces use of phasers in an attack */
463     int percent, ihurt=0, l, i=0, jx, jy, iquad, itflag;
464     int atackd = 0, attempt = 0;
465     double hit;
466     double pfac, dustfac, hitmax=0.0, hittot=0.0, chgfac=1.0, r;
467
468     iattak = 1;
469     if (alldone) return;
470 #ifdef DEBUG
471     if (idebug) prout("ATTACK!");
472 #endif
473
474     if (ithere) movetho();
475
476     if (neutz) { /* The one chance not to be attacked */
477         neutz = 0;
478         return;
479     }
480     if ((((comhere || ishere) && (justin == 0)) || skill == SKILL_EMERITUS)&&(torps_ok!=0)) movcom();
481     if (nenhere==0 || (nenhere==1 && iqhere && iqengry==0)) return;
482     pfac = 1.0/inshld;
483     if (shldchg == 1) chgfac = 0.25+0.5*Rand();
484     skip(1);
485     if (skill <= SKILL_FAIR) i = 2;
486     for (l=1; l <= nenhere; l++) {
487         if (game.kpower[l] < 0) continue;       /* too weak to attack */
488         /* compute hit strength and diminsh shield power */
489         r = Rand();
490         /* Increase chance of photon torpedos if docked or enemy energy low */
491         if (condit == IHDOCKED) r *= 0.25;
492         if (game.kpower[l] < 500) r *= 0.25; 
493         jx = game.kx[l];
494         jy = game.ky[l];
495         iquad = game.quad[jx][jy];
496         if (iquad==IHT || (iquad==IHQUEST && !iqengry)) continue;
497         itflag = (iquad == IHK && r > 0.0005) || !torps_ok ||
498             (iquad==IHC && r > 0.015) ||
499             (iquad==IHR && r > 0.3) ||
500             (iquad==IHS && r > 0.07) ||
501             (iquad==IHQUEST && r > 0.05);
502         if (itflag) {
503             /* Enemy uses phasers */
504             if (condit == IHDOCKED) continue; /* Don't waste the effort! */
505             attempt = 1; /* Attempt to attack */
506             dustfac = 0.8+0.05*Rand();
507             hit = game.kpower[l]*pow(dustfac,game.kavgd[l]);
508             game.kpower[l] *= 0.75;
509         }
510         else { /* Enemy used photon torpedo */
511             double course = 1.90985*atan2((double)secty-jy, (double)jx-sectx);
512             hit = 0;
513             proutn("***TORPEDO INCOMING");
514             if (game.damage[DSRSENS] <= 0.0) {
515                 proutn(" From ");
516                 crmena(0, iquad, i, jx, jy);
517             }
518             attempt = 1;
519             prout("  ");
520             r = (Rand()+Rand())*0.5 -0.5;
521             r += 0.002*game.kpower[l]*r;
522             torpedo(course, r, jx, jy, &hit, 0, 1, 1);
523             if (game.state.remkl==0) 
524                 finish(FWON); /* Klingons did themselves in! */
525             if (game.state.galaxy[quadx][quady].supernova || alldone) 
526                 return; /* Supernova or finished */
527             if (hit == 0) continue;
528         }
529         if (shldup != 0 || shldchg != 0 || condit==IHDOCKED) {
530             /* shields will take hits */
531             double absorb, hitsh, propor = pfac*shield*(condit==IHDOCKED ? 2.1 : 1.0);
532             if(propor < 0.1) propor = 0.1;
533             hitsh = propor*chgfac*hit+1.0;
534             atackd=1;
535             absorb = 0.8*hitsh;
536             if (absorb > shield) absorb = shield;
537             shield -= absorb;
538             hit -= hitsh;
539             if (condit==IHDOCKED) dock(0);
540             if (propor > 0.1 && hit < 0.005*energy) continue;
541         }
542         /* It's a hit -- print out hit size */
543         atackd = 1; /* We weren't going to check casualties, etc. if
544                        shields were down for some strange reason. This
545                        doesn't make any sense, so I've fixed it */
546         ihurt = 1;
547         proutn("%d unit hit", (int)hit);
548         if ((game.damage[DSRSENS] > 0 && itflag) || skill<=SKILL_FAIR) {
549             proutn(" on the ");
550             crmshp();
551         }
552         if (game.damage[DSRSENS] <= 0.0 && itflag) {
553             proutn(" from ");
554             crmena(0, iquad, i, jx, jy);
555         }
556         skip(1);
557         /* Decide if hit is critical */
558         if (hit > hitmax) hitmax = hit;
559         hittot += hit;
560         fry(hit);
561         prout("Hit %g energy %g", hit, energy);
562         energy -= hit;
563         if (condit==IHDOCKED) 
564             dock(0);
565     }
566     if (energy <= 0) {
567         /* Returning home upon your shield, not with it... */
568         finish(FBATTLE);
569         return;
570     }
571     if (attempt == 0 && condit == IHDOCKED)
572         prout("***Enemies decide against attacking your ship.");
573     if (atackd == 0) return;
574     percent = 100.0*pfac*shield+0.5;
575     if (ihurt==0) {
576         /* Shields fully protect ship */
577         proutn("Enemy attack reduces shield strength to ");
578     }
579     else {
580         /* Print message if starship suffered hit(s) */
581         skip(1);
582         proutn("Energy left %2d    shields ", (int)energy);
583         if (shldup) proutn("up ");
584         else if (game.damage[DSHIELD] == 0) proutn("down ");
585         else proutn("damaged, ");
586     }
587     prout("%d%%,   torpedoes left %d", percent, torps);
588     /* Check if anyone was hurt */
589     if (hitmax >= 200 || hittot >= 500) {
590         int icas= hittot*Rand()*0.015;
591         if (icas >= 2) {
592             skip(1);
593             prout("Mc Coy-  \"Sickbay to bridge.  We suffered %d casualties", icas);
594             prout("   in that last attack.\"");
595             casual += icas;
596         }
597     }
598     /* After attack, reset average distance to enemies */
599     for (l = 1; l <= nenhere; l++)
600         game.kavgd[l] = game.kdist[l];
601     sortkl();
602     return;
603 }
604                 
605 void deadkl(int ix, int iy, int type, int ixx, int iyy) 
606 {
607     /* Added ixx and iyy allow enemy to "move" before dying */
608
609     int i,j;
610
611     skip(1);
612     crmena(1, type, 2, ixx, iyy);
613     /* Decide what kind of enemy it is and update approriately */
614     if (type == IHR) {
615         /* chalk up a Romulan */
616         game.state.galaxy[quadx][quady].romulans--;
617         irhere--;
618         game.state.nromkl++;
619         game.state.nromrem--;
620     }
621     else if (type == IHT) {
622         /* Killed a Tholian */
623         ithere = 0;
624     }
625     else if (type == IHQUEST) {
626         /* Killed a Thingy */
627         iqhere=iqengry=thingx=thingy=0;
628     }
629     else {
630         /* Some type of a Klingon */
631         game.state.galaxy[quadx][quady].klingons--;
632         klhere--;
633         game.state.remkl--;
634         switch (type) {
635         case IHC:
636             comhere = 0;
637             for (i=1; i<=game.state.remcom; i++)
638                 if (game.state.cx[i]==quadx && game.state.cy[i]==quady) break;
639             game.state.cx[i] = game.state.cx[game.state.remcom];
640             game.state.cy[i] = game.state.cy[game.state.remcom];
641             game.state.cx[game.state.remcom] = 0;
642             game.state.cy[game.state.remcom] = 0;
643             game.state.remcom--;
644             game.future[FTBEAM] = 1e30;
645             if (game.state.remcom != 0)
646                 game.future[FTBEAM] = game.state.date + expran(1.0*incom/game.state.remcom);
647             game.state.killc++;
648             break;
649         case IHK:
650             game.state.killk++;
651             break;
652         case IHS:
653             game.state.nscrem = ishere = game.state.isx = game.state.isy = isatb = iscate = 0;
654             game.state.nsckill = 1;
655             game.future[FSCMOVE] = game.future[FSCDBAS] = 1e30;
656             break;
657         }
658     }
659
660     /* For each kind of enemy, finish message to player */
661     prout(" destroyed.");
662     game.quad[ix][iy] = IHDOT;
663     if (game.state.remkl==0) return;
664
665     game.state.remtime = game.state.remres/(game.state.remkl + 4*game.state.remcom);
666
667     /* Remove enemy ship from arrays describing local conditions */
668     if (game.future[FCDBAS] < 1e30 && batx==quadx && baty==quady && type==IHC)
669         game.future[FCDBAS] = 1e30;
670     for (i=1; i<=nenhere; i++)
671         if (game.kx[i]==ix && game.ky[i]==iy) break;
672     nenhere--;
673     if (i <= nenhere)  {
674         for (j=i; j<=nenhere; j++) {
675             game.kx[j] = game.kx[j+1];
676             game.ky[j] = game.ky[j+1];
677             game.kpower[j] = game.kpower[j+1];
678             game.kavgd[j] = game.kdist[j] = game.kdist[j+1];
679         }
680     }
681     game.kx[nenhere+1] = 0;
682     game.ky[nenhere+1] = 0;
683     game.kdist[nenhere+1] = 0;
684     game.kavgd[nenhere+1] = 0;
685     game.kpower[nenhere+1] = 0;
686     return;
687 }
688
689 static int targetcheck(double x, double y, double *course) 
690 {
691     double deltx, delty;
692     /* Return TRUE if target is invalid */
693     if (x < 1.0 || x > QUADSIZE || y < 1.0 || y > QUADSIZE) {
694         huh();
695         return 1;
696     }
697     deltx = 0.1*(y - secty);
698     delty = 0.1*(sectx - x);
699     if (deltx==0 && delty== 0) {
700         skip(1);
701         prout("Spock-  \"Bridge to sickbay.  Dr. McCoy,");
702         prout("  I recommend an immediate review of");
703         prout("  the Captain's psychological profile.\"");
704         chew();
705         return 1;
706     }
707     *course = 1.90985932*atan2(deltx, delty);
708     return 0;
709 }
710
711 void photon(void) 
712 {
713     double targ[4][3], course[4];
714     double r, dummy;
715     int key, n, i, osuabor;
716
717     ididit = 0;
718
719     if (game.damage[DPHOTON]) {
720         prout("Photon tubes damaged.");
721         chew();
722         return;
723     }
724     if (torps == 0) {
725         prout("No torpedoes left.");
726         chew();
727         return;
728     }
729     key = scan();
730     for (;;) {
731         if (key == IHALPHA) {
732             huh();
733             return;
734         }
735         else if (key == IHEOL) {
736             prout("%d torpedoes left.", torps);
737             proutn("Number of torpedoes to fire- ");
738             key = scan();
739         }
740         else /* key == IHREAL */ {
741             n = aaitem + 0.5;
742             if (n <= 0) { /* abort command */
743                 chew();
744                 return;
745             }
746             if (n > 3) {
747                 chew();
748                 prout("Maximum of 3 torpedoes per burst.");
749                 key = IHEOL;
750                 return;
751             }
752             if (n <= torps) break;
753             chew();
754             key = IHEOL;
755         }
756     }
757     for (i = 1; i <= n; i++) {
758         key = scan();
759         if (i==1 && key == IHEOL) {
760             break;      /* we will try prompting */
761         }
762         if (i==2 && key == IHEOL) {
763             /* direct all torpedoes at one target */
764             while (i <= n) {
765                 targ[i][1] = targ[1][1];
766                 targ[i][2] = targ[1][2];
767                 course[i] = course[1];
768                 i++;
769             }
770             break;
771         }
772         if (key != IHREAL) {
773             huh();
774             return;
775         }
776         targ[i][1] = aaitem;
777         key = scan();
778         if (key != IHREAL) {
779             huh();
780             return;
781         }
782         targ[i][2] = aaitem;
783         if (targetcheck(targ[i][1], targ[i][2], &course[i])) return;
784     }
785     chew();
786     if (i == 1 && key == IHEOL) {
787         /* prompt for each one */
788         for (i = 1; i <= n; i++) {
789             proutn("Target sector for torpedo number %d- ", i);
790             key = scan();
791             if (key != IHREAL) {
792                 huh();
793                 return;
794             }
795             targ[i][1] = aaitem;
796             key = scan();
797             if (key != IHREAL) {
798                 huh();
799                 return;
800             }
801             targ[i][2] = aaitem;
802             chew();
803             if (targetcheck(targ[i][1], targ[i][2], &course[i])) return;
804         }
805     }
806     ididit = 1;
807     /* Loop for moving <n> torpedoes */
808     osuabor = 0;
809     for (i = 1; i <= n && !osuabor; i++) {
810         if (condit != IHDOCKED) torps--;
811         r = (Rand()+Rand())*0.5 -0.5;
812         if (fabs(r) >= 0.47) {
813             /* misfire! */
814             r = (Rand()+1.2) * r;
815             if (n>1) {
816                 prouts("***TORPEDO NUMBER %d MISFIRES", i);
817             }
818             else prouts("***TORPEDO MISFIRES.");
819             skip(1);
820             if (i < n)
821                 prout("  Remainder of burst aborted.");
822             osuabor=1;
823             if (Rand() <= 0.2) {
824                 prout("***Photon tubes damaged by misfire.");
825                 game.damage[DPHOTON] = damfac*(1.0+2.0*Rand());
826                 break;
827             }
828         }
829         if (shldup || condit == IHDOCKED) 
830             r *= 1.0 + 0.0001*shield;
831         torpedo(course[i], r, sectx, secty, &dummy, 0, i, n);
832         if (alldone || game.state.galaxy[quadx][quady].supernova)
833             return;
834     }
835     if (game.state.remkl==0) finish(FWON);
836 }
837
838         
839
840 static void overheat(double rpow) 
841 {
842     if (rpow > 1500) {
843         double chekbrn = (rpow-1500.)*0.00038;
844         if (Rand() <= chekbrn) {
845             prout("Weapons officer Sulu-  \"Phasers overheated, sir.\"");
846             game.damage[DPHASER] = damfac*(1.0 + Rand()) * (1.0+chekbrn);
847         }
848     }
849 }
850
851 static int checkshctrl(double rpow) 
852 {
853     double hit;
854     int icas;
855         
856     skip(1);
857     if (Rand() < .998) {
858         prout("Shields lowered.");
859         return 0;
860     }
861     /* Something bad has happened */
862     prouts("***RED ALERT!  RED ALERT!");
863     skip(2);
864     hit = rpow*shield/inshld;
865     energy -= rpow+hit*0.8;
866     shield -= hit*0.2;
867     if (energy <= 0.0) {
868         prouts("Sulu-  \"Captain! Shield malf***********************\"");
869         skip(1);
870         stars();
871         finish(FPHASER);
872         return 1;
873     }
874     prouts("Sulu-  \"Captain! Shield malfunction! Phaser fire contained!\"");
875     skip(2);
876     prout("Lt. Uhura-  \"Sir, all decks reporting damage.\"");
877     icas = hit*Rand()*0.012;
878     skip(1);
879     fry(0.8*hit);
880     if (icas) {
881         skip(1);
882         prout("McCoy to bridge- \"Severe radiation burns, Jim.");
883         prout("  %d casualties so far.\"", icas);
884         casual -= icas;
885     }
886     skip(1);
887     prout("Phaser energy dispersed by shields.");
888     prout("Enemy unaffected.");
889     overheat(rpow);
890     return 1;
891 }
892         
893
894 void phasers(void) 
895 {
896     double hits[21], rpow=0, extra, powrem, over, temp;
897     int kz = 0, k=1, i, irec=0; /* Cheating inhibitor */
898     int ifast=0, no=0, ipoop=1, msgflag = 1;
899     enum {NOTSET, MANUAL, FORCEMAN, AUTOMATIC} automode = NOTSET;
900     int key=0;
901
902     skip(1);
903     /* SR sensors and Computer */
904     if (game.damage[DSRSENS]+game.damage[DCOMPTR] > 0) ipoop = 0;
905     if (condit == IHDOCKED) {
906         prout("Phasers can't be fired through base shields.");
907         chew();
908         return;
909     }
910     if (game.damage[DPHASER] != 0) {
911         prout("Phaser control damaged.");
912         chew();
913         return;
914     }
915     if (shldup) {
916         if (game.damage[DSHCTRL]) {
917             prout("High speed shield control damaged.");
918             chew();
919             return;
920         }
921         if (energy <= 200.0) {
922             prout("Insufficient energy to activate high-speed shield control.");
923             chew();
924             return;
925         }
926         prout("Weapons Officer Sulu-  \"High-speed shield control enabled, sir.\"");
927         ifast = 1;
928                 
929     }
930     /* Original code so convoluted, I re-did it all */
931     while (automode==NOTSET) {
932         key=scan();
933         if (key == IHALPHA) {
934             if (isit("manual")) {
935                 if (nenhere==0) {
936                     prout("There is no enemy present to select.");
937                     chew();
938                     key = IHEOL;
939                     automode=AUTOMATIC;
940                 }
941                 else {
942                     automode = MANUAL;
943                     key = scan();
944                 }
945             }
946             else if (isit("automatic")) {
947                 if ((!ipoop) && nenhere != 0) {
948                     automode = FORCEMAN;
949                 }
950                 else {
951                     if (nenhere==0)
952                         prout("Energy will be expended into space.");
953                     automode = AUTOMATIC;
954                     key = scan();
955                 }
956             }
957             else if (isit("no")) {
958                 no = 1;
959             }
960             else {
961                 huh();
962                 return;
963             }
964         }
965         else if (key == IHREAL) {
966             if (nenhere==0) {
967                 prout("Energy will be expended into space.");
968                 automode = AUTOMATIC;
969             }
970             else if (!ipoop)
971                 automode = FORCEMAN;
972             else
973                 automode = AUTOMATIC;
974         }
975         else {
976             /* IHEOL */
977             if (nenhere==0) {
978                 prout("Energy will be expended into space.");
979                 automode = AUTOMATIC;
980             }
981             else if (!ipoop)
982                 automode = FORCEMAN;
983             else 
984                 proutn("Manual or automatic? ");
985         }
986     }
987                                 
988     switch (automode) {
989     case AUTOMATIC:
990         if (key == IHALPHA && isit("no")) {
991             no = 1;
992             key = scan();
993         }
994         if (key != IHREAL && nenhere != 0) {
995             prout("Phasers locked on target. Energy available: %.2f",
996                   ifast?energy-200.0:energy,1,2);
997         }
998         irec=0;
999         do {
1000             chew();
1001             if (!kz) for (i = 1; i <= nenhere; i++)
1002                 irec+=fabs(game.kpower[i])/(PHASEFAC*pow(0.90,game.kdist[i]))*
1003                     (1.01+0.05*Rand()) + 1.0;
1004             kz=1;
1005             proutn("(%d) units required. ", irec);
1006             chew();
1007             proutn("Units to fire= ");
1008             key = scan();
1009             if (key!=IHREAL) return;
1010             rpow = aaitem;
1011             if (rpow > (ifast?energy-200:energy)) {
1012                 proutn("Energy available= %.2f",
1013                        ifast?energy-200:energy);
1014                 skip(1);
1015                 key = IHEOL;
1016             }
1017         } while (rpow > (ifast?energy-200:energy));
1018         if (rpow<=0) {
1019             /* chicken out */
1020             chew();
1021             return;
1022         }
1023         if ((key=scan()) == IHALPHA && isit("no")) {
1024             no = 1;
1025         }
1026         if (ifast) {
1027             energy -= 200; /* Go and do it! */
1028             if (checkshctrl(rpow)) return;
1029         }
1030         chew();
1031         energy -= rpow;
1032         extra = rpow;
1033         if (nenhere) {
1034             extra = 0.0;
1035             powrem = rpow;
1036             for (i = 1; i <= nenhere; i++) {
1037                 hits[i] = 0.0;
1038                 if (powrem <= 0) continue;
1039                 hits[i] = fabs(game.kpower[i])/(PHASEFAC*pow(0.90,game.kdist[i]));
1040                 over = (0.01 + 0.05*Rand())*hits[i];
1041                 temp = powrem;
1042                 powrem -= hits[i] + over;
1043                 if (powrem <= 0 && temp < hits[i]) hits[i] = temp;
1044                 if (powrem <= 0) over = 0.0;
1045                 extra += over;
1046             }
1047             if (powrem > 0.0) extra += powrem;
1048             hittem(hits);
1049             ididit=1;
1050         }
1051         if (extra > 0 && alldone == 0) {
1052             if (ithere) {
1053                 proutn("*** Tholian web absorbs ");
1054                 if (nenhere>0) proutn("excess ");
1055                 prout("phaser energy.");
1056             }
1057             else {
1058                 prout("%d expended on empty space.", (int)extra);
1059             }
1060         }
1061         break;
1062
1063     case FORCEMAN:
1064         chew();
1065         key = IHEOL;
1066         if (game.damage[DCOMPTR]!=0)
1067             prout("Battle comuter damaged, manual file only.");
1068         else {
1069             skip(1);
1070             prouts("---WORKING---");
1071             skip(1);
1072             prout("Short-range-sensors-damaged");
1073             prout("Insufficient-data-for-automatic-phaser-fire");
1074             prout("Manual-fire-must-be-used");
1075             skip(1);
1076         }
1077     case MANUAL:
1078         rpow = 0.0;
1079         for (k = 1; k <= nenhere;) {
1080             int ii = game.kx[k], jj = game.ky[k];
1081             int ienm = game.quad[ii][jj];
1082             if (msgflag) {
1083                 proutn("Energy available= %.2f",
1084                        energy-.006-(ifast?200:0));
1085                 skip(1);
1086                 msgflag = 0;
1087                 rpow = 0.0;
1088             }
1089             if (game.damage[DSRSENS] && !(abs(sectx-ii) < 2 && abs(secty-jj) < 2) &&
1090                 (ienm == IHC || ienm == IHS)) {
1091                 cramen(ienm);
1092                 prout(" can't be located without short range scan.");
1093                 chew();
1094                 key = IHEOL;
1095                 hits[k] = 0; /* prevent overflow -- thanks to Alexei Voitenko */
1096                 k++;
1097                 continue;
1098             }
1099             if (key == IHEOL) {
1100                 chew();
1101                 if (ipoop && k > kz)
1102                     irec=(fabs(game.kpower[k])/(PHASEFAC*pow(0.9,game.kdist[k])))*
1103                         (1.01+0.05*Rand()) + 1.0;
1104                 kz = k;
1105                 proutn("(");
1106                 if (game.damage[DCOMPTR]==0) proutn("%d", irec);
1107                 else proutn("??");
1108                 proutn(")  ");
1109                 proutn("units to fire at ");
1110                 crmena(0, ienm, 2, ii, jj);
1111                 proutn("-  ");
1112                 key = scan();
1113             }
1114             if (key == IHALPHA && isit("no")) {
1115                 no = 1;
1116                 key = scan();
1117                 continue;
1118             }
1119             if (key == IHALPHA) {
1120                 huh();
1121                 return;
1122             }
1123             if (key == IHEOL) {
1124                 if (k==1) { /* Let me say I'm baffled by this */
1125                     msgflag = 1;
1126                 }
1127                 continue;
1128             }
1129             if (aaitem < 0) {
1130                 /* abort out */
1131                 chew();
1132                 return;
1133             }
1134             hits[k] = aaitem;
1135             rpow += aaitem;
1136             /* If total requested is too much, inform and start over */
1137                                 
1138             if (rpow > (ifast?energy-200:energy)) {
1139                 prout("Available energy exceeded -- try again.");
1140                 chew();
1141                 return;
1142             }
1143             key = scan(); /* scan for next value */
1144             k++;
1145         }
1146         if (rpow == 0.0) {
1147             /* zero energy -- abort */
1148             chew();
1149             return;
1150         }
1151         if (key == IHALPHA && isit("no")) {
1152             no = 1;
1153         }
1154         energy -= rpow;
1155         chew();
1156         if (ifast) {
1157             energy -= 200.0;
1158             if (checkshctrl(rpow)) return;
1159         }
1160         hittem(hits);
1161         ididit=1;
1162     case NOTSET:;       /* avoid gcc warning */
1163     }
1164     /* Say shield raised or malfunction, if necessary */
1165     if (alldone) 
1166         return;
1167     if (ifast) {
1168         skip(1);
1169         if (no == 0) {
1170             if (Rand() >= 0.99) {
1171                 prout("Sulu-  \"Sir, the high-speed shield control has malfunctioned . . .");
1172                 prouts("         CLICK   CLICK   POP  . . .");
1173                 prout(" No  response, sir!");
1174                 shldup = 0;
1175             }
1176             else
1177                 prout("Shields raised.");
1178         }
1179         else
1180             shldup = 0;
1181     }
1182     overheat(rpow);
1183 }
1184
1185 void hittem(double *hits) 
1186 {
1187     double kp, kpow, wham, hit, dustfac, kpini;
1188     int nenhr2=nenhere, k=1, kk=1, ii, jj, ienm;
1189
1190     skip(1);
1191
1192     for (; k <= nenhr2; k++, kk++) {
1193         if ((wham = hits[k])==0) continue;
1194         dustfac = 0.9 + 0.01*Rand();
1195         hit = wham*pow(dustfac,game.kdist[kk]);
1196         kpini = game.kpower[kk];
1197         kp = fabs(kpini);
1198         if (PHASEFAC*hit < kp) kp = PHASEFAC*hit;
1199         game.kpower[kk] -= (game.kpower[kk] < 0 ? -kp: kp);
1200         kpow = game.kpower[kk];
1201         ii = game.kx[kk];
1202         jj = game.ky[kk];
1203         if (hit > 0.005) {
1204             if (game.damage[DSRSENS]==0)
1205                 boom(ii, jj);
1206             proutn("%d unit hit on ", (int)hit);
1207         }
1208         else
1209             proutn("Very small hit on ");
1210         ienm = game.quad[ii][jj];
1211         if (ienm==IHQUEST) iqengry=1;
1212         crmena(0,ienm,2,ii,jj);
1213         skip(1);
1214         if (kpow == 0) {
1215             deadkl(ii, jj, ienm, ii, jj);
1216             if (game.state.remkl==0) finish(FWON);
1217             if (alldone) return;
1218             kk--; /* don't do the increment */
1219         }
1220         else /* decide whether or not to emasculate klingon */
1221             if (kpow > 0 && Rand() >= 0.9 &&
1222                 kpow <= ((0.4 + 0.4*Rand())*kpini)) {
1223                 prout("***Mr. Spock-  \"Captain, the vessel at ",
1224                       cramlc(sector,ii,jj));
1225                 prout("   has just lost its firepower.\"");
1226                 game.kpower[kk] = -kpow;
1227             }
1228     }
1229     return;
1230 }
1231