Introduce 'coord' data structure, an (x,y) tuple.
[super-star-trek.git] / src / moving.c
1 #include <unistd.h>
2 #include "sstlinux.h"
3 #include "sst.h"
4
5 static void getcd(int, 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(int 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(int i) 
493 {
494     int blooey=0, twarp=0, iwarp;
495     double power;
496
497     if (i!=2) { /* 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 || 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 #ifdef DEBUG
569         if (game.idebug &&game.warpfac==10 && twarp==0) {
570             blooey=0;
571             proutn("Force time warp? ");
572             if (ja()==1) twarp=1;
573         }
574 #endif
575         if (blooey || twarp) {
576             /* If time warp or engine damage, check path */
577             /* If it is obstructed, don't do warp or damage */
578             double angle = ((15.0-game.direc)*0.5235998);
579             double deltax = -sin(angle);
580             double deltay = cos(angle);
581             double bigger, x, y;
582             int n, l, ix, iy;
583             if (fabs(deltax) > fabs(deltay))
584                 bigger = fabs(deltax);
585             else
586                 bigger = fabs(deltay);
587                         
588             deltax /= bigger;
589             deltay /= bigger;
590             n = 10.0 * game.dist * bigger +0.5;
591             x = game.sector.x;
592             y = game.sector.y;
593             for (l = 1; l <= n; l++) {
594                 x += deltax;
595                 ix = x + 0.5;
596                 y += deltay;
597                 iy = y +0.5;
598                 if (!VALID_SECTOR(ix, iy)) break;
599                 if (game.quad[ix][iy] != IHDOT) {
600                     blooey = 0;
601                     twarp = 0;
602                 }
603             }
604         }
605     }
606                                 
607
608     /* Activate Warp Engines and pay the cost */
609     imove();
610     if (game.alldone) return;
611     game.energy -= game.dist*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1);
612     if (game.energy <= 0) finish(FNRG);
613     game.optime = 10.0*game.dist/game.wfacsq;
614     if (twarp) timwrp();
615     if (blooey) {
616         game.damage[DWARPEN] = game.damfac*(3.0*Rand()+1.0);
617         skip(1);
618         prout("Engineering to bridge--");
619         prout("  Scott here.  The warp engines are damaged.");
620         prout("  We'll have to reduce speed to warp 4.");
621     }
622     game.ididit = 1;
623     return;
624 }
625
626
627
628 void setwrp(void) 
629 {
630     int key;
631     double oldfac;
632         
633     while ((key=scan()) == IHEOL) {
634         chew();
635         proutn("Warp factor- ");
636     }
637     chew();
638     if (key != IHREAL) {
639         huh();
640         return;
641     }
642     if (game.damage[DWARPEN] > 10.0) {
643         prout("Warp engines inoperative.");
644         return;
645     }
646     if (game.damage[DWARPEN] > 0.0 && aaitem > 4.0) {
647         prout("Engineer Scott- \"I'm doing my best, Captain,");
648         prout("  but right now we can only go warp 4.\"");
649         return;
650     }
651     if (aaitem > 10.0) {
652         prout("Helmsman Sulu- \"Our top speed is warp 10, Captain.\"");
653         return;
654     }
655     if (aaitem < 1.0) {
656         prout("Helmsman Sulu- \"We can't go below warp 1, Captain.\"");
657         return;
658     }
659     oldfac = game.warpfac;
660     game.warpfac = aaitem;
661     game.wfacsq=game.warpfac*game.warpfac;
662     if (game.warpfac <= oldfac || game.warpfac <= 6.0) {
663         proutn("Helmsman Sulu- \"Warp factor %d, Captain.\"", 
664                (int)game.warpfac);
665         return;
666     }
667     if (game.warpfac < 8.00) {
668         prout("Engineer Scott- \"Aye, but our maximum safe speed is warp 6.\"");
669         return;
670     }
671     if (game.warpfac == 10.0) {
672         prout("Engineer Scott- \"Aye, Captain, we'll try it.\"");
673         return;
674     }
675     prout("Engineer Scott- \"Aye, Captain, but our engines may not take it.\"");
676     return;
677 }
678
679 void atover(int igrab) 
680 {
681     double power, distreq;
682
683     chew();
684     /* is captain on planet? */
685     if (game.landed==1) {
686         if (game.damage[DTRANSP]) {
687             finish(FPNOVA);
688             return;
689         }
690         prout("Scotty rushes to the transporter controls.");
691         if (game.shldup) {
692             prout("But with the shields up it's hopeless.");
693             finish(FPNOVA);
694         }
695         prouts("His desperate attempt to rescue you . . .");
696         if (Rand() <= 0.5) {
697             prout("fails.");
698             finish(FPNOVA);
699             return;
700         }
701         prout("SUCCEEDS!");
702         if (game.imine) {
703             game.imine = 0;
704             proutn("The crystals mined were ");
705             if (Rand() <= 0.25) {
706                 prout("lost.");
707             }
708             else {
709                 prout("saved.");
710                 game.icrystl = 1;
711             }
712         }
713     }
714     if (igrab) return;
715
716     /* Check to see if captain in shuttle craft */
717     if (game.icraft) finish(FSTRACTOR);
718     if (game.alldone) return;
719
720     /* Inform captain of attempt to reach safety */
721     skip(1);
722     do {
723         if (game.justin) {
724             prouts("***RED ALERT!  READ ALERT!");
725             skip(1);
726             proutn("The ");
727             crmshp();
728             prout(" has stopped in a quadrant containing");
729             prouts("   a supernova.");
730             skip(2);
731         }
732         proutn("***Emergency automatic override attempts to hurl ");
733         crmshp();
734         skip(1);
735         prout("safely out of quadrant.");
736         if (game.damage[DRADIO] == 0.0)
737             game.state.galaxy[game.quadrant.x][game.quadrant.y].charted = TRUE;
738         /* Try to use warp engines */
739         if (game.damage[DWARPEN]) {
740             skip(1);
741             prout("Warp engines damaged.");
742             finish(FSNOVAED);
743             return;
744         }
745         game.warpfac = 6.0+2.0*Rand();
746         game.wfacsq = game.warpfac * game.warpfac;
747         prout("Warp factor set to %d", (int)game.warpfac);
748         power = 0.75*game.energy;
749         game.dist = power/(game.warpfac*game.warpfac*game.warpfac*(game.shldup+1));
750         distreq = 1.4142+Rand();
751         if (distreq < game.dist) game.dist = distreq;
752         game.optime = 10.0*game.dist/game.wfacsq;
753         game.direc = 12.0*Rand();       /* How dumb! */
754         game.justin = 0;
755         game.inorbit = 0;
756         warp(2);
757         if (game.justin == 0) {
758             /* This is bad news, we didn't leave quadrant. */
759             if (game.alldone) return;
760             skip(1);
761             prout("Insufficient energy to leave quadrant.");
762             finish(FSNOVAED);
763             return;
764         }
765     } while 
766         /* Repeat if another snova */
767         (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova);
768     if (KLINGREM==0) 
769         finish(FWON); /* Snova killed remaining enemy. */
770 }
771
772 void timwrp() 
773 {
774     int l, gotit;
775     prout("***TIME WARP ENTERED.");
776     if (game.state.snap && Rand() < 0.5) {
777         /* Go back in time */
778         prout("You are traveling backwards in time %d stardates.",
779               (int)(game.state.date-game.snapsht.date));
780         game.state = game.snapsht;
781         game.state.snap = 0;
782         if (game.state.remcom) {
783             schedule(FTBEAM, expran(game.intime/game.state.remcom));
784             schedule(FBATTAK, expran(0.3*game.intime));
785         }
786         schedule(FSNOVA, expran(0.5*game.intime));
787         /* next snapshot will be sooner */
788         schedule(FSNAP, expran(0.25*game.state.remtime));
789                                 
790         if (game.state.nscrem) schedule(FSCMOVE, 0.2777);
791         game.isatb = 0;
792         unschedule(FCDBAS);
793         unschedule(FSCDBAS);
794         game.battle.x = game.battle.y = 0;
795
796         /* Make sure Galileo is consistant -- Snapshot may have been taken
797            when on planet, which would give us two Galileos! */
798         gotit = 0;
799         for (l = 0; l < game.inplan; l++) {
800             if (game.state.plnets[l].known == shuttle_down) {
801                 gotit = 1;
802                 if (game.iscraft==1 && game.ship==IHE) {
803                     prout("Checkov-  \"Security reports the Galileo has disappeared, Sir!");
804                     game.iscraft = 0;
805                 }
806             }
807         }
808         /* Likewise, if in the original time the Galileo was abandoned, but
809            was on ship earlier, it would have vanished -- lets restore it */
810         if (game.iscraft==0 && gotit==0 && game.damage[DSHUTTL] >= 0.0) {
811             prout("Checkov-  \"Security reports the Galileo has reappeared in the dock!\"");
812             game.iscraft = 1;
813         }
814         /* 
815          * There used to be code to do the actual reconstrction here,
816          * but the starchart is now part of the snapshotted galaxy state.
817          */
818         prout("Spock has reconstructed a correct star chart from memory");
819     }
820     else {
821         /* Go forward in time */
822         game.optime = -0.5*game.intime*log(Rand());
823         prout("You are traveling forward in time %d stardates.", (int)game.optime);
824         /* cheat to make sure no tractor beams occur during time warp */
825         postpone(FTBEAM, game.optime);
826         game.damage[DRADIO] += game.optime;
827     }
828     newqad(0);
829     events();   /* Stas Sergeev added this -- do pending events */
830 }
831
832 void probe(void) 
833 {
834     double angle, bigger;
835     int key;
836     /* New code to launch a deep space probe */
837     if (game.nprobes == 0) {
838         chew();
839         skip(1);
840         if (game.ship == IHE) 
841             prout("Engineer Scott- \"We have no more deep space probes, Sir.\"");
842         else
843             prout("Ye Faerie Queene has no deep space probes.");
844         return;
845     }
846     if (game.damage[DDSP] != 0.0) {
847         chew();
848         skip(1);
849         prout("Engineer Scott- \"The probe launcher is damaged, Sir.\"");
850         return;
851     }
852     if (is_scheduled(FDSPROB)) {
853         chew();
854         skip(1);
855         if (game.damage[DRADIO] != 0 && game.condit != IHDOCKED) {
856             prout("Spock-  \"Records show the previous probe has not yet");
857             prout("   reached its destination.\"");
858         }
859         else
860             prout("Uhura- \"The previous probe is still reporting data, Sir.\"");
861         return;
862     }
863     key = scan();
864
865     if (key == IHEOL) {
866         /* slow mode, so let Kirk know how many probes there are left */
867         prout(game.nprobes==1 ? "%d probe left." : "%d probes left.", game.nprobes);
868         proutn("Are you sure you want to fire a probe? ");
869         if (ja()==0) return;
870     }
871
872     game.isarmed = FALSE;
873     if (key == IHALPHA && strcmp(citem,"armed") == 0) {
874         game.isarmed = TRUE;
875         key = scan();
876     }
877     else if (key == IHEOL) {
878         proutn("Arm NOVAMAX warhead? ");
879         game.isarmed = ja();
880     }
881     getcd(TRUE, key);
882     if (game.direc == -1.0) return;
883     game.nprobes--;
884     angle = ((15.0 - game.direc) * 0.5235988);
885     game.probeinx = -sin(angle);
886     game.probeiny = cos(angle);
887     if (fabs(game.probeinx) > fabs(game.probeiny))
888         bigger = fabs(game.probeinx);
889     else
890         bigger = fabs(game.probeiny);
891                 
892     game.probeiny /= bigger;
893     game.probeinx /= bigger;
894     game.proben = 10.0*game.dist*bigger +0.5;
895     game.probex = game.quadrant.x*QUADSIZE + game.sector.x - 1; // We will use better packing than original
896     game.probey = game.quadrant.y*QUADSIZE + game.sector.y - 1;
897     game.probec = game.quadrant;
898     schedule(FDSPROB, 0.01); // Time to move one sector
899     prout("Ensign Chekov-  \"The deep space probe is launched, Captain.\"");
900     game.ididit = 1;
901     return;
902 }
903
904 void mayday(void) 
905 {
906     /* There's more than one way to move in this game! */
907     double ddist, xdist, probf;
908     int line = 0, l, ix, iy;
909
910     chew();
911     /* Test for game.conditions which prevent calling for help */
912     if (game.condit == IHDOCKED) {
913         prout("Lt. Uhura-  \"But Captain, we're already docked.\"");
914         return;
915     }
916     if (game.damage[DRADIO] != 0) {
917         prout("Subspace radio damaged.");
918         return;
919     }
920     if (game.state.rembase==0) {
921         prout("Lt. Uhura-  \"Captain, I'm not getting any response from Starbase.\"");
922         return;
923     }
924     if (game.landed == 1) {
925         proutn("You must be aboard the ");
926         crmshp();
927         prout(".");
928         return;
929     }
930     /* OK -- call for help from nearest starbase */
931     game.nhelp++;
932     if (game.base.x!=0) {
933         /* There's one in this quadrant */
934         ddist = sqrt(square(game.base.x-game.sector.x)+square(game.base.y-game.sector.y));
935     }
936     else {
937         ddist = FOREVER;
938         for_starbases(l) {
939             xdist=10.0*sqrt(square(game.state.baseq[l].x-game.quadrant.x)+square(game.state.baseq[l].y-game.quadrant.y));
940             if (xdist < ddist) {
941                 ddist = xdist;
942                 line = l;
943             }
944         }
945         /* Since starbase not in quadrant, set up new quadrant */
946         game.quadrant.x = game.state.baseq[line].x;
947         game.quadrant.y = game.state.baseq[line].y;
948         newqad(1);
949     }
950     /* dematerialize starship */
951     game.quad[game.sector.x][game.sector.y]=IHDOT;
952     proutn("Starbase in %s responds--", cramlc(quadrant, game.quadrant));
953     proutn("");
954     crmshp();
955     prout(" dematerializes.");
956     game.sector.x=0;
957     for (l = 1; l <= 5; l++) {
958         ix = game.base.x+3.0*Rand()-1;
959         iy = game.base.y+3.0*Rand()-1;
960         if (VALID_SECTOR(ix,iy) && game.quad[ix][iy]==IHDOT) {
961             /* found one -- finish up */
962             game.sector.x=ix;
963             game.sector.y=iy;
964             break;
965         }
966     }
967     if (game.sector.x==0){
968         prout("You have been lost in space...");
969         finish(FMATERIALIZE);
970         return;
971     }
972     /* Give starbase three chances to rematerialize starship */
973     probf = pow((1.0 - pow(0.98,ddist)), 0.33333333);
974     for (l = 1; l <= 3; l++) {
975         switch (l) {
976         case 1: proutn("1st"); break;
977         case 2: proutn("2nd"); break;
978         case 3: proutn("3rd"); break;
979         }
980         proutn(" attempt to re-materialize ");
981         crmshp();
982         switch (l){
983         case 1: game.quad[ix][iy]=IHMATER0;
984             break;
985         case 2: game.quad[ix][iy]=IHMATER1;
986             break;
987         case 3: game.quad[ix][iy]=IHMATER2;
988             break;
989         }
990         textcolor(RED);
991         warble();
992         if (Rand() > probf) break;
993         prout("fails.");
994         delay(500);
995         textcolor(DEFAULT);
996     }
997     if (l > 3) {
998         game.quad[ix][iy]=IHQUEST;
999         game.alive = 0;
1000         drawmaps(1);
1001         setwnd(message_window);
1002         finish(FMATERIALIZE);
1003         return;
1004     }
1005     game.quad[ix][iy]=game.ship;
1006     textcolor(GREEN);
1007     prout("succeeds.");
1008     textcolor(DEFAULT);
1009     dock(0);
1010     skip(1);
1011     prout("Lt. Uhura-  \"Captain, we made it!\"");
1012 }