diff --git a/include/econfig-spec.h b/include/econfig-spec.h index 756bed16..e52ecc11 100644 --- a/include/econfig-spec.h +++ b/include/econfig-spec.h @@ -177,6 +177,8 @@ EMPCF_OPT("NO_PLAGUE", opt_NO_PLAGUE, "Disable plague") EMPCF_OPT("PINPOINTMISSILE", opt_PINPOINTMISSILE, "Enable marine missiles") +EMPCF_OPT("RAILWAYS", opt_RAILWAYS, + "Highways double as rail") EMPCF_OPT("RES_POP", opt_RES_POP, "Population is limited by research") EMPCF_OPT("SAIL", opt_SAIL, diff --git a/include/sect.h b/include/sect.h index 2c682961..fb437c96 100644 --- a/include/sect.h +++ b/include/sect.h @@ -183,6 +183,11 @@ extern struct dchrstr bigcity_dchr; #define FORTEFF 5 /* forts must be 5% efficient to fire. */ +/* Can trains enter sector SP? */ +#define SCT_HAS_RAIL(sp) \ + (opt_RAILWAYS ? sct_rail_track((sp)) != 0 \ + : intrchr[INT_RAIL].in_enable && (sp)->sct_rail != 0) + #define MOB_MOVE 0 #define MOB_MARCH 1 #define MOB_RAIL 2 @@ -218,5 +223,6 @@ struct sctintrins { extern struct sctintrins intrchr[INT_DEF + 2]; extern int fort_fire(struct sctstr *); +extern int sct_rail_track(struct sctstr *); #endif diff --git a/info/Options.t b/info/Options.t index c5b199dc..e7059193 100644 --- a/info/Options.t +++ b/info/Options.t @@ -41,6 +41,7 @@ MARKET: Time-delay market and trade. LOANS: Allows S&L type interaction between countries. LANDSPIES: Creates land unit style spies. NO_FORT_FIRE: Forts cannot fire. +RAILWAYS Highways double as rail TECH_POP: Technology costs more to make as your civilian population grows past 50,000 civilians. TREATIES: Sign treaties with your friends and enemies, and breaking of diff --git a/info/Railroad.t b/info/Railroad.t new file mode 100644 index 00000000..88649998 --- /dev/null +++ b/info/Railroad.t @@ -0,0 +1,27 @@ +.TH Concept "Railroad" +.NA Railroad "How railroads work" +.LV Expert +Trains are land units with capability \*Qtrain\*U. They can't be +loaded on land units, but they can be loaded on ships, subject to the +usual restrictions for non-light and heavy land units. +.s1 +Unlike other units, trains can enter a sector only if it has +(operational) railroad track. +.s1 +If option RAILWAYS is disabled, a sector has railroad track as long as +its rail infrastructure efficiency is non-zero. Train mobility cost +depends on rail infrastructure efficiency (see \*Qinfo Mobility\*U). +Spy and satellite reports show approximate rail infrastructure +efficiency in column \*Qrl eff\*U. +.s1 +If option RAILWAYS is enabled, all highway-like sectors are railways, +and the track is operational as long as the sector is at least 5% +efficient. A sector is highway-like if its mobility cost at 100% is +zero (column mob cost in \*Qshow sect s\*u). Operational railways +additionally extend track into adjacent sectors that are at least 60% +efficient and owned by the same nation. Sector selector track gives +the number of operational railways within one sector range. Spy and +satellite reports show the presence of track in column \*Qrl eff\*U. +To visualize your railway network, try \*Qsect # ?track#0\*U. +.s1 +.SA "LandUnits" diff --git a/info/Unit-types.t b/info/Unit-types.t index a2761dce..e72282d1 100644 --- a/info/Unit-types.t +++ b/info/Unit-types.t @@ -193,6 +193,7 @@ the unit can fire 'general unit flak' (see "info Flak") the unit is a spy .L train the unit is a train, and can't be loaded on land units +(see "info Railroad") .L heavy the unit cannot be carried on land units or ships, not even supply ships .in @@ -235,4 +236,3 @@ marines 1 5f 10s xlight light marine .FI .s1 .SA "land, LandUnits" - diff --git a/src/lib/commands/sinf.c b/src/lib/commands/sinf.c index 5d01ecd5..32e8fc1f 100644 --- a/src/lib/commands/sinf.c +++ b/src/lib/commands/sinf.c @@ -73,7 +73,10 @@ sinfra(void) pr("%4d%% ", sect.sct_effic); pr("%4d%% ", sect.sct_road); prmobcost(§, MOB_MOVE); - pr("%4d%% ", sect.sct_rail); + if (opt_RAILWAYS) + pr(sct_rail_track(§) ? " yes " : " no "); + else + pr("%4d%% ", sect.sct_rail); prmobcost(§, MOB_RAIL); pr("%4d%% ", SCT_DEFENSE(§)); pr("%5.2f\n", sector_strength(§)); diff --git a/src/lib/commands/spy.c b/src/lib/commands/spy.c index 5c31d243..8d65dea4 100644 --- a/src/lib/commands/spy.c +++ b/src/lib/commands/spy.c @@ -189,7 +189,7 @@ spy_report(struct sctstr *sp) sp->sct_oldown, roundintby((int)sp->sct_effic, 10), roundintby((int)sp->sct_road, 10), - roundintby((int)sp->sct_rail, 10), + opt_RAILWAYS ? !!sct_rail_track(sp) : roundintby(sp->sct_rail, 10), roundintby((int)sp->sct_defense, 10), roundintby(sp->sct_item[I_CIVIL], 10), roundintby(sp->sct_item[I_MILIT], 10), diff --git a/src/lib/common/move.c b/src/lib/common/move.c index 3257acb9..c648bef3 100644 --- a/src/lib/common/move.c +++ b/src/lib/common/move.c @@ -33,8 +33,10 @@ #include +#include "file.h" #include "misc.h" #include "nat.h" +#include "optlist.h" #include "path.h" #include "sect.h" #include "xy.h" @@ -48,6 +50,12 @@ sector_mcost(struct sctstr *sp, int mobtype) if (base < 0) return -1.0; + if (mobtype == MOB_RAIL && opt_RAILWAYS) { + if (!SCT_HAS_RAIL(sp)) + return -1; + mobtype = MOB_MARCH; + } + /* linear function in eff, d_mob0 at 0%, d_mob1 at 100% */ base += (dchr[sp->sct_type].d_mob1 - base) * sp->sct_effic / 100; if (CANT_HAPPEN(base < 0)) @@ -83,3 +91,33 @@ speed_factor(double effspd, int tech) { return 480.0 / (effspd + techfact(tech, effspd)); } + +/* Minimal efficiency for railway and railway extension (opt_RAILWAYS) */ +#define SCT_RAIL_EFF 5 +#define SCT_RAIL_EXT_EFF 60 + +/* Is sector SP a railway? */ +#define SCT_IS_RAILWAY(sp) \ + (dchr[(sp)->sct_type].d_mob1 == 0 && (sp)->sct_effic >= SCT_RAIL_EFF) +/* May sector SP have a railway extension? */ +#define SCT_MAY_HAVE_RAIL_EXT(sp) \ + ((sp)->sct_effic >= SCT_RAIL_EXT_EFF) +/* Does railway sector SP extend railway track into sector TOSP? */ +#define SCT_EXTENDS_RAIL(sp, tosp) \ + ((sp)->sct_own == (tosp)->sct_own && SCT_MAY_HAVE_RAIL_EXT(tosp)) + +int +sct_rail_track(struct sctstr *sp) +{ + int i, res; + struct sctstr *nsp; + + res = !!SCT_IS_RAILWAY(sp); + for (i = DIR_FIRST; i <= DIR_LAST; i++) { + nsp = getsectp(sp->sct_x + diroff[i][0], + sp->sct_y + diroff[i][1]); + if (SCT_IS_RAILWAY(nsp) && SCT_EXTENDS_RAIL(nsp, sp)) + res++; + } + return res; +} diff --git a/src/lib/common/nsc.c b/src/lib/common/nsc.c index 15e7d809..d507e30b 100644 --- a/src/lib/common/nsc.c +++ b/src/lib/common/nsc.c @@ -49,6 +49,7 @@ static void *nsc_ver(struct valstr *, struct natstr *, void *); static void *nsc_ver_maxnoc(struct valstr *, struct natstr *, void *); static void *nsc_sct_terr(struct valstr *, struct natstr *, void *); +static void *nsc_sct_track(struct valstr *, struct natstr *, void *); static void *nsc_cargo_nplane(struct valstr *, struct natstr *, void *); static void *nsc_cargo_nchopper(struct valstr *, struct natstr *, void *); static void *nsc_cargo_nxlight(struct valstr *, struct natstr *, void *); @@ -160,6 +161,7 @@ struct castr sect_ca[] = { {"uran", fldoff(sct_uran), NSC_UCHAR, 0, NULL, EF_BAD, 0}, {"oldown", fldoff(sct_oldown), NSC_NATID, 0, NULL, EF_NATION, 0}, {"off", fldoff(sct_off), NSC_UCHAR, 0, NULL, EF_BAD, 0}, + {"track", 0, NSC_LONG, 0, nsc_sct_track, EF_BAD, NSC_EXTRA}, NSC_IVEC(fldoff(sct_item), ""), NSC_IVEC(fldoff(sct_dist), "_dist"), NSC_IVEC(fldoff(sct_del), "_del"), @@ -772,6 +774,13 @@ nsc_sct_terr(struct valstr *val, struct natstr *np, void *ptr) return ptr; } +static void * +nsc_sct_track(struct valstr *val, struct natstr *np, void *ptr) +{ + val->val_as.lng = sct_rail_track(ptr); + return NULL; +} + static void * nsc_cargo_nplane(struct valstr *val, struct natstr *np, void *ptr) { diff --git a/src/lib/common/path.c b/src/lib/common/path.c index 103d3b0a..b2e9b5ed 100644 --- a/src/lib/common/path.c +++ b/src/lib/common/path.c @@ -218,8 +218,7 @@ bp_neighbors(struct as_coord c, struct as_coord *cp, void *pp) move through it. We calculate it later. */ if (dchr[sp->sct_type].d_mob0 < 0) continue; - if (bp->bp_mobtype == MOB_RAIL - && (!intrchr[INT_RAIL].in_enable || sp->sct_rail == 0)) + if (bp->bp_mobtype == MOB_RAIL && !SCT_HAS_RAIL(sp)) continue; if (sp->sct_own != from->sct_own) continue; diff --git a/src/lib/global/options.c b/src/lib/global/options.c index 3cab294c..f06af592 100644 --- a/src/lib/global/options.c +++ b/src/lib/global/options.c @@ -57,6 +57,7 @@ int opt_NOMOBCOST = 1; int opt_NO_FORT_FIRE = 0; int opt_NO_PLAGUE = 1; int opt_PINPOINTMISSILE = 1; +int opt_RAILWAYS = 0; int opt_RES_POP = 0; int opt_SAIL = 1; int opt_SLOW_WAR = 0; diff --git a/src/lib/subs/lndsub.c b/src/lib/subs/lndsub.c index 548877ef..69c1998f 100644 --- a/src/lib/subs/lndsub.c +++ b/src/lib/subs/lndsub.c @@ -915,7 +915,7 @@ lnd_mar_one_sector(struct emp_qelem *list, int dir, natid actor, continue; } } - if ((!intrchr[INT_RAIL].in_enable || sect.sct_rail == 0) + if (!SCT_HAS_RAIL(§) && lnd_mobtype(&llp->unit.land) == MOB_RAIL) { if (together) { pr("no rail system in %s\n", xyas(newx, newy, actor)); diff --git a/src/lib/subs/satmap.c b/src/lib/subs/satmap.c index fd29c8eb..20a5726d 100644 --- a/src/lib/subs/satmap.c +++ b/src/lib/subs/satmap.c @@ -265,7 +265,7 @@ satdisp_sect(struct sctstr *sp, int acc) dchr[sp->sct_type].d_mnem, sp->sct_own, roundintby((int)sp->sct_effic, acc / 2), roundintby((int)sp->sct_road, acc / 2), - roundintby((int)sp->sct_rail, acc / 2), + opt_RAILWAYS ? !!sct_rail_track(sp) : roundintby(sp->sct_rail, acc / 2), roundintby((int)sp->sct_defense, acc / 2), roundintby(sp->sct_item[I_CIVIL], acc), roundintby(sp->sct_item[I_MILIT], acc),