Clean up the rather nasty logoc of snova(), and fix a minor navigation bug.
[super-star-trek.git] / src / moving.c
1 #include <unistd.h>
2 #include "sstlinux.h"
3 #include "sst.h"
4
5 static void getcd(bool, int);
6
7 void imove(void)
8 /* movement execution for warp, impule, supernova, and tractor-beam events */
9 {
10     double angle, deltax, deltay, bigger, x, y,
11         finald, finalx, finaly, stopegy, probf;
12     int n, m, kink, kinks, iquad;
13     coord w;
14     bool trbeam = 0;
15
16     w.x = w.y = 0;
17     if (game.inorbit) {
18         prout(_("Helmsman Sulu- \"Leaving standard orbit.\""));
19         game.inorbit = false;
20     }
21
22     angle = ((15.0 - game.direc) * 0.5235988);
23     deltax = -sin(angle);
24     deltay = cos(angle);
25     if (fabs(deltax) > fabs(deltay))
26         bigger = fabs(deltax);
27     else
28         bigger = fabs(deltay);
29                 
30     deltay /= bigger;
31     deltax /= bigger;
32
33     /* If tractor beam is to occur, don't move full distance */
34     if (game.state.date+game.optime >= scheduled(FTBEAM)) {
35         trbeam = true;
36         game.condit = IHRED;
37         game.dist = game.dist*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1;
38         game.optime = scheduled(FTBEAM) - game.state.date + 1e-5;
39     }
40     /* Move within the quadrant */
41     game.quad[game.sector.x][game.sector.y] = IHDOT;
42     x = game.sector.x;
43     y = game.sector.y;
44     n = 10.0*game.dist*bigger+0.5;
45
46     if (n > 0) {
47         for (m = 1; m <= n; m++) {
48             w.x = (x += deltax) + 0.5;
49             w.y = (y += deltay) + 0.5;
50             if (!VALID_SECTOR(w.x, w.y)) {
51                 /* Leaving quadrant -- allow final enemy attack */
52                 /* Don't do it if being pushed by Nova */
53                 if (game.nenhere != 0 && game.iattak != 2) {
54                     newcnd();
55                     for_local_enemies(m) {
56                         finald = sqrt((w.x-game.ks[m].x)*(double)(w.x-game.ks[m].x) +
57                                       (w.y-game.ks[m].y)*(double)(w.y-game.ks[m].y));
58                         game.kavgd[m] = 0.5 * (finald+game.kdist[m]);
59                     }
60                     /*
61                      * Stas Sergeev added the game.condition
62                      * that attacks only happen if Klingons
63                      * are present and your skill is good.
64                      */
65                     if (game.skill > SKILL_GOOD && game.klhere > 0 && !game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova)
66                         attack(0);
67                     if (game.alldone) return;
68                 }
69                 /* compute final position -- new quadrant and sector */
70                 x = QUADSIZE*(game.quadrant.x-1)+game.sector.x;
71                 y = QUADSIZE*(game.quadrant.y-1)+game.sector.y;
72                 w.x = x+10.0*game.dist*bigger*deltax+0.5;
73                 w.y = y+10.0*game.dist*bigger*deltay+0.5;
74                 /* check for edge of galaxy */
75                 kinks = 0;
76                 do {
77                     kink = 0;
78                     if (w.x <= 0) {
79                         w.x = -w.x + 1;
80                         kink = 1;
81                     }
82                     if (w.y <= 0) {
83                         w.y = -w.y + 1;
84                         kink = 1;
85                     }
86                     if (w.x > GALSIZE*QUADSIZE) {
87                         w.x = (GALSIZE*QUADSIZE*2)+1 - w.x;
88                         kink = 1;
89                     }
90                     if (w.y > GALSIZE*QUADSIZE) {
91                         w.y = (GALSIZE*QUADSIZE*2)+1 - w.y;
92                         kink = 1;
93                     }
94                     if (kink) kinks = 1;
95                 } while (kink);
96
97                 if (kinks) {
98                     game.nkinks += 1;
99                     if (game.nkinks == 3) {
100                         /* Three strikes -- you're out! */
101                         finish(FNEG3);
102                         return;
103                     }
104                     skip(1);
105                     prout(_("YOU HAVE ATTEMPTED TO CROSS THE NEGATIVE ENERGY BARRIER"));
106                     prout(_("AT THE EDGE OF THE GALAXY.  THE THIRD TIME YOU TRY THIS,"));
107                     prout(_("YOU WILL BE DESTROYED."));
108                 }
109                 /* Compute final position in new quadrant */
110                 if (trbeam) return; /* Don't bother if we are to be beamed */
111                 game.quadrant.x = (w.x+(QUADSIZE-1))/QUADSIZE;
112                 game.quadrant.y = (w.y+(QUADSIZE-1))/QUADSIZE;
113                 game.sector.x = w.x - QUADSIZE*(game.quadrant.x-1);
114                 game.sector.y = w.y - QUADSIZE*(game.quadrant.y-1);
115                 skip(1);
116                 prout(_("Entering %s."), cramlc(quadrant, game.quadrant));
117                 game.quad[game.sector.x][game.sector.y] = game.ship;
118                 newqad(false);
119                 if (game.skill>SKILL_NOVICE) attack(0);
120                 return;
121             }
122             iquad = game.quad[w.x][w.y];
123             if (iquad != IHDOT) {
124                 /* object encountered in flight path */
125                 stopegy = 50.0*game.dist/game.optime;
126                 game.dist=0.1*sqrt((game.sector.x-w.x)*(double)(game.sector.x-w.x) +
127                               (game.sector.y-w.y)*(double)(game.sector.y-w.y));
128                 switch (iquad) {
129                 case IHT: /* Ram a Tholian */
130                 case IHK: /* Ram enemy ship */
131                 case IHC:
132                 case IHS:
133                 case IHR:
134                 case IHQUEST:
135                     game.sector.x = w.x;
136                     game.sector.y = w.y;
137                     ram(0, iquad, game.sector);
138                     finalx = game.sector.x;
139                     finaly = game.sector.y;
140                     break;
141                 case IHBLANK:
142                     skip(1);
143                     prouts(_("***RED ALERT!  RED ALERT!"));
144                     skip(1);
145                     proutn("***");
146                     crmshp();
147                     proutn(_(" pulled into black hole at "));
148                     prout(cramlc(sector, w));
149                     /*
150                      * Getting pulled into a black hole was certain
151                      * death in Almy's original.  Stas Sergeev added a
152                      * possibility that you'll get timewarped instead.
153                      */
154                     n=0;
155                     for (m=0;m<NDEVICES;m++)
156                         if (game.damage[m]>0) 
157                             n++;
158                     probf=pow(1.4,(game.energy+game.shield)/5000.0-1.0)*pow(1.3,1.0/(n+1)-1.0);
159                     if ((game.options & OPTION_BLKHOLE) && Rand()>probf) 
160                         timwrp();
161                     else 
162                         finish(FHOLE);
163                     return;
164                 default:
165                     /* something else */
166                     skip(1);
167                     crmshp();
168                     if (iquad == IHWEB)
169                         proutn(_(" encounters Tholian web at "));
170                     else
171                         proutn(_(" blocked by object at "));
172                     proutn(cramlc(sector, w));
173                     prout(";");
174                     proutn(_("Emergency stop required "));
175                     prout(_("%2d units of energy."), (int)stopegy);
176                     game.energy -= stopegy;
177                     finalx = x-deltax+0.5;
178                     game.sector.x = finalx;
179                     finaly = y-deltay+0.5;
180                     game.sector.y = finaly;
181                     if (game.energy <= 0) {
182                         finish(FNRG);
183                         return;
184                     }
185                     break;
186                 }
187                 goto no_quad_change;    /* sorry! */
188             }
189         }
190         game.dist = 0.1*sqrt((game.sector.x-w.x)*(double)(game.sector.x-w.x) +
191                         (game.sector.y-w.y)*(double)(game.sector.y-w.y));
192         game.sector.x = w.x;
193         game.sector.y = w.y;
194     }
195     finalx = game.sector.x;
196     finaly = game.sector.y;
197 no_quad_change:
198     /* No quadrant change -- compute new avg enemy distances */
199     game.quad[game.sector.x][game.sector.y] = game.ship;
200     if (game.nenhere) {
201         for_local_enemies(m) {
202             finald = sqrt((w.x-game.ks[m].x)*(double)(w.x-game.ks[m].x) +
203                           (w.y-game.ks[m].y)*(double)(w.y-game.ks[m].y));
204             game.kavgd[m] = 0.5 * (finald+game.kdist[m]);
205             game.kdist[m] = finald;
206         }
207         sortkl();
208         if (!game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova && game.iattak == 0)
209             attack(0);
210         for_local_enemies(m) game.kavgd[m] = game.kdist[m];
211     }
212     newcnd();
213     game.iattak = 0;
214     drawmaps(0);
215     setwnd(message_window);
216     return;
217 }
218
219 void dock(bool verbose) 
220 /* dock our ship at a starbase */
221 {
222     chew();
223     if (game.condit == IHDOCKED && verbose) {
224         prout(_("Already docked."));
225         return;
226     }
227     if (game.inorbit) {
228         prout(_("You must first leave standard orbit."));
229         return;
230     }
231     if (game.base.x==0 || abs(game.sector.x-game.base.x) > 1 || abs(game.sector.y-game.base.y) > 1) {
232         crmshp();
233         prout(_(" not adjacent to base."));
234         return;
235     }
236     game.condit = IHDOCKED;
237     if (verbose) prout(_("Docked."));
238     game.ididit = true;
239     if (game.energy < game.inenrg) game.energy = game.inenrg;
240     game.shield = game.inshld;
241     game.torps = game.intorps;
242     game.lsupres = game.inlsr;
243     game.state.crew = FULLCREW;
244     if (!damaged(DRADIO) &&
245         (is_scheduled(FCDBAS) || game.isatb == 1) && game.iseenit == 0) {
246         /* get attack report from base */
247         prout(_("Lt. Uhura- \"Captain, an important message from the starbase:\""));
248         attakreport(false);
249         game.iseenit = 1;
250     }
251 }
252
253 /* 
254  * This program originally required input in terms of a (clock)
255  * direction and distance. Somewhere in history, it was changed to
256  * cartesian coordinates. So we need to convert. I think
257  * "manual" input should still be done this way -- it's a real
258  * pain if the computer isn't working! Manual mode is still confusing
259  * because it involves giving x and y motions, yet the coordinates
260  * are always displayed y - x, where +y is downward!
261  */
262
263 static void getcd(bool isprobe, int akey)
264 /* get course and distance */
265 {
266     int irowq=game.quadrant.x, icolq=game.quadrant.y, key=0;
267     double xi, xj, xk, xl;
268     double deltax, deltay;
269     enum {unspecified, manual, automatic} navmode = unspecified;
270     enum {curt, normal, verbose} itemp = curt;
271     coord incr;
272     bool iprompt = false;
273
274     /* Get course direction and distance. If user types bad values, return
275        with DIREC = -1.0. */
276
277     game.direc = -1.0;
278         
279     if (game.landed == 1 && !isprobe) {
280         prout(_("Dummy! You can't leave standard orbit until you"));
281         proutn(_("are back aboard the "));
282         crmshp();
283         prout(".");
284         chew();
285         return;
286     }
287     while (navmode == unspecified) {
288         if (damaged(DCOMPTR)) {
289             if (isprobe)
290                 prout(_("Computer damaged; manual navigation only"));
291             else
292                 prout(_("Computer damaged; manual movement only"));
293             chew();
294             navmode = manual;
295             key = IHEOL;
296             break;
297         }
298         if (isprobe && akey != -1) {
299             /* For probe launch, use pre-scanned value first time */
300             key = akey;
301             akey = -1;
302         }
303         else 
304             key = scan();
305
306         if (key == IHEOL) {
307             proutn(_("Manual or automatic- "));
308             iprompt = true;
309             chew();
310         }
311         else if (key == IHALPHA) {
312             if (isit("manual")) {
313                 navmode = manual;
314                 key = scan();
315                 break;
316             }
317             else if (isit("automatic")) {
318                 navmode = automatic;
319                 key = scan();
320                 break;
321             }
322             else {
323                 huh();
324                 chew();
325                 return;
326             }
327         }
328         else { /* numeric */
329             if (isprobe)
330                 prout(_("(Manual navigation assumed.)"));
331             else
332                 prout(_("(Manual movement assumed.)"));
333             navmode = automatic;
334             break;
335         }
336     }
337
338     if (navmode == automatic) {
339         while (key == IHEOL) {
340             if (isprobe)
341                 proutn(_("Target quadrant or quadrant&sector- "));
342             else
343                 proutn(_("Destination sector or quadrant&sector- "));
344             chew();
345             iprompt = true;
346             key = scan();
347         }
348
349         if (key != IHREAL) {
350             huh();
351             return;
352         }
353         xi = aaitem;
354         key = scan();
355         if (key != IHREAL){
356             huh();
357             return;
358         }
359         xj = aaitem;
360         key = scan();
361         if (key == IHREAL) {
362             /* both quadrant and sector specified */
363             xk = aaitem;
364             key = scan();
365             if (key != IHREAL) {
366                 huh();
367                 return;
368             }
369             xl = aaitem;
370
371             irowq = xi + 0.5;
372             icolq = xj + 0.5;
373             incr.y = xk + 0.5;
374             incr.x = xl + 0.5;
375         }
376         else {
377             if (isprobe) {
378                 /* only quadrant specified -- go to center of dest quad */
379                 irowq = xi + 0.5;
380                 icolq = xj + 0.5;
381                 incr.y = incr.x = 5;
382             }
383             else {
384                 incr.y = xi + 0.5;
385                 incr.x = xj + 0.5;
386             }
387             itemp = normal;
388         }
389         if (!VALID_QUADRANT(icolq,irowq)||!VALID_SECTOR(incr.x,incr.y)) {
390             huh();
391             return;
392         }
393         skip(1);
394         if (!isprobe) {
395             if (itemp > curt) {
396                 if (iprompt) {
397                     prout(_("Helmsman Sulu- \"Course locked in for %s.\""),
398                           cramlc(sector, incr));
399                 }
400             }
401             else prout(_("Ensign Chekov- \"Course laid in, Captain.\""));
402         }
403         deltax = icolq - game.quadrant.y + 0.1*(incr.x-game.sector.y);
404         deltay = game.quadrant.x - irowq + 0.1*(game.sector.x-incr.y);
405     }
406     else { /* manual */
407         while (key == IHEOL) {
408             proutn(_("X and Y displacements- "));
409             chew();
410             iprompt = true;
411             key = scan();
412         }
413         itemp = verbose;
414         if (key != IHREAL) {
415             huh();
416             return;
417         }
418         deltax = aaitem;
419         key = scan();
420         if (key != IHREAL) {
421             huh();
422             return;
423         }
424         deltay = aaitem;
425     }
426     /* Check for zero movement */
427     if (deltax == 0 && deltay == 0) {
428         chew();
429         return;
430     }
431     if (itemp == verbose && !isprobe) {
432         skip(1);
433         prout(_("Helmsman Sulu- \"Aye, Sir.\""));
434     }
435     game.dist = sqrt(deltax*deltax + deltay*deltay);
436     game.direc = atan2(deltax, deltay)*1.90985932;
437     if (game.direc < 0.0) game.direc += 12.0;
438     chew();
439     return;
440 }
441                 
442
443
444 void impuls(void) 
445 /* move under impulse power */
446 {
447     double power;
448
449     game.ididit = false;
450     if (damaged(DIMPULS)) {
451         chew();
452         skip(1);
453         prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""));
454         return;
455     }
456
457     if (game.energy > 30.0) {
458         getcd(false, 0);
459         if (game.direc == -1.0) return;
460         power = 20.0 + 100.0*game.dist;
461     }
462     else
463         power = 30.0;
464
465     if (power >= game.energy) {
466         /* Insufficient power for trip */
467         skip(1);
468         prout(_("First Officer Spock- \"Captain, the impulse engines"));
469         prout(_("require 20.0 units to engage, plus 100.0 units per"));
470         if (game.energy > 30) {
471             proutn(_("quadrant.  We can go, therefore, a maximum of %d"),
472                    (int)(0.01 * (game.energy-20.0)-0.05));
473             prout(_(" quadrants.\""));
474         }
475         else {
476             prout(_("quadrant.  They are, therefore, useless.\""));
477         }
478         chew();
479         return;
480     }
481     /* Make sure enough time is left for the trip */
482     game.optime = game.dist/0.095;
483     if (game.optime >= game.state.remtime) {
484         prout(_("First Officer Spock- \"Captain, our speed under impulse"));
485         prout(_("power is only 0.95 sectors per stardate. Are you sure"));
486         proutn(_("we dare spend the time?\" "));
487         if (ja() == false) return;
488     }
489     /* Activate impulse engines and pay the cost */
490     imove();
491     game.ididit = true;
492     if (game.alldone) return;
493     power = 20.0 + 100.0*game.dist;
494     game.energy -= power;
495     game.optime = game.dist/0.095;
496     if (game.energy <= 0) finish(FNRG);
497     return;
498 }
499
500
501 void warp(bool timewarp)
502 /* move under warp drive */
503 {
504     int iwarp;
505     bool blooey = false, twarp = false;
506     double power;
507
508     if (!timewarp) { /* Not WARPX entry */
509         game.ididit = false;
510         if (game.damage[DWARPEN] > 10.0) {
511             chew();
512             skip(1);
513             prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""));
514             return;
515         }
516         if (damaged(DWARPEN) && game.warpfac > 4.0) {
517             chew();
518             skip(1);
519             prout(_("Engineer Scott- \"Sorry, Captain. Until this damage"));
520             prout(_("  is repaired, I can only give you warp 4.\""));
521             return;
522         }
523                         
524         /* Read in course and distance */
525         getcd(false, 0);
526         if (game.direc == -1.0) return;
527
528         /* Make sure starship has enough energy for the trip */
529         power = (game.dist+0.05)*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1);
530
531
532         if (power >= game.energy) {
533             /* Insufficient power for trip */
534             game.ididit = false;
535             skip(1);
536             prout(_("Engineering to bridge--"));
537             if (!game.shldup || 0.5*power > game.energy) {
538                 iwarp = pow((game.energy/(game.dist+0.05)), 0.333333333);
539                 if (iwarp <= 0) {
540                     prout(_("We can't do it, Captain. We don't have enough energy."));
541                 }
542                 else {
543                     proutn(_("We don't have enough energy, but we could do it at warp %d"), iwarp);
544                     if (game.shldup) {
545                         prout(",");
546                         prout(_("if you'll lower the shields."));
547                     }
548                     else
549                         prout(".");
550                 }
551             }
552             else
553                 prout(_("We haven't the energy to go that far with the shields up."));
554             return;
555         }
556                                                 
557         /* Make sure enough time is left for the trip */
558         game.optime = 10.0*game.dist/game.wfacsq;
559         if (game.optime >= 0.8*game.state.remtime) {
560             skip(1);
561             prout(_("First Officer Spock- \"Captain, I compute that such"));
562             proutn(_("  a trip would require approximately %2.0f"),
563                    100.0*game.optime/game.state.remtime);
564             prout(_(" percent of our"));
565             proutn(_("  remaining time.  Are you sure this is wise?\" "));
566             if (ja() == false) { game.ididit = false; game.optime=0; return;}
567         }
568     }
569     /* Entry WARPX */
570     if (game.warpfac > 6.0) {
571         /* Decide if engine damage will occur */
572         double prob = game.dist*(6.0-game.warpfac)*(6.0-game.warpfac)/66.666666666;
573         if (prob > Rand()) {
574             blooey = true;
575             game.dist = Rand()*game.dist;
576         }
577         /* Decide if time warp will occur */
578         if (0.5*game.dist*pow(7.0,game.warpfac-10.0) > Rand()) twarp = true;
579         if (idebug && game.warpfac==10 && !twarp) {
580             blooey = false;
581             proutn("=== Force time warp? ");
582             if (ja() == true) twarp = true;
583         }
584         if (blooey || twarp) {
585             /* If time warp or engine damage, check path */
586             /* If it is obstructed, don't do warp or damage */
587             double angle = ((15.0-game.direc)*0.5235998);
588             double deltax = -sin(angle);
589             double deltay = cos(angle);
590             double bigger, x, y;
591             int n, l, ix, iy;
592             if (fabs(deltax) > fabs(deltay))
593                 bigger = fabs(deltax);
594             else
595                 bigger = fabs(deltay);
596                         
597             deltax /= bigger;
598             deltay /= bigger;
599             n = 10.0 * game.dist * bigger +0.5;
600             x = game.sector.x;
601             y = game.sector.y;
602             for (l = 1; l <= n; l++) {
603                 x += deltax;
604                 ix = x + 0.5;
605                 y += deltay;
606                 iy = y +0.5;
607                 if (!VALID_SECTOR(ix, iy)) break;
608                 if (game.quad[ix][iy] != IHDOT) {
609                     blooey = false;
610                     twarp = false;
611                 }
612             }
613         }
614     }
615                                 
616
617     /* Activate Warp Engines and pay the cost */
618     imove();
619     if (game.alldone) return;
620     game.energy -= game.dist*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1);
621     if (game.energy <= 0) finish(FNRG);
622     game.optime = 10.0*game.dist/game.wfacsq;
623     if (twarp) timwrp();
624     if (blooey) {
625         game.damage[DWARPEN] = game.damfac*(3.0*Rand()+1.0);
626         skip(1);
627         prout(_("Engineering to bridge--"));
628         prout(_("  Scott here.  The warp engines are damaged."));
629         prout(_("  We'll have to reduce speed to warp 4."));
630     }
631     game.ididit = true;
632     return;
633 }
634
635
636
637 void setwrp(void) 
638 /* change the warp factor */
639 {
640     int key;
641     double oldfac;
642         
643     while ((key=scan()) == IHEOL) {
644         chew();
645         proutn(_("Warp factor- "));
646     }
647     chew();
648     if (key != IHREAL) {
649         huh();
650         return;
651     }
652     if (game.damage[DWARPEN] > 10.0) {
653         prout(_("Warp engines inoperative."));
654         return;
655     }
656     if (damaged(DWARPEN) && aaitem > 4.0) {
657         prout(_("Engineer Scott- \"I'm doing my best, Captain,"));
658         prout(_("  but right now we can only go warp 4.\""));
659         return;
660     }
661     if (aaitem > 10.0) {
662         prout(_("Helmsman Sulu- \"Our top speed is warp 10, Captain.\""));
663         return;
664     }
665     if (aaitem < 1.0) {
666         prout(_("Helmsman Sulu- \"We can't go below warp 1, Captain.\""));
667         return;
668     }
669     oldfac = game.warpfac;
670     game.warpfac = aaitem;
671     game.wfacsq=game.warpfac*game.warpfac;
672     if (game.warpfac <= oldfac || game.warpfac <= 6.0) {
673         prout(_("Helmsman Sulu- \"Warp factor %d, Captain.\""),
674                (int)game.warpfac);
675         return;
676     }
677     if (game.warpfac < 8.00) {
678         prout(_("Engineer Scott- \"Aye, but our maximum safe speed is warp 6.\""));
679         return;
680     }
681     if (game.warpfac == 10.0) {
682         prout(_("Engineer Scott- \"Aye, Captain, we'll try it.\""));
683         return;
684     }
685     prout(_("Engineer Scott- \"Aye, Captain, but our engines may not take it.\""));
686     return;
687 }
688
689 void atover(bool igrab) 
690 /* cope with being tossed out of quadrant by supernova or yanked by beam */
691 {
692     double power, distreq;
693
694     chew();
695     /* is captain on planet? */
696     if (game.landed==1) {
697         if (damaged(DTRANSP)) {
698             finish(FPNOVA);
699             return;
700         }
701         prout(_("Scotty rushes to the transporter controls."));
702         if (game.shldup) {
703             prout(_("But with the shields up it's hopeless."));
704             finish(FPNOVA);
705         }
706         prouts(_("His desperate attempt to rescue you . . ."));
707         if (Rand() <= 0.5) {
708             prout(_("fails."));
709             finish(FPNOVA);
710             return;
711         }
712         prout(_("SUCCEEDS!"));
713         if (game.imine) {
714             game.imine = false;
715             proutn(_("The crystals mined were "));
716             if (Rand() <= 0.25) {
717                 prout(_("lost."));
718             }
719             else {
720                 prout(_("saved."));
721                 game.icrystl = 1;
722             }
723         }
724     }
725     if (igrab) return;
726
727     /* Check to see if captain in shuttle craft */
728     if (game.icraft) finish(FSTRACTOR);
729     if (game.alldone) return;
730
731     /* Inform captain of attempt to reach safety */
732     skip(1);
733     do {
734         if (game.justin) {
735             prouts(_("***RED ALERT!  RED ALERT!"));
736             skip(1);
737             proutn(_("The "));
738             crmshp();
739             prout(_(" has stopped in a quadrant containing"));
740             prouts(_("   a supernova."));
741             skip(2);
742         }
743         proutn(_("***Emergency automatic override attempts to hurl "));
744         crmshp();
745         skip(1);
746         prout(_("safely out of quadrant."));
747         if (!damaged(DRADIO))
748             game.state.galaxy[game.quadrant.x][game.quadrant.y].charted = true;
749         /* Try to use warp engines */
750         if (damaged(DWARPEN)) {
751             skip(1);
752             prout(_("Warp engines damaged."));
753             finish(FSNOVAED);
754             return;
755         }
756         game.warpfac = 6.0+2.0*Rand();
757         game.wfacsq = game.warpfac * game.warpfac;
758         prout(_("Warp factor set to %d"), (int)game.warpfac);
759         power = 0.75*game.energy;
760         game.dist = power/(game.warpfac*game.warpfac*game.warpfac*(game.shldup+1));
761         distreq = 1.4142+Rand();
762         if (distreq < game.dist) game.dist = distreq;
763         game.optime = 10.0*game.dist/game.wfacsq;
764         game.direc = 12.0*Rand();       /* How dumb! */
765         game.justin = false;
766         game.inorbit = false;
767         warp(true);
768         if (!game.justin) {
769             /* This is bad news, we didn't leave quadrant. */
770             if (game.alldone) return;
771             skip(1);
772             prout(_("Insufficient energy to leave quadrant."));
773             finish(FSNOVAED);
774             return;
775         }
776     } while 
777         /* Repeat if another snova */
778         (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova);
779     if (KLINGREM==0) 
780         finish(FWON); /* Snova killed remaining enemy. */
781 }
782
783 void timwrp() 
784 /* let's do the time warp again */
785 {
786     int l, gotit;
787     prout(_("***TIME WARP ENTERED."));
788     if (game.state.snap && Rand() < 0.5) {
789         /* Go back in time */
790         prout(_("You are traveling backwards in time %d stardates."),
791               (int)(game.state.date-game.snapsht.date));
792         game.state = game.snapsht;
793         game.state.snap = 0;
794         if (game.state.remcom) {
795             schedule(FTBEAM, expran(game.intime/game.state.remcom));
796             schedule(FBATTAK, expran(0.3*game.intime));
797         }
798         schedule(FSNOVA, expran(0.5*game.intime));
799         /* next snapshot will be sooner */
800         schedule(FSNAP, expran(0.25*game.state.remtime));
801                                 
802         if (game.state.nscrem) schedule(FSCMOVE, 0.2777);
803         game.isatb = 0;
804         unschedule(FCDBAS);
805         unschedule(FSCDBAS);
806         game.battle.x = game.battle.y = 0;
807
808         /* Make sure Galileo is consistant -- Snapshot may have been taken
809            when on planet, which would give us two Galileos! */
810         gotit = 0;
811         for (l = 0; l < game.inplan; l++) {
812             if (game.state.plnets[l].known == shuttle_down) {
813                 gotit = 1;
814                 if (game.iscraft==1 && game.ship==IHE) {
815                     prout(_("Checkov-  \"Security reports the Galileo has disappeared, Sir!"));
816                     game.iscraft = 0;
817                 }
818             }
819         }
820         /* Likewise, if in the original time the Galileo was abandoned, but
821            was on ship earlier, it would have vanished -- lets restore it */
822         if (game.iscraft==0 && gotit==0 && game.damage[DSHUTTL] >= 0.0) {
823             prout(_("Checkov-  \"Security reports the Galileo has reappeared in the dock!\""));
824             game.iscraft = 1;
825         }
826         /* 
827          * There used to be code to do the actual reconstrction here,
828          * but the starchart is now part of the snapshotted galaxy state.
829          */
830         prout(_("Spock has reconstructed a correct star chart from memory"));
831     }
832     else {
833         /* Go forward in time */
834         game.optime = -0.5*game.intime*log(Rand());
835         prout(_("You are traveling forward in time %d stardates."), (int)game.optime);
836         /* cheat to make sure no tractor beams occur during time warp */
837         postpone(FTBEAM, game.optime);
838         game.damage[DRADIO] += game.optime;
839     }
840     newqad(false);
841     events();   /* Stas Sergeev added this -- do pending events */
842 }
843
844 void probe(void) 
845 /* launch deep-space probe */
846 {
847     double angle, bigger;
848     int key;
849     /* New code to launch a deep space probe */
850     if (game.nprobes == 0) {
851         chew();
852         skip(1);
853         if (game.ship == IHE) 
854             prout(_("Engineer Scott- \"We have no more deep space probes, Sir.\""));
855         else
856             prout(_("Ye Faerie Queene has no deep space probes."));
857         return;
858     }
859     if (damaged(DDSP)) {
860         chew();
861         skip(1);
862         prout(_("Engineer Scott- \"The probe launcher is damaged, Sir.\""));
863         return;
864     }
865     if (is_scheduled(FDSPROB)) {
866         chew();
867         skip(1);
868         if (damaged(DRADIO) && game.condit != IHDOCKED) {
869             prout(_("Spock-  \"Records show the previous probe has not yet"));
870             prout(_("   reached its destination.\""));
871         }
872         else
873             prout(_("Uhura- \"The previous probe is still reporting data, Sir.\""));
874         return;
875     }
876     key = scan();
877
878     if (key == IHEOL) {
879         /* slow mode, so let Kirk know how many probes there are left */
880         prout(game.nprobes==1 ? _("%d probe left.") : _("%d probes left."), game.nprobes);
881         proutn(_("Are you sure you want to fire a probe? "));
882         if (ja() == false) return;
883     }
884
885     game.isarmed = false;
886     if (key == IHALPHA && strcmp(citem,"armed") == 0) {
887         game.isarmed = true;
888         key = scan();
889     }
890     else if (key == IHEOL) {
891         proutn(_("Arm NOVAMAX warhead? "));
892         game.isarmed = ja();
893     }
894     getcd(true, key);
895     if (game.direc == -1.0) return;
896     game.nprobes--;
897     angle = ((15.0 - game.direc) * 0.5235988);
898     game.probeinx = -sin(angle);
899     game.probeiny = cos(angle);
900     if (fabs(game.probeinx) > fabs(game.probeiny))
901         bigger = fabs(game.probeinx);
902     else
903         bigger = fabs(game.probeiny);
904                 
905     game.probeiny /= bigger;
906     game.probeinx /= bigger;
907     game.proben = 10.0*game.dist*bigger +0.5;
908     game.probex = game.quadrant.x*QUADSIZE + game.sector.x - 1; // We will use better packing than original
909     game.probey = game.quadrant.y*QUADSIZE + game.sector.y - 1;
910     game.probec = game.quadrant;
911     schedule(FDSPROB, 0.01); // Time to move one sector
912     prout(_("Ensign Chekov-  \"The deep space probe is launched, Captain.\""));
913     game.ididit = true;
914     return;
915 }
916
917 void mayday(void) 
918 /* yell for help from nearest starbase */
919 {
920     /* There's more than one way to move in this game! */
921     double ddist, xdist, probf;
922     int line = 0, m, ix, iy;
923
924     chew();
925     /* Test for game.conditions which prevent calling for help */
926     if (game.condit == IHDOCKED) {
927         prout(_("Lt. Uhura-  \"But Captain, we're already docked.\""));
928         return;
929     }
930     if (damaged(DRADIO)) {
931         prout(_("Subspace radio damaged."));
932         return;
933     }
934     if (game.state.rembase==0) {
935         prout(_("Lt. Uhura-  \"Captain, I'm not getting any response from Starbase.\""));
936         return;
937     }
938     if (game.landed == 1) {
939         proutn(_("You must be aboard the "));
940         crmshp();
941         prout(".");
942         return;
943     }
944     /* OK -- call for help from nearest starbase */
945     game.nhelp++;
946     if (game.base.x!=0) {
947         /* There's one in this quadrant */
948         ddist = sqrt(square(game.base.x-game.sector.x)+square(game.base.y-game.sector.y));
949     }
950     else {
951         ddist = FOREVER;
952         for_starbases(m) {
953             xdist=10.0*sqrt(square(game.state.baseq[m].x-game.quadrant.x)+square(game.state.baseq[m].y-game.quadrant.y));
954             if (xdist < ddist) {
955                 ddist = xdist;
956                 line = m;
957             }
958         }
959         /* Since starbase not in quadrant, set up new quadrant */
960         game.quadrant = game.state.baseq[line];
961         newqad(true);
962     }
963     /* dematerialize starship */
964     game.quad[game.sector.x][game.sector.y]=IHDOT;
965     proutn(_("Starbase in %s responds--"), cramlc(quadrant, game.quadrant));
966     proutn("");
967     crmshp();
968     prout(_(" dematerializes."));
969     game.sector.x=0;
970     for (m = 1; m <= 5; m++) {
971         ix = game.base.x+3.0*Rand()-1;
972         iy = game.base.y+3.0*Rand()-1;
973         if (VALID_SECTOR(ix,iy) && game.quad[ix][iy]==IHDOT) {
974             /* found one -- finish up */
975             game.sector.x=ix;
976             game.sector.y=iy;
977             break;
978         }
979     }
980     if (game.sector.x==0){
981         prout(_("You have been lost in space..."));
982         finish(FMATERIALIZE);
983         return;
984     }
985     /* Give starbase three chances to rematerialize starship */
986     probf = pow((1.0 - pow(0.98,ddist)), 0.33333333);
987     for (m = 1; m <= 3; m++) {
988         switch (m) {
989         case 1: proutn(_("1st")); break;
990         case 2: proutn(_("2nd")); break;
991         case 3: proutn(_("3rd")); break;
992         }
993         proutn(_(" attempt to re-materialize "));
994         crmshp();
995         switch (m){
996         case 1: game.quad[ix][iy]=IHMATER0;
997             break;
998         case 2: game.quad[ix][iy]=IHMATER1;
999             break;
1000         case 3: game.quad[ix][iy]=IHMATER2;
1001             break;
1002         }
1003         textcolor(RED);
1004         warble();
1005         if (Rand() > probf) break;
1006         prout(_("fails."));
1007         delay(500);
1008         textcolor(DEFAULT);
1009     }
1010     if (m > 3) {
1011         game.quad[ix][iy]=IHQUEST;
1012         game.alive = 0;
1013         drawmaps(1);
1014         setwnd(message_window);
1015         finish(FMATERIALIZE);
1016         return;
1017     }
1018     game.quad[ix][iy]=game.ship;
1019     textcolor(GREEN);
1020     prout(_("succeeds."));
1021     textcolor(DEFAULT);
1022     dock(false);
1023     skip(1);
1024     prout(_("Lt. Uhura-  \"Captain, we made it!\""));
1025 }