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