config: Make product work independently configurable
[empserver] / src / lib / subs / show.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2021, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  show.c: General show routines
28  *
29  *  Known contributors to this file:
30  *     Julian Onions, 1988
31  *     Jeff Bailey, 1990
32  *     Steve McClure, 1996
33  *     Ron Koenderink, 2005-2009
34  *     Markus Armbruster, 2006-2021
35  */
36
37 #include <config.h>
38
39 #include <math.h>
40 #include "game.h"
41 #include "item.h"
42 #include "land.h"
43 #include "nat.h"
44 #include "news.h"
45 #include "nuke.h"
46 #include "optlist.h"
47 #include "plane.h"
48 #include "player.h"
49 #include "product.h"
50 #include "prototypes.h"
51 #include "sect.h"
52 #include "server.h"
53 #include "ship.h"
54
55 static char *fmttime2822(time_t);
56 static void show_load(short[]);
57 static void show_capab(int, struct symbol *);
58
59 struct chr_index {
60     int type;
61     int tech;
62 };
63
64 static int
65 chr_index_cmp(const void *a, const void *b)
66 {
67     const struct chr_index *ca = a, *cb = b;
68     if (ca->tech == cb->tech)
69         return ca->type - cb->type;
70     return ca->tech - cb->tech;
71 }
72
73 static int
74 make_mchr_index(struct chr_index chridx[], int tlev)
75 {
76     struct natstr *natp = getnatp(player->cnum);
77     int i, n;
78
79     n = 0;
80     for (i = 0; mchr[i].m_name; i++) {
81         if (!mchr[i].m_name[0])
82             continue;
83         if (mchr[i].m_tech > tlev)
84             continue;
85         chridx[n].type = i;
86         chridx[n].tech = mchr[i].m_tech;
87         n++;
88     }
89     if (natp->nat_flags & NF_TECHLISTS)
90         qsort(chridx, n, sizeof(*chridx), chr_index_cmp);
91     return n;
92 }
93
94 static int
95 make_plchr_index(struct chr_index chridx[], int tlev)
96 {
97     struct natstr *natp = getnatp(player->cnum);
98     int i, n;
99
100     n = 0;
101     for (i = 0; plchr[i].pl_name; i++) {
102         if (!plchr[i].pl_name[0])
103             continue;
104         if (plchr[i].pl_tech > tlev)
105             continue;
106         chridx[n].type = i;
107         chridx[n].tech = plchr[i].pl_tech;
108         n++;
109     }
110     if (natp->nat_flags & NF_TECHLISTS)
111         qsort(chridx, n, sizeof(*chridx), chr_index_cmp);
112     return n;
113 }
114
115 static int
116 make_lchr_index(struct chr_index chridx[], int tlev)
117 {
118     struct natstr *natp = getnatp(player->cnum);
119     int i, n;
120
121     n = 0;
122     for (i = 0; lchr[i].l_name; i++) {
123         if (!lchr[i].l_name[0])
124             continue;
125         if (lchr[i].l_tech > tlev)
126             continue;
127         chridx[n].type = i;
128         chridx[n].tech = lchr[i].l_tech;
129         n++;
130     }
131     if (natp->nat_flags & NF_TECHLISTS)
132         qsort(chridx, n, sizeof(*chridx), chr_index_cmp);
133     return n;
134 }
135
136 static int
137 make_nchr_index(struct chr_index chridx[], int tlev)
138 {
139     struct natstr *natp = getnatp(player->cnum);
140     int i, n;
141
142     n = 0;
143     for (i = 0; nchr[i].n_name; i++) {
144         if (!nchr[i].n_name[0])
145             continue;
146         if (nchr[i].n_tech > tlev)
147             continue;
148         chridx[n].type = i;
149         chridx[n].tech = nchr[i].n_tech;
150         n++;
151     }
152     if (natp->nat_flags & NF_TECHLISTS)
153         qsort(chridx, n, sizeof(*chridx), chr_index_cmp);
154     return n;
155 }
156
157 void
158 show_bridge(int tlev)
159 {
160     if (tlev < buil_bt)
161         return;
162     pr("Bridges require %g tech, %d hcm, 0 workers,\n",
163        buil_bt, buil_bh);
164     pr("%d available workforce, and cost $%g\n",
165        (SCT_BLD_WORK(0, buil_bh) * SCT_MINEFF + 99) / 100,
166        buil_bc);
167 }
168
169 void
170 show_tower(int tlev)
171 {
172     if (tlev < buil_tower_bt)
173         return;
174     pr("Bridge towers require %g tech, %d hcm, 0 workers,\n",
175        buil_tower_bt, buil_tower_bh);
176     pr("%d available workforce, and cost $%g\n",
177        (SCT_BLD_WORK(0, buil_tower_bh) * SCT_MINEFF + 99) / 100,
178        buil_tower_bc);
179 }
180
181 void
182 show_nuke_stats(int tlev)
183 {
184     show_nuke_capab(tlev);
185 }
186
187 void
188 show_nuke_build(int tlev)
189 {
190     struct chr_index chridx[ARRAY_SIZE(nchr)];
191     int n = make_nchr_index(chridx, tlev);
192     int i;
193     struct nchrstr *np;
194
195     pr("%13s lcm hcm  oil  rad avail tech res $\n", "");
196
197     for (i = 0; i < n; i++) {
198         np = &nchr[chridx[i].type];
199         pr("%-13.13s %3d %3d %4d %4d %5d %4d %3.0f $%6d\n",
200            np->n_name, np->n_mat[I_LCM], np->n_mat[I_HCM],
201            np->n_mat[I_OIL], np->n_mat[I_RAD], np->n_bwork, np->n_tech,
202            drnuke_const > MIN_DRNUKE_CONST ?
203                 ceil(np->n_tech * drnuke_const) : 0.0,
204            np->n_cost);
205     }
206 }
207
208 void
209 show_nuke_capab(int tlev)
210 {
211     struct chr_index chridx[ARRAY_SIZE(nchr)];
212     int n = make_nchr_index(chridx, tlev);
213     int i;
214     struct nchrstr *np;
215
216     pr("%13s blst dam lbs tech res $%7s abilities\n", "", "");
217     for (i = 0; i < n; i++) {
218         np = &nchr[chridx[i].type];
219         pr("%-13.13s %4d %3d %3d %4d %3.0f $%7d",
220            np->n_name, np->n_blast, np->n_dam,
221            np->n_weight, np->n_tech,
222            drnuke_const > MIN_DRNUKE_CONST ?
223                 ceil(np->n_tech * drnuke_const) : 0.0,
224            np->n_cost);
225         show_capab(np->n_flags, nuke_chr_flags);
226         pr("\n");
227     }
228 }
229
230 void
231 show_ship_build(int tlev)
232 {
233     struct chr_index chridx[ARRAY_SIZE(mchr)];
234     int n = make_mchr_index(chridx, tlev);
235     int i;
236     struct mchrstr *mp;
237
238     pr("%25s lcm hcm avail tech $\n", "");
239     for (i = 0; i < n; i++) {
240         mp = &mchr[chridx[i].type];
241         pr("%-25.25s %3d %3d %5d %4d $%d\n",
242            mp->m_name, mp->m_mat[I_LCM], mp->m_mat[I_HCM],
243            mp->m_bwork, mp->m_tech, mp->m_cost);
244     }
245 }
246
247 void
248 show_ship_stats(int tlev)
249 {
250     struct chr_index chridx[ARRAY_SIZE(mchr)];
251     int n = make_mchr_index(chridx, tlev);
252     int i;
253     struct mchrstr *mp;
254
255     pr("%25s      s  v  s  r  f  l  p  h  x\n", "");
256     pr("%25s      p  i  p  n  i  n  l  e  p\n", "");
257     pr("%25s def  d  s  y  g  r  d  n  l  l\n", "");
258     for (i = 0; i < n; i++) {
259         mp = &mchr[chridx[i].type];
260         pr("%-25.25s %3d %2d %2d %2d %2d %2d %2d %2d %2d %2d\n",
261            mp->m_name, m_armor(mp, tlev), m_speed(mp, tlev),
262            m_visib(mp, tlev), mp->m_vrnge,
263            m_frnge(mp, tlev), m_glim(mp, tlev),
264            mp->m_nland, mp->m_nplanes, mp->m_nchoppers, mp->m_nxlight);
265     }
266 }
267
268 void
269 show_ship_capab(int tlev)
270 {
271     struct chr_index chridx[ARRAY_SIZE(mchr)];
272     int n = make_mchr_index(chridx, tlev);
273     int i;
274     struct mchrstr *mp;
275
276     pr("%25s cargoes & capabilities\n", "");
277     for (i = 0; i < n; i++) {
278         mp = &mchr[chridx[i].type];
279         pr("%-25.25s ", mp->m_name);
280         show_load(mp->m_item);
281         show_capab(mp->m_flags, ship_chr_flags);
282         pr("\n");
283     }
284 }
285
286 void
287 show_plane_stats(int tlev)
288 {
289     struct chr_index chridx[ARRAY_SIZE(plchr)];
290     int n = make_plchr_index(chridx, tlev);
291     int i;
292     struct plchrstr *pp;
293
294     pr("%25s acc load att def ran fuel stlth\n", "");
295     for (i = 0; i < n; i++) {
296         pp = &plchr[chridx[i].type];
297         pr("%-25.25s %3d %4d %3d %3d %3d %4d %4d%%\n",
298            pp->pl_name, pl_acc(pp, tlev), pl_load(pp, tlev),
299            pl_att(pp, tlev), pl_def(pp, tlev), pl_range(pp, tlev),
300            pp->pl_fuel, pp->pl_stealth);
301     }
302 }
303
304 void
305 show_plane_capab(int tlev)
306 {
307     struct chr_index chridx[ARRAY_SIZE(plchr)];
308     int n = make_plchr_index(chridx, tlev);
309     int i;
310     struct plchrstr *pp;
311
312     pr("%25s capabilities\n", "");
313     for (i = 0; i < n; i++) {
314         pp = &plchr[chridx[i].type];
315         pr("%-25.25s ", pp->pl_name);
316         show_capab(pp->pl_flags, plane_chr_flags);
317         pr("\n");
318     }
319 }
320
321 void
322 show_plane_build(int tlev)
323 {
324     struct chr_index chridx[ARRAY_SIZE(plchr)];
325     int n = make_plchr_index(chridx, tlev);
326     int i;
327     struct plchrstr *pp;
328
329     pr("%25s lcm hcm crew avail tech $\n", "");
330     for (i = 0; i < n; i++) {
331         pp = &plchr[chridx[i].type];
332         pr("%-25.25s %3d %3d %4d %5d %4d $%d\n",
333            pp->pl_name, pp->pl_mat[I_LCM],
334            pp->pl_mat[I_HCM], pp->pl_mat[I_MILIT],
335            pp->pl_bwork, pp->pl_tech, pp->pl_cost);
336     }
337 }
338
339 void
340 show_land_build(int tlev)
341 {
342     struct chr_index chridx[ARRAY_SIZE(lchr)];
343     int n = make_lchr_index(chridx, tlev);
344     int i;
345     struct lchrstr *lp;
346
347     pr("%25s lcm hcm guns avail tech $\n", "");
348     for (i = 0; i < n; i++) {
349         lp = &lchr[chridx[i].type];
350         pr("%-25.25s %3d %3d %4d %5d %4d $%d\n",
351            lp->l_name, lp->l_mat[I_LCM],
352            lp->l_mat[I_HCM],
353            lp->l_mat[I_GUN],
354            lp->l_bwork, lp->l_tech, lp->l_cost);
355     }
356 }
357
358 void
359 show_land_capab(int tlev)
360 {
361     struct chr_index chridx[ARRAY_SIZE(lchr)];
362     int n = make_lchr_index(chridx, tlev);
363     int i;
364     struct lchrstr *lcp;
365
366     pr("%25s capabilities\n", "");
367     for (i = 0; i < n; i++) {
368         lcp = &lchr[chridx[i].type];
369         pr("%-25s ", lcp->l_name);
370         show_load(lcp->l_item);
371         show_capab(lcp->l_flags, land_chr_flags);
372         pr("\n");
373     }
374 }
375
376 void
377 show_land_stats(int tlev)
378 {
379     struct chr_index chridx[ARRAY_SIZE(lchr)];
380     int n = make_lchr_index(chridx, tlev);
381     int i;
382     struct lchrstr *lcp;
383
384     pr("%25s              s  v  s  r  r  a  f  a  a  x  l\n", "");
385     pr("%25s              p  i  p  a  n  c  i  m  a  p  n\n", "");
386     pr("%25s att def vul  d  s  y  d  g  c  r  m  f  l  d\n", "");
387     for (i = 0; i < n; i++) {
388         lcp = &lchr[chridx[i].type];
389         pr("%-25s %1.1f %1.1f %3d %2d %2d %2d %2d %2d %2d %2d %2d %2d"
390            " %2d %2d\n",
391            lcp->l_name,
392            l_att(lcp, tlev), l_def(lcp, tlev), l_vul(lcp, tlev),
393            l_spd(lcp, tlev), lcp->l_vis, lcp->l_spy, lcp->l_rad,
394            l_frg(lcp, tlev), l_acc(lcp, tlev), l_dam(lcp, tlev),
395            lcp->l_ammo, lcp->l_aaf, lcp->l_nxlight, lcp->l_nland);
396     }
397 }
398
399 void
400 show_sect_build(int foo)
401 {
402     int i, first;
403
404     pr("                         build 100%% efficiency  maint\n"
405        "sector type               lcm  hcm avail     $      $\n");
406     for (i = 0; dchr[i].d_name; i++) {
407         if (dchr[i].d_mnem == 0)
408             continue;
409         if (dchr[i].d_mob0 < 0)
410             continue;
411         if (dchr[i].d_cost == 100
412             && !dchr[i].d_mat[I_LCM] && !dchr[i].d_mat[I_HCM]
413             && dchr[i].d_maint == 0)
414             continue;           /* the usual, skip */
415         pr("%c %-21.21s  %4d %4d %5d %5d  %5d\n",
416            dchr[i].d_mnem, dchr[i].d_name,
417            dchr[i].d_mat[I_LCM], dchr[i].d_mat[I_HCM],
418            dchr[i].d_bwork, dchr[i].d_cost,
419            dchr[i].d_maint * etu_per_update);
420     }
421     pr("any other                   0    0   100   100      0\n");
422
423     first = 1;
424     for (i = 0; intrchr[i].in_name; i++) {
425         if (!intrchr[i].in_enable)
426             continue;
427         if (first)
428             pr("\n                         build 100%% efficiency\n"
429                "infrastructure type       lcm  hcm mobil     $\n");
430         pr("%-23.23s  %4d %4d %5d %5d\n",
431            intrchr[i].in_name,
432            intrchr[i].in_mat[I_LCM], intrchr[i].in_mat[I_HCM],
433            intrchr[i].in_bmobil, intrchr[i].in_cost);
434         first = 0;
435     }
436 }
437
438 void
439 show_sect_stats(int foo)
440 {
441     int i;
442     struct natstr *natp = getnatp(player->cnum);
443
444     pr("                        mob cost   max   max   naviga    packing   max\n");
445     pr("  sector type            0%% 100%%   off   def   bility      bonus   pop\n");
446
447     for (i = 0; dchr[i].d_name; i++) {
448         if (dchr[i].d_mnem == 0)
449             continue;
450         pr("%c %-21.21s", dchr[i].d_mnem, dchr[i].d_name);
451         if (dchr[i].d_mob0 < 0)
452             pr("  no way ");
453         else
454             pr(" %3.1f  %3.1f", dchr[i].d_mob0, dchr[i].d_mob1);
455         pr("  %5.2f %5.2f %7.7s %10.10s %5d\n",
456            dchr[i].d_ostr, dchr[i].d_dstr,
457            symbol_by_value(dchr[i].d_nav, sector_navigation),
458            symbol_by_value(dchr[i].d_pkg, packing),
459            max_population(natp->nat_level[NAT_RLEV], i, 100));
460     }
461 }
462
463 void
464 show_sect_capab(int foo)
465 {
466     int i;
467
468     pr("  sector type             product  p.e.  capabilities\n");
469
470     for (i = 0; dchr[i].d_name; i++) {
471         if (dchr[i].d_mnem == 0 || (dchr[i].d_prd < 0 && !dchr[i].d_flags))
472             continue;
473         pr("%c %-23s ",
474            dchr[i].d_mnem, dchr[i].d_name);
475         if (dchr[i].d_prd >= 0)
476             pr("%-7s %4d%% ",
477                pchr[dchr[i].d_prd].p_sname, dchr[i].d_peffic);
478         else
479             pr("              ");
480         show_capab(dchr[i].d_flags, sect_chr_flags);
481         pr("\n");
482     }
483 }
484
485 void
486 show_item(int tlev)
487 {
488     struct ichrstr *ip;
489
490     pr("item power sell lbs    packing     melt  item\n");
491     pr("mnem                in no wh ur bk deno  name\n");
492
493     for (ip = ichr; ip->i_name; ip++) {
494         pr("  %c  %5d %4s %3d %2d %2d %2d %2d %2d %4d  %s\n",
495            ip->i_mnem, ip->i_power,
496            ip->i_sell ? "yes" : "no",
497            ip->i_lbs,
498            ip->i_pkg[IPKG], ip->i_pkg[NPKG], ip->i_pkg[WPKG],
499            ip->i_pkg[UPKG], ip->i_pkg[BPKG],
500            ip->i_melt_denom, ip->i_name);
501     }
502 }
503
504 void
505 show_product(int tlev)
506 {
507     struct pchrstr *pp;
508     int i;
509     char *lev;
510
511     pr("product    cost  raw materials  avail   reso dep  level p.e.\n");
512
513     for (pp = pchr; pp->p_sname; pp++) {
514         if (!pp->p_sname[0])
515             continue;
516         pr("%7.7s %c  $%-3d ",
517            pp->p_sname,
518            pp->p_type < 0 ? ' ' : ichr[pp->p_type].i_mnem,
519            pp->p_cost);
520         BUILD_ASSERT(MAXPRCON <= 3); /* output has only three columns */
521         for (i = 0; i < 3; i++) {
522             if (i < MAXPRCON && pp->p_camt[i]
523                 && pp->p_ctype[i] > I_NONE && pp->p_ctype[i] <= I_MAX)
524                 pr(" %2d%c", pp->p_camt[i], ichr[pp->p_ctype[i]].i_mnem);
525             else
526                 pr("    ");
527         }
528         pr(" %8d", pp->p_bwork);
529         if (pp->p_nrndx)
530             pr("  %5.5s %3d  ",
531                symbol_by_value(pp->p_nrndx, resources), pp->p_nrdep);
532         else
533             pr("             ");
534         if (pp->p_nlndx < 0)
535             pr("1.0\n");
536         else {
537             lev = symbol_by_value(pp->p_nlndx, level);
538             pr("(%.4s%+d)/(%.4s%+d)\n",
539                lev, -pp->p_nlmin, lev, pp->p_nllag - pp->p_nlmin);
540         }
541     }
542 }
543
544 void
545 show_news(int tlev)
546 {
547     int i, j;
548
549     pr("id category           good will\n");
550     pr("    messages\n");
551
552     for (i = 1; i < N_MAX_VERB + 1; i++) {
553         if (rpt[i].r_newspage == N_NOTUSED)
554             continue;
555         pr("%-2d %-20.20s %4d\n", rpt[i].r_uid,
556            page_headings[rpt[i].r_newspage].name, rpt[i].r_good_will);
557         for (j = 0; j < NUM_RPTS; j++)
558             pr("    %s\n", rpt[i].r_newstory[j]);
559     }
560 }
561
562 /*
563  * Show update policy and up to @n scheduled updates.
564  */
565 void
566 show_updates(int n)
567 {
568     struct gamestr *game = game_tick_tick();
569     int demand = 0;
570     int i;
571
572     pr("%s, Turn %d, ETU %d\n", fmttime2822(time(NULL)),
573        game->game_turn, game->game_tick);
574
575     if (update_time[0]) {
576         if (update_demand == UPD_DEMAND_SCHED) {
577             pr("Demand updates occur according to schedule:\n");
578             demand = 1;
579         } else
580             pr("Updates occur according to schedule:\n");
581         for (i = 0; i < n && update_time[i]; i++)
582             pr("%3d.  %s\n", game->game_turn + i,
583                fmttime2822(update_time[i]));
584         if (update_window) {
585             pr("Updates occur within %d seconds after the scheduled time\n",
586                update_window);
587         }
588     } else
589         pr("There are no updates scheduled.\n");
590
591     if (update_demand == UPD_DEMAND_ASYNC) {
592         pr("Demand updates occur right after the demand is set.\n");
593         if (*update_demandtimes != 0) {
594             pr("Demand updates are allowed during: %s\n",
595                update_demandtimes);
596         }
597         demand = 1;
598     }
599
600     if (demand) {
601         pr("Demand updates require %d country(s) to want one.\n",
602            update_wantmin);
603     }
604
605     if (updates_disabled())
606         pr("\nUPDATES ARE DISABLED!\n");
607 }
608
609 /*
610  * Return @t formatted according to RFC 2822.
611  * The return value is statically allocated and overwritten on
612  * subsequent calls.
613  */
614 static char *
615 fmttime2822(time_t t)
616 {
617     static char buf[32];
618 #if defined(_WIN32)
619     size_t n;
620     int nn;
621     TIME_ZONE_INFORMATION tzi;
622     long time_offset;
623     struct tm *time;
624
625     time = localtime(&t);
626
627     n = strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", time);
628     if (CANT_HAPPEN(n == 0)) {
629         buf[0] = 0;
630         return buf;
631     }
632     GetTimeZoneInformation(&tzi);
633     time_offset = -(tzi.Bias +
634         (time->tm_isdst ? tzi.DaylightBias : tzi.StandardBias));
635
636     nn = _snprintf(buf + n, sizeof(buf) - n, " %+03ld%02ld",
637                    time_offset / 60, labs(time_offset) % 60);
638     if (CANT_HAPPEN(nn <= 0 || nn + n >= sizeof(buf)))
639         buf[0] = 0;
640 #else
641     size_t n = strftime(buf, sizeof(buf), "%a, %d %b %Y %T %z",
642                         localtime(&t));
643     if (CANT_HAPPEN(n == 0))
644         buf[0] = 0;
645 #endif
646     return buf;
647 }
648
649 static void
650 show_load(short item[])
651 {
652     i_type i;
653
654     for (i = I_NONE + 1; i <= I_MAX; ++i) {
655         if (item[i])
656             pr(" %d%c", item[i], ichr[i].i_mnem);
657     }
658 }
659
660 static void
661 show_capab(int flags, struct symbol *table)
662 {
663     char buf[1024];
664
665     symbol_set_fmt(buf, sizeof(buf), flags, table, " ", 0);
666     if (*buf)
667         pr(" %s", buf);
668 }