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