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