empserver/src/lib/subs/show.c
Markus Armbruster 84d88442b3 Don't misinterpret blank configuration entries as sentinels
Configuration table entries not defined by builtin and custom
configuration files remain blank.  They get misinterpreted as sentinel
in tables that use one.  Affected are tables product, ship-chr,
plane-chr, land-chr and nuke-chr.  Tables item, sect-chr and
infrastructure are immune despite using a sentinel, because omitting
entries is not permitted there.

Code relying on the sentinel fails to pick up entries after the first
blank one.  They don't get set up correctly, they're invisible to
build and show, and not recognized as symbolic selector values (the
frg in ship ?type=frg).  xdump is fine, because it doesn't rely on
sentinels.  It dumps blank entries normally.

The bugs don't bite in the stock game, because the builtin
configuration files are all dense.

The sentinels are all null strings.  Set them to "" in the affected
tables' oninit callback.  Fix up code iterating over the tables to
ignore such entries.  This is precisely the code relying on sentinels,
plus xdump's xdvisible().
2011-06-25 16:51:56 +02:00

675 lines
16 KiB
C

/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2011, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure, Markus Armbruster
*
* Empire is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* ---
*
* See files README, COPYING and CREDITS in the root of the source
* tree for related information and legal notices. It is expected
* that future projects/authors will amend these files as needed.
*
* ---
*
* show.c: General show routines
*
* Known contributors to this file:
* Julian Onions, 1988
* Jeff Bailey, 1990
* Steve McClure, 1996
* Ron Koenderink, 2005-2009
* Markus Armbruster, 2006-2011
*/
#include <config.h>
#include <math.h>
#include "file.h"
#include "game.h"
#include "item.h"
#include "land.h"
#include "nat.h"
#include "news.h"
#include "nuke.h"
#include "optlist.h"
#include "plane.h"
#include "player.h"
#include "product.h"
#include "prototypes.h"
#include "sect.h"
#include "server.h"
#include "ship.h"
static char *fmttime2822(time_t);
static void show_load(short[]);
static void show_capab(int, struct symbol *);
struct chr_index {
int type;
int tech;
};
static int
chr_index_cmp(const void *a, const void *b)
{
const struct chr_index *ca = a, *cb = b;
if (ca->tech == cb->tech)
return ca->type - cb->type;
return ca->tech - cb->tech;
}
static int
make_mchr_index(struct chr_index chridx[], int tlev)
{
struct natstr *natp = getnatp(player->cnum);
int i, n;
n = 0;
for (i = 0; mchr[i].m_name; i++) {
if (!mchr[i].m_name[0])
continue;
if (mchr[i].m_tech > tlev)
continue;
if ((mchr[i].m_flags & M_TRADE) && !opt_TRADESHIPS)
continue;
chridx[n].type = i;
chridx[n].tech = mchr[i].m_tech;
n++;
}
if (natp->nat_flags & NF_TECHLISTS)
qsort(chridx, n, sizeof(*chridx), chr_index_cmp);
return n;
}
static int
make_plchr_index(struct chr_index chridx[], int tlev)
{
struct natstr *natp = getnatp(player->cnum);
int i, n;
n = 0;
for (i = 0; plchr[i].pl_name; i++) {
if (!plchr[i].pl_name[0])
continue;
if (plchr[i].pl_tech > tlev)
continue;
chridx[n].type = i;
chridx[n].tech = plchr[i].pl_tech;
n++;
}
if (natp->nat_flags & NF_TECHLISTS)
qsort(chridx, n, sizeof(*chridx), chr_index_cmp);
return n;
}
static int
make_lchr_index(struct chr_index chridx[], int tlev)
{
struct natstr *natp = getnatp(player->cnum);
int i, n;
n = 0;
for (i = 0; lchr[i].l_name; i++) {
if (!lchr[i].l_name[0])
continue;
if (lchr[i].l_tech > tlev)
continue;
if ((lchr[i].l_flags & L_SPY) && !opt_LANDSPIES)
continue;
chridx[n].type = i;
chridx[n].tech = lchr[i].l_tech;
n++;
}
if (natp->nat_flags & NF_TECHLISTS)
qsort(chridx, n, sizeof(*chridx), chr_index_cmp);
return n;
}
static int
make_nchr_index(struct chr_index chridx[], int tlev)
{
struct natstr *natp = getnatp(player->cnum);
int i, n;
n = 0;
for (i = 0; nchr[i].n_name; i++) {
if (!nchr[i].n_name[0])
continue;
if (nchr[i].n_tech > tlev)
continue;
chridx[n].type = i;
chridx[n].tech = nchr[i].n_tech;
n++;
}
if (natp->nat_flags & NF_TECHLISTS)
qsort(chridx, n, sizeof(*chridx), chr_index_cmp);
return n;
}
void
show_bridge(int tlev)
{
if (tlev < buil_bt)
return;
pr("Bridges require %g tech, %d hcm, 0 workers,\n",
buil_bt, buil_bh);
pr("%d available workforce, and cost $%g\n",
(SCT_BLD_WORK(0, buil_bh) * SCT_MINEFF + 99) / 100,
buil_bc);
}
void
show_tower(int tlev)
{
if (tlev < buil_tower_bt)
return;
pr("Bridge towers require %g tech, %d hcm, 0 workers,\n",
buil_tower_bt, buil_tower_bh);
pr("%d available workforce, and cost $%g\n",
(SCT_BLD_WORK(0, buil_tower_bh) * SCT_MINEFF + 99) / 100,
buil_tower_bc);
}
void
show_nuke_stats(int tlev)
{
show_nuke_capab(tlev);
}
void
show_nuke_build(int tlev)
{
struct chr_index chridx[sizeof(nchr) / sizeof(*nchr)];
int n = make_nchr_index(chridx, tlev);
int i;
struct nchrstr *np;
int avail;
pr("%13s lcm hcm oil rad avail tech res $\n", "");
for (i = 0; i < n; i++) {
np = &nchr[chridx[i].type];
avail = NUK_BLD_WORK(np->n_lcm, np->n_hcm, np->n_oil, np->n_rad);
pr("%-13.13s %3d %3d %4d %4d %5d %4d %3.0f $%6d\n",
np->n_name, np->n_lcm, np->n_hcm, np->n_oil,
np->n_rad, avail, np->n_tech,
drnuke_const > MIN_DRNUKE_CONST ?
ceil(np->n_tech * drnuke_const) : 0.0,
np->n_cost);
}
}
void
show_nuke_capab(int tlev)
{
struct chr_index chridx[sizeof(nchr) / sizeof(*nchr)];
int n = make_nchr_index(chridx, tlev);
int i;
struct nchrstr *np;
pr("%13s blst dam lbs tech res $%7s abilities\n", "", "");
for (i = 0; i < n; i++) {
np = &nchr[chridx[i].type];
pr("%-13.13s %4d %3d %3d %4d %3.0f $%7d",
np->n_name, np->n_blast, np->n_dam,
np->n_weight, np->n_tech,
drnuke_const > MIN_DRNUKE_CONST ?
ceil(np->n_tech * drnuke_const) : 0.0,
np->n_cost);
show_capab(np->n_flags, nuke_chr_flags);
pr("\n");
}
}
void
show_ship_build(int tlev)
{
struct chr_index chridx[sizeof(mchr) / sizeof(*mchr)];
int n = make_mchr_index(chridx, tlev);
int i;
struct mchrstr *mp;
pr("%25s lcm hcm avail tech $\n", "");
for (i = 0; i < n; i++) {
mp = &mchr[chridx[i].type];
pr("%-25.25s %3d %3d %5d %4d $%d\n",
mp->m_name, mp->m_lcm, mp->m_hcm,
SHP_BLD_WORK(mp->m_lcm, mp->m_hcm), mp->m_tech, mp->m_cost);
}
}
void
show_ship_stats(int tlev)
{
struct chr_index chridx[sizeof(mchr) / sizeof(*mchr)];
int n = make_mchr_index(chridx, tlev);
int i;
struct mchrstr *mp;
pr("%25s s v s r f l p h x\n", "");
pr("%25s p i p n i n l e p\n", "");
pr("%25s def d s y g r d n l l\n", "");
for (i = 0; i < n; i++) {
mp = &mchr[chridx[i].type];
pr("%-25.25s %3d %2d %2d %2d %2d %2d %2d %2d %2d %2d\n",
mp->m_name, m_armor(mp, tlev), m_speed(mp, tlev),
m_visib(mp, tlev), mp->m_vrnge,
m_frnge(mp, tlev), m_glim(mp, tlev),
mp->m_nland, mp->m_nplanes, mp->m_nchoppers, mp->m_nxlight);
}
}
void
show_ship_capab(int tlev)
{
struct chr_index chridx[sizeof(mchr) / sizeof(*mchr)];
int n = make_mchr_index(chridx, tlev);
int i;
struct mchrstr *mp;
pr("%25s cargos & capabilities\n", "");
for (i = 0; i < n; i++) {
mp = &mchr[chridx[i].type];
pr("%-25.25s ", mp->m_name);
show_load(mp->m_item);
show_capab(mp->m_flags, ship_chr_flags);
pr("\n");
}
}
void
show_plane_stats(int tlev)
{
struct chr_index chridx[sizeof(plchr) / sizeof(*plchr)];
int n = make_plchr_index(chridx, tlev);
int i;
struct plchrstr *pp;
pr("%25s acc load att def ran fuel stlth\n", "");
for (i = 0; i < n; i++) {
pp = &plchr[chridx[i].type];
pr("%-25.25s %3d %4d %3d %3d %3d %4d %4d%%\n",
pp->pl_name, pl_acc(pp, tlev), pl_load(pp, tlev),
pl_att(pp, tlev), pl_def(pp, tlev), pl_range(pp, tlev),
pp->pl_fuel, pp->pl_stealth);
}
}
void
show_plane_capab(int tlev)
{
struct chr_index chridx[sizeof(plchr) / sizeof(*plchr)];
int n = make_plchr_index(chridx, tlev);
int i;
struct plchrstr *pp;
pr("%25s capabilities\n", "");
for (i = 0; i < n; i++) {
pp = &plchr[chridx[i].type];
pr("%-25.25s ", pp->pl_name);
show_capab(pp->pl_flags, plane_chr_flags);
pr("\n");
}
}
void
show_plane_build(int tlev)
{
struct chr_index chridx[sizeof(plchr) / sizeof(*plchr)];
int n = make_plchr_index(chridx, tlev);
int i;
struct plchrstr *pp;
pr("%25s lcm hcm crew avail tech $\n", "");
for (i = 0; i < n; i++) {
pp = &plchr[chridx[i].type];
pr("%-25.25s %3d %3d %4d %5d %4d $%d\n",
pp->pl_name, pp->pl_lcm,
pp->pl_hcm, pp->pl_crew,
PLN_BLD_WORK(pp->pl_lcm, pp->pl_hcm), pp->pl_tech, pp->pl_cost);
}
}
void
show_land_build(int tlev)
{
struct chr_index chridx[sizeof(lchr) / sizeof(*lchr)];
int n = make_lchr_index(chridx, tlev);
int i;
struct lchrstr *lp;
pr("%25s lcm hcm guns avail tech $\n", "");
for (i = 0; i < n; i++) {
lp = &lchr[chridx[i].type];
pr("%-25.25s %3d %3d %4d %5d %4d $%d\n",
lp->l_name, lp->l_lcm,
lp->l_hcm,
lp->l_gun,
LND_BLD_WORK(lp->l_lcm, lp->l_hcm), lp->l_tech, lp->l_cost);
}
}
void
show_land_capab(int tlev)
{
struct chr_index chridx[sizeof(lchr) / sizeof(*lchr)];
int n = make_lchr_index(chridx, tlev);
int i;
struct lchrstr *lcp;
pr("%25s capabilities\n", "");
for (i = 0; i < n; i++) {
lcp = &lchr[chridx[i].type];
pr("%-25s ", lcp->l_name);
show_load(lcp->l_item);
show_capab(lcp->l_flags, land_chr_flags);
pr("\n");
}
}
void
show_land_stats(int tlev)
{
struct chr_index chridx[sizeof(lchr) / sizeof(*lchr)];
int n = make_lchr_index(chridx, tlev);
int i;
struct lchrstr *lcp;
pr("%25s s v s r r a f a a x l\n", "");
pr("%25s p i p a n c i m a p n\n", "");
pr("%25s att def vul d s y d g c r m f l d\n", "");
for (i = 0; i < n; i++) {
lcp = &lchr[chridx[i].type];
pr("%-25s %1.1f %1.1f %3d %2d %2d %2d %2d %2d %2d %2d %2d %2d"
" %2d %2d\n",
lcp->l_name,
l_att(lcp, tlev), l_def(lcp, tlev), l_vul(lcp, tlev),
l_spd(lcp, tlev), lcp->l_vis, lcp->l_spy, lcp->l_rad,
l_frg(lcp, tlev), l_acc(lcp, tlev), l_dam(lcp, tlev),
lcp->l_ammo, lcp->l_aaf, lcp->l_nxlight, lcp->l_nland);
}
}
void
show_sect_build(int foo)
{
int i, first;
pr(" desig build 100%% eff maint\n"
"sector type $ lcm hcm $ $\n");
for (i = 0; dchr[i].d_name; i++) {
if (dchr[i].d_mnem == 0)
continue;
if (dchr[i].d_mob0 < 0)
continue;
if (dchr[i].d_cost <= 0 && dchr[i].d_build == 1
&& dchr[i].d_lcms == 0 && dchr[i].d_hcms == 0
&& dchr[i].d_maint == 0)
continue; /* the usual, skip */
pr("%c %-21.21s", dchr[i].d_mnem, dchr[i].d_name);
if (dchr[i].d_cost < 0)
pr(" can't");
else
pr(" %5d", dchr[i].d_cost);
pr(" %5d%5d %5d %5d\n",
100 * dchr[i].d_lcms,
100 * dchr[i].d_hcms,
100 * dchr[i].d_build,
dchr[i].d_maint * etu_per_update);
}
pr("any other 0 0 0 100 0\n");
first = 1;
for (i = 0; intrchr[i].in_name; i++) {
if (!intrchr[i].in_enable)
continue;
if (first)
pr("\nInfrastructure building - adding 1 point of efficiency costs:\n"
" type lcms hcms mobility $$$$\n");
pr("%-20s %4d %4d %8d %4d\n",
intrchr[i].in_name, intrchr[i].in_lcms, intrchr[i].in_hcms,
intrchr[i].in_mcost, intrchr[i].in_dcost);
first = 0;
}
}
void
show_sect_stats(int foo)
{
int i;
struct natstr *natp = getnatp(player->cnum);
pr(" mob cost max max naviga packing max\n");
pr(" sector type 0%% 100%% off def bility bonus pop\n");
for (i = 0; dchr[i].d_name; i++) {
if (dchr[i].d_mnem == 0)
continue;
pr("%c %-21.21s", dchr[i].d_mnem, dchr[i].d_name);
if (dchr[i].d_mob0 < 0)
pr(" no way ");
else
pr(" %3.1f %3.1f", dchr[i].d_mob0, dchr[i].d_mob1);
pr(" %5.2f %5.2f %7.7s %10.10s %5d\n",
dchr[i].d_ostr, dchr[i].d_dstr,
symbol_by_value(dchr[i].d_nav, sector_navigation),
symbol_by_value(dchr[i].d_pkg, packing),
max_population(natp->nat_level[NAT_RLEV], i, 100));
}
}
void
show_sect_capab(int foo)
{
int i;
pr(" sector type product p.e.\n");
for (i = 0; dchr[i].d_name; i++) {
if (dchr[i].d_mnem == 0 || dchr[i].d_prd < 0)
continue;
pr("%c %-23s %-7s %4d%%\n",
dchr[i].d_mnem, dchr[i].d_name, pchr[dchr[i].d_prd].p_sname,
dchr[i].d_peffic);
}
}
void
show_item(int tlev)
{
struct ichrstr *ip;
pr("item value sell lbs packing melt item\n");
pr("mnem in no wh ur bk deno name\n");
for (ip = ichr; ip->i_name; ip++) {
pr(" %c %5d %4s %3d %2d %2d %2d %2d %2d %4d %s\n",
ip->i_mnem, ip->i_value, ip->i_sell ? "yes" : "no", ip->i_lbs,
ip->i_pkg[IPKG], ip->i_pkg[NPKG], ip->i_pkg[WPKG],
ip->i_pkg[UPKG], ip->i_pkg[BPKG],
ip->i_melt_denom, ip->i_name);
}
}
void
show_product(int tlev)
{
struct pchrstr *pp;
int i;
char *lev;
pr("product cost raw materials reso dep level p.e.\n");
for (pp = pchr; pp->p_sname; pp++) {
if (!pp->p_sname[0])
continue;
pr("%7.7s %c $%-3d ",
pp->p_sname,
pp->p_type < 0 ? ' ' : ichr[pp->p_type].i_mnem,
pp->p_cost);
(void)CANT_HAPPEN(MAXPRCON > 3); /* output has only three columns */
for (i = 0; i < 3; i++) {
if (i < MAXPRCON && pp->p_camt[i]
&& pp->p_ctype[i] > I_NONE && pp->p_ctype[i] <= I_MAX)
pr(" %2d%c", pp->p_camt[i], ichr[pp->p_ctype[i]].i_mnem);
else
pr(" ");
}
if (pp->p_nrndx)
pr(" %5.5s %3d ",
symbol_by_value(pp->p_nrndx, resources), pp->p_nrdep);
else
pr(" ");
if (pp->p_nlndx < 0)
pr("1.0\n");
else {
lev = symbol_by_value(pp->p_nlndx, level);
pr("(%.4s%+d)/(%.4s%+d)\n",
lev, -pp->p_nlmin, lev, pp->p_nllag - pp->p_nlmin);
}
}
}
void
show_news(int tlev)
{
int i, j;
pr("id category good will\n");
pr(" messsages\n");
for (i = 1; i < N_MAX_VERB + 1; i++) {
if (rpt[i].r_newspage == N_NOTUSED)
continue;
pr("%-2d %-20.20s %4d\n", rpt[i].r_uid,
page_headings[rpt[i].r_newspage].name, rpt[i].r_good_will);
for (j = 0; j < NUM_RPTS; j++)
pr(" %s\n", rpt[i].r_newstory[j]);
}
}
/*
* Show update policy and up to N scheduled updates.
*/
void
show_updates(int n)
{
struct gamestr *game = game_tick_tick();
int demand = 0;
int i;
pr("%s, Turn %d, ETU %d\n", fmttime2822(time(NULL)),
game->game_turn, game->game_tick);
if (update_time[0]) {
if (update_demand == UPD_DEMAND_SCHED) {
pr("Demand updates occur according to schedule:\n");
demand = 1;
} else
pr("Updates occur according to schedule:\n");
for (i = 0; i < n && update_time[i]; i++)
pr("%3d. %s\n", game->game_turn + i,
fmttime2822(update_time[i]));
if (update_window) {
pr("Updates occur within %d seconds after the scheduled time\n",
update_window);
}
} else
pr("There are no updates scheduled.\n");
if (update_demand == UPD_DEMAND_ASYNC) {
pr("Demand updates occur right after the demand is set.\n");
if (*update_demandtimes != 0) {
pr("Demand updates are allowed during: %s\n",
update_demandtimes);
}
demand = 1;
}
if (demand) {
pr("Demand updates require %d country(s) to want one.\n",
update_wantmin);
}
if (updates_disabled())
pr("\nUPDATES ARE DISABLED!\n");
}
/*
* Return T formatted according to RFC 2822.
* The return value is statically allocated and overwritten on
* subsequent calls.
*/
static char *
fmttime2822(time_t t)
{
static char buf[32];
#if defined(_WIN32)
size_t n;
int nn;
TIME_ZONE_INFORMATION tzi;
long time_offset;
struct tm *time;
time = localtime(&t);
n = strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", time);
if (CANT_HAPPEN(n == 0)) {
buf[0] = 0;
return buf;
}
GetTimeZoneInformation(&tzi);
time_offset = -(tzi.Bias +
(time->tm_isdst ? tzi.DaylightBias : tzi.StandardBias));
nn = _snprintf(buf + n, sizeof(buf) - n, " %+03d%02d",
time_offset / 60, abs(time_offset) % 60);
if (CANT_HAPPEN(nn <= 0 || nn + n >= sizeof(buf)))
buf[0] = 0;
#else
size_t n = strftime(buf, sizeof(buf), "%a, %d %b %Y %T %z",
localtime(&t));
if (CANT_HAPPEN(n == 0))
buf[0] = 0;
#endif
return buf;
}
static void
show_load(short item[])
{
i_type i;
for (i = I_NONE + 1; i <= I_MAX; ++i) {
if (item[i])
pr(" %d%c", item[i], ichr[i].i_mnem);
}
}
static void
show_capab(int flags, struct symbol *table)
{
int i;
char *p;
for (i = 0; i < 32; i++) {
if (!(flags & bit(i)))
continue;
p = symbol_by_value(bit(i), table);
if (p)
pr(" %s", p);
}
}