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