Parallel cleanups in the C and scratch Python code.
[super-star-trek.git] / src / reports.c
1 #include "sst.h"
2 #include <math.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 void attakreport(bool curt)
7 /* report status of bases under attack */
8 {
9     if (!curt) {
10         if (is_scheduled(FCDBAS)) {
11             prout(_("Starbase in %s is currently under Commander attack."),
12                   cramlc(quadrant, game.battle));
13             prout(_("It can hold out until Stardate %d."),
14                   (int)scheduled(FCDBAS));
15         }
16         else if (game.isatb == 1) {
17             prout(_("Starbase in %s is under Super-commander attack."),
18                   cramlc(quadrant, game.state.kscmdr));
19             prout(_("It can hold out until Stardate %d."),
20                   (int)scheduled(FSCDBAS));
21         } else {
22             prout(_("No Starbase is currently under attack."));
23         }
24     } else {
25         if (is_scheduled(FCDBAS))
26             proutn(_("Base in %i - %i attacked by C. Alive until %.1f"), game.battle.x, game.battle.y, scheduled(FCDBAS));
27         if (game.isatb)
28             proutn(_("Base in %i - %i attacked by S. Alive until %.1f"), game.state.kscmdr.x, game.state.kscmdr.y, scheduled(FSCDBAS));
29         clreol();
30     }
31 }
32         
33
34 void report(void)
35 /* report on general game status */
36 {
37     char *s1,*s2,*s3;
38
39     chew();
40     s1 = (game.thawed?_("thawed "):"");
41     switch (game.length) {
42     case 1: s2=_("short"); break;
43     case 2: s2=_("medium"); break;
44     case 4: s2=_("long"); break;
45     default: s2=_("unknown length"); break;
46     }
47     switch (game.skill) {
48     case SKILL_NOVICE: s3=_("novice"); break;
49     case SKILL_FAIR: s3=_("fair"); break;
50     case SKILL_GOOD: s3=_("good"); break;
51     case SKILL_EXPERT: s3=_("expert"); break;
52     case SKILL_EMERITUS: s3=_("emeritus"); break;
53     default: s3=_("skilled"); break;
54     }
55     skip(1);
56     prout(_("You %s a %s%s %s game."),
57           game.alldone? _("were playing") : _("are playing"), s1, s2, s3);
58     if (game.skill>SKILL_GOOD && game.thawed && !game.alldone) prout(_("No plaque is allowed."));
59     if (game.tourn) prout(_("This is tournament game %d."), game.tourn);
60     prout(_("Your secret password is \"%s\""),game.passwd);
61     proutn(_("%d of %d Klingons have been killed"), KLINGKILLED, INKLINGTOT);
62     if (NKILLC) prout(_(", including %d Commander%s."), NKILLC, NKILLC==1?"":_("s"));
63     else if (NKILLK + NKILLSC > 0) prout(_(", but no Commanders."));
64     else prout(".");
65     if (game.skill > SKILL_FAIR) prout(_("The Super Commander has %sbeen destroyed."),
66                                   game.state.nscrem?_("not "):"");
67     if (game.state.rembase != game.inbase) {
68         proutn(_("There "));
69         if (game.inbase-game.state.rembase==1) proutn(_("has been 1 base"));
70         else {
71             proutn(_("have been %d bases"), game.inbase-game.state.rembase);
72         }
73         prout(_(" destroyed, %d remaining."), game.state.rembase);
74     }
75     else prout(_("There are %d bases."), game.inbase);
76     if (!damaged(DRADIO) || game.condition == docked || game.iseenit) {
77         /* Don't report this if not seen and
78            either the radio is dead or not at base! */
79         attakreport(false);
80         game.iseenit = true;
81     }
82     if (game.casual) prout(_("%d casualt%s suffered so far."),
83                       game.casual, game.casual==1? "y" : "ies");
84     if (game.nhelp) prout(_("There were %d call%s for help."),
85                      game.nhelp, game.nhelp==1 ? "" : _("s"));
86     if (game.ship == IHE) {
87         proutn(_("You have "));
88         if (game.nprobes) proutn("%d", game.nprobes);
89         else proutn(_("no"));
90         proutn(_(" deep space probe"));
91         if (game.nprobes!=1) proutn(_("s"));
92         prout(".");
93     }
94     if ((!damaged(DRADIO) || game.condition == docked)
95                 && is_scheduled(FDSPROB)) {
96         if (game.isarmed) 
97             proutn(_("An armed deep space probe is in "));
98         else
99             proutn(_("A deep space probe is in "));
100         proutn(cramlc(quadrant, game.probec));
101         prout(".");
102     }
103     if (game.icrystl) {
104         if (game.cryprob <= .05)
105             prout(_("Dilithium crystals aboard ship... not yet used."));
106         else {
107             int i=0;
108             double ai = 0.05;
109             while (game.cryprob > ai) {
110                 ai *= 2.0;
111                 i++;
112             }
113             prout(_("Dilithium crystals have been used %d time%s."),
114                   i, i==1? "" : _("s"));
115         }
116     }
117     skip(1);
118 }
119         
120 void lrscan(void) 
121 /* long-range sensor scan */
122 {
123     int x, y;
124     if (damaged(DLRSENS)) {
125         /* Now allow base's sensors if docked */
126         if (game.condition != docked) {
127             prout(_("LONG-RANGE SENSORS DAMAGED."));
128             return;
129         }
130         prout(_("Starbase's long-range scan"));
131     }
132     else {
133         prout(_("Long-range scan"));
134     }
135     for (x = game.quadrant.x-1; x <= game.quadrant.x+1; x++) {
136         proutn(" ");
137         for (y = game.quadrant.y-1; y <= game.quadrant.y+1; y++) {
138             if (!VALID_QUADRANT(x, y))
139                 proutn("  -1");
140             else {
141                 if (!damaged(DRADIO))
142                     game.state.galaxy[x][y].charted = true;
143                 game.state.chart[x][y].klingons = game.state.galaxy[x][y].klingons;
144                 game.state.chart[x][y].starbase = game.state.galaxy[x][y].starbase;
145                 game.state.chart[x][y].stars = game.state.galaxy[x][y].stars;
146                 if (game.state.galaxy[x][y].supernova) 
147                     proutn(" ***");
148                 else
149                     proutn(" %3d", game.state.chart[x][y].klingons*100 + game.state.chart[x][y].starbase * 10 + game.state.chart[x][y].stars);
150             }
151         }
152         prout(" ");
153     }
154 }
155
156 void dreprt(void) 
157 /* damage report */
158 {
159     bool jdam = false;
160     int i;
161     chew();
162
163     for (i = 0; i < NDEVICES; i++) {
164         if (damaged(i)) {
165             if (!jdam) {
166                 prout(_("\tDEVICE\t\t\t-REPAIR TIMES-"));
167                 prout(_("\t\t\tIN FLIGHT\t\tDOCKED"));
168                 jdam = true;
169             }
170             prout("  %-26s\t%8.2f\t\t%8.2f", 
171                   device[i],
172                   game.damage[i]+0.05,
173                   game.docfac*game.damage[i]+0.005);
174         }
175     }
176     if (!jdam) prout(_("All devices functional."));
177 }
178
179 void rechart(void)
180 /* update the chart in the Enterprise's computer from galaxy data */
181 {
182     int i, j;
183     game.lastchart = game.state.date;
184     for_quadrants(i)
185         for_quadrants(j) 
186             if (game.state.galaxy[i][j].charted) {
187                 game.state.chart[i][j].klingons = game.state.galaxy[i][j].klingons;
188                 game.state.chart[i][j].starbase = game.state.galaxy[i][j].starbase;
189                 game.state.chart[i][j].stars = game.state.galaxy[i][j].stars;
190             }
191 }
192
193 void chart(void)
194 /* display the star chart */ 
195 {
196     int i,j;
197     chew();
198
199     if (!damaged(DRADIO))
200         rechart();
201
202     if (game.lastchart < game.state.date && game.condition == docked) {
203         prout(_("Spock-  \"I revised the Star Chart from the starbase's records.\""));
204         rechart();
205     }
206
207     prout(_("       STAR CHART FOR THE KNOWN GALAXY"));
208     if (game.state.date > game.lastchart)
209         prout(_("(Last surveillance update %d stardates ago)."),
210               (int)(game.state.date-game.lastchart));
211     prout("      1    2    3    4    5    6    7    8");
212     for_quadrants(i) {
213         proutn("%d |", i);
214         for_quadrants(j) {
215             char buf[4];
216             if ((game.options & OPTION_SHOWME) && i == game.quadrant.x && j == game.quadrant.y)
217                 proutn("<");
218             else
219                 proutn(" ");
220             if (game.state.galaxy[i][j].supernova)
221                 strcpy(buf, "***");
222             else if (!game.state.galaxy[i][j].charted && game.state.galaxy[i][j].starbase)
223                 strcpy(buf, ".1.");
224             else if (game.state.galaxy[i][j].charted)
225                 sprintf(buf, "%3d", game.state.chart[i][j].klingons*100 + game.state.chart[i][j].starbase * 10 + game.state.chart[i][j].stars);
226             else
227                 strcpy(buf, "...");
228             proutn(buf);
229             if ((game.options & OPTION_SHOWME) && i == game.quadrant.x && j == game.quadrant.y)
230                 proutn(">");
231             else
232                 proutn(" ");
233         }
234         proutn("  |");
235         if (i<GALSIZE) skip(1);
236     }
237 }
238
239 static void sectscan(int goodScan, int i, int j) 
240 /* light up an individual dot in a sector */
241 {
242     if (goodScan || (abs(i-game.sector.x)<= 1 && abs(j-game.sector.y) <= 1)){
243         if ((game.quad[i][j]==IHMATER0)||(game.quad[i][j]==IHMATER1)||(game.quad[i][j]==IHMATER2)||(game.quad[i][j]==IHE)||(game.quad[i][j]==IHF)){
244             switch (game.condition) {
245             case red: textcolor(RED); break;
246             case green: textcolor(GREEN); break;
247             case yellow: textcolor(YELLOW); break;
248             case docked: textcolor(CYAN); break;
249             case dead: textcolor(BROWN);
250             }
251             if (game.quad[i][j] != game.ship) 
252                 highvideo();
253         }
254         proutn("%c ",game.quad[i][j]);
255         textcolor(DEFAULT);
256     }
257     else
258         proutn("- ");
259 }
260
261 void status(int req)
262 /* print status report lines */
263 {
264 #define RQ(n, a) if (!req || req == n) do { a } while(0)
265     char *cp = NULL, s[256];
266     int t, dam = 0;
267
268     RQ(1,
269         prstat(_("Stardate"), _("%.1f, Time Left %.2f"), game.state.date, game.state.remtime);
270     );
271
272     RQ(2,
273         if (game.condition != docked) newcnd();
274         switch (game.condition) {
275             case red: cp = _("RED"); break;
276             case green: cp = _("GREEN"); break;
277             case yellow: cp = _("YELLOW"); break;
278             case docked: cp = _("DOCKED"); break;
279             case dead: cp = _("DEAD"); break;
280         }
281         for (t=0;t<NDEVICES;t++)
282             if (game.damage[t]>0) 
283                 dam++;
284         prstat(_("Condition"), _("%s, %i DAMAGES"), cp, dam);
285     );
286
287     RQ(3,
288         prstat(_("Position"), "%d - %d , %d - %d",
289               game.quadrant.x, game.quadrant.y, game.sector.x, game.sector.y);
290     );
291
292     RQ(4,
293         if (damaged(DLIFSUP)) {
294             if (game.condition == docked)
295                 sprintf(s, _("DAMAGED, Base provides"));
296             else
297                 sprintf(s, _("DAMAGED, reserves=%4.2f"), game.lsupres);
298         }
299         else
300             sprintf(s, _("ACTIVE"));
301         prstat(_("Life Support"), s);
302     );
303
304     RQ(5,
305         prstat(_("Warp Factor"), "%.1f", game.warpfac);
306     );
307
308     RQ(6,
309         prstat(_("Energy"), "%.2f%s", game.energy,
310                 (game.icrystl && (game.options & OPTION_SHOWME)) ? /* ESR */
311                 _(" (have crystals)") : "");
312     );
313
314     RQ(7,
315         prstat(_("Torpedoes"), "%d", game.torps);
316     );
317
318     RQ(8,
319         if (damaged(DSHIELD))
320             strcpy(s, _("DAMAGED,"));
321         else if (game.shldup)
322             strcpy(s, _("UP,"));
323         else
324             strcpy(s, _("DOWN,"));
325         sprintf(s + strlen(s), _(" %d%% %.1f units"),
326                (int)((100.0*game.shield)/game.inshld + 0.5), game.shield);
327         prstat(_("Shields"), s);
328     );
329
330     RQ(9,
331         prstat(_("Klingons Left"), "%d", KLINGREM);
332     );
333
334     RQ(10,
335         if (game.options & OPTION_WORLDS) {
336             int plnet = game.state.galaxy[game.quadrant.x][game.quadrant.y].planet;
337             if (plnet != NOPLANET && game.state.plnets[plnet].inhabited != UNINHABITED)
338                 prstat(_("Major system"), "%s", systnames[plnet]);
339             else
340                 prout(_("Sector is uninhabited"));
341         }
342     );
343
344     RQ(11,
345         attakreport(!req);
346     );
347
348 #undef RQ
349 }
350
351 void request(void)
352 {
353     int req;
354     static char requests[][3] =
355         {"da","co","po","ls","wa","en","to","sh","kl","sy", "ti"};
356
357     while (scan() == IHEOL)
358         proutn(_("Information desired? "));
359     chew();
360     for (req = 0; req < ARRAY_SIZE(requests); req++)
361         if (strncmp(citem, requests[req], min(2,strlen(citem)))==0)
362             break;
363     if (req >= ARRAY_SIZE(requests)) {
364         prout(_("UNRECOGNIZED REQUEST. Legal requests are:"));
365         prout(("  date, condition, position, lsupport, warpfactor,"));
366         prout(("  energy, torpedoes, shields, klingons, system, time."));
367         return;
368     }
369     status(req + 1);
370 }
371                 
372 void srscan(void)
373 /* short-range scan */
374 {
375     int i, j;
376     int goodScan=true;
377     if (damaged(DSRSENS)) {
378         /* Allow base's sensors if docked */
379         if (game.condition != docked) {
380             prout(_("   S.R. SENSORS DAMAGED!"));
381             goodScan=false;
382         }
383         else
384             prout(_("  [Using Base's sensors]"));
385     }
386     else prout(_("     Short-range scan"));
387     if (goodScan && !damaged(DRADIO)) { 
388         game.state.chart[game.quadrant.x][game.quadrant.y].klingons = game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons;
389         game.state.chart[game.quadrant.x][game.quadrant.y].starbase = game.state.galaxy[game.quadrant.x][game.quadrant.y].starbase;
390         game.state.chart[game.quadrant.x][game.quadrant.y].stars = game.state.galaxy[game.quadrant.x][game.quadrant.y].stars;
391         game.state.galaxy[game.quadrant.x][game.quadrant.y].charted = true;
392     }
393     prout("    1 2 3 4 5 6 7 8 9 10");
394     if (game.condition != docked) newcnd();
395     for (i = 1; i <= QUADSIZE; i++) {
396         proutn("%2d  ", i);
397         for_sectors(j) {
398             sectscan(goodScan, i, j);
399         }
400         skip(1);
401     }
402 }
403                         
404                         
405 void eta(void)
406 /* use computer to get estimated time of arrival for a warp jump */
407 {
408     coord w1, w2;
409     bool wfl, prompt = false;
410     double ttime, twarp, tpower;
411     if (damaged(DCOMPTR)) {
412         prout(_("COMPUTER DAMAGED, USE A POCKET CALCULATOR."));
413         skip(1);
414         return;
415     }
416     if (scan() != IHREAL) {
417         prompt = true;
418         chew();
419         proutn(_("Destination quadrant and/or sector? "));
420         if (scan()!=IHREAL) {
421             huh();
422             return;
423         }
424     }
425     w1.y = aaitem +0.5;
426     if (scan() != IHREAL) {
427         huh();
428         return;
429     }
430     w1.x = aaitem + 0.5;
431     if (scan() == IHREAL) {
432         w2.y = aaitem + 0.5;
433         if (scan() != IHREAL) {
434             huh();
435             return;
436         }
437         w2.x = aaitem + 0.5;
438     }
439     else {
440         if (game.quadrant.y>w1.x) w2.x = 1;
441         else w2.x=QUADSIZE;
442         if (game.quadrant.x>w1.y) w2.y = 1;
443         else w2.y=QUADSIZE;
444     }
445
446     if (!VALID_QUADRANT(w1.x, w1.y) || !VALID_SECTOR(w2.x, w2.y)) {
447         huh();
448         return;
449     }
450     game.dist = sqrt(square(w1.y-game.quadrant.y+0.1*(w2.y-game.sector.y))+
451                 square(w1.x-game.quadrant.x+0.1*(w2.x-game.sector.x)));
452     wfl = false;
453
454     if (prompt) prout(_("Answer \"no\" if you don't know the value:"));
455     for (;;) {
456         chew();
457         proutn(_("Time or arrival date? "));
458         if (scan()==IHREAL) {
459             ttime = aaitem;
460             if (ttime > game.state.date) ttime -= game.state.date; // Actually a star date
461             if (ttime <= 1e-10 ||
462                 (twarp=(floor(sqrt((10.0*game.dist)/ttime)*10.0)+1.0)/10.0) > 10) {
463                 prout(_("We'll never make it, sir."));
464                 chew();
465                 return;
466             }
467             if (twarp < 1.0) twarp = 1.0;
468             break;
469         }
470         chew();
471         proutn(_("Warp factor? "));
472         if (scan()== IHREAL) {
473             wfl = true;
474             twarp = aaitem;
475             if (twarp<1.0 || twarp > 10.0) {
476                 huh();
477                 return;
478             }
479             break;
480         }
481         prout(_("Captain, certainly you can give me one of these."));
482     }
483     for (;;) {
484         chew();
485         ttime = (10.0*game.dist)/square(twarp);
486         tpower = game.dist*twarp*twarp*twarp*(game.shldup+1);
487         if (tpower >= game.energy) {
488             prout(_("Insufficient energy, sir."));
489             if (!game.shldup || tpower > game.energy*2.0) {
490                 if (!wfl) return;
491                 proutn(_("New warp factor to try? "));
492                 if (scan() == IHREAL) {
493                     wfl = true;
494                     twarp = aaitem;
495                     if (twarp<1.0 || twarp > 10.0) {
496                         huh();
497                         return;
498                     }
499                     continue;
500                 }
501                 else {
502                     chew();
503                     skip(1);
504                     return;
505                 }
506             }
507             prout(_("But if you lower your shields,"));
508             proutn(_("remaining"));
509             tpower /= 2;
510         }
511         else
512             proutn(_("Remaining"));
513         prout(_(" energy will be %.2f."), game.energy-tpower);
514         if (wfl) {
515             prout(_("And we will arrive at stardate %.2f."),
516                   game.state.date+ttime);
517         }
518         else if (twarp==1.0)
519             prout(_("Any warp speed is adequate."));
520         else {
521             prout(_("Minimum warp needed is %.2f,"), twarp);
522             prout(_("and we will arrive at stardate %.2f."),
523                   game.state.date+ttime);
524         }
525         if (game.state.remtime < ttime)
526             prout(_("Unfortunately, the Federation will be destroyed by then."));
527         if (twarp > 6.0)
528             prout(_("You'll be taking risks at that speed, Captain"));
529         if ((game.isatb==1 && same(game.state.kscmdr, w1) &&
530              scheduled(FSCDBAS)< ttime+game.state.date)||
531             (scheduled(FCDBAS)<ttime+game.state.date && same(game.battle, w1)))
532             prout(_("The starbase there will be destroyed by then."));
533         proutn(_("New warp factor to try? "));
534         if (scan() == IHREAL) {
535             wfl = true;
536             twarp = aaitem;
537             if (twarp<1.0 || twarp > 10.0) {
538                 huh();
539                 return;
540             }
541         }
542         else {
543             chew();
544             skip(1);
545             return;
546         }
547     }
548                         
549 }
550
551 #if BSD_BUG_FOR_BUG
552 /*
553  *      A visual scan is made in a particular direction of three sectors
554  *      in the general direction specified.  This takes time, and
555  *      Klingons can attack you, so it should be done only when sensors
556  *      are out.  Code swiped from BSD-Trek.  Not presently used, as we
557  *      automatically display all adjacent sectors on the short-range
558  *      scan even when short-range sensors are out.
559  */
560
561 /* This struct[] has the delta x, delta y for particular directions */
562 coord visdelta[] =
563 {
564     {-1,-1},
565     {-1, 0},
566     {-1, 1},
567     {0,  1},
568     {1,  1},
569     {1,  0},
570     {1, -1},
571     {0, -1},
572     {-1,-1},
573     {-1, 0},
574     {-1, 1},
575 };
576
577 void visual(void)
578 {
579     int         co, ix, iy;
580     coord       *v;
581
582     if (scan() != IHREAL) {
583         chew();
584         proutn(_("Direction? "));
585         if (scan()!=IHREAL) {
586             huh();
587             return;
588         }
589     }
590     if (aaitem < 0.0 || aaitem > 360.0)
591         return;
592     co = (aaitem + 22) / 45;
593     v = &visdelta[co];
594     ix = game.sector.x + v->x;
595     iy = game.sector.y + v->y;
596     if (ix < 0 || ix >= QUADSIZE || iy < 0 || iy >= QUADSIZE)
597         co = '?';
598     else
599         co = game.quad[ix][iy];
600     printf("%d,%d %c ", ix, iy, co);
601     v++;
602     ix = game.sector.x + v->x;
603     iy = game.sector.y + v->y;
604     if (ix < 0 || ix >= QUADSIZE || iy < 0 || iy >= QUADSIZE)
605         co = '?';
606     else
607         co = game.quad[ix][iy];
608     printf("%c ", co);
609     v++;
610     ix = game.sector.x + v->x;
611     iy = game.sector.y + v->y;
612     if (ix < 0 || ix >= QUADSIZE || iy < 0 || iy >= QUADSIZE)
613         co = '?';
614     else
615         co = game.quad[ix][iy];
616     printf("%c %d,%d\n", co, ix, iy);
617     game.optime = 0.5;
618     game.ididit = true;
619 }
620 #endif