diff --git a/Make.mk b/Make.mk index 97e44e5d..26985515 100644 --- a/Make.mk +++ b/Make.mk @@ -284,6 +284,7 @@ ifeq ($(empthread),Windows) $(client): src/lib/w32/getopt.o endif +$(util): src/lib/common/move.o # FIXME hack for opt_RAILWAYS $(util): $(libs) lib/libas.a: $(filter src/lib/as/%, $(obj)) diff --git a/include/commodity.h b/include/commodity.h index c3757661..d515364e 100644 --- a/include/commodity.h +++ b/include/commodity.h @@ -44,6 +44,7 @@ struct comstr { /* initial part must match struct empobj */ short ef_type; short com_uid; + unsigned com_seqno; time_t com_timestamp; natid com_owner; /* end of part matching struct empobj */ diff --git a/include/econfig-spec.h b/include/econfig-spec.h index bc4ccc6a..d426e58a 100644 --- a/include/econfig-spec.h +++ b/include/econfig-spec.h @@ -161,6 +161,8 @@ EMPCF_OPT("INTERDICT_ATT", opt_INTERDICT_ATT, "Interdict post-attack move in") EMPCF_OPT("LANDSPIES", opt_LANDSPIES, "Enable the land unit type spies") +EMPCF_OPT("LIMBER", opt_LIMBER, + "Artillery requires mobility to start firing") EMPCF_OPT("LOANS", opt_LOANS, "Allow bailing out of other countries via S&L scandals") EMPCF_OPT("LOSE_CONTACT", opt_LOSE_CONTACT, @@ -179,6 +181,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/empobj.h b/include/empobj.h index 2610c18e..63931c10 100644 --- a/include/empobj.h +++ b/include/empobj.h @@ -57,6 +57,7 @@ struct empobj { */ short ef_type; short uid; + unsigned seqno; time_t timestamp; /* end of part matching struct emptypedstr */ natid own; /* valid if EFF_OWNER is in table's flags */ diff --git a/include/file.h b/include/file.h index 79517784..9d724f65 100644 --- a/include/file.h +++ b/include/file.h @@ -67,6 +67,7 @@ struct empfile { struct emptypedstr { short ef_type; short uid; + unsigned seqno; time_t timestamp; }; @@ -187,6 +188,7 @@ extern int ef_close(int); extern int ef_flush(int); extern void ef_blank(int, int, void *); extern int ef_write(int, int, void *); +extern void ef_set_uid(int, void *, int); extern int ef_extend(int, int); extern int ef_ensure_space(int, int, int); extern int ef_truncate(int, int); diff --git a/include/game.h b/include/game.h index 8a52509c..1a343c7f 100644 --- a/include/game.h +++ b/include/game.h @@ -40,6 +40,7 @@ struct gamestr { /* initial part must match struct empobj */ short ef_type; short game_uid; + unsigned game_seqno; time_t game_timestamp; /* end of part matching struct empobj */ char game_upd_disable; /* updates disabled? */ diff --git a/include/journal.h b/include/journal.h index bd35399f..7ef10ae1 100644 --- a/include/journal.h +++ b/include/journal.h @@ -34,13 +34,17 @@ #ifndef JOURNAL_H #define JOURNAL_H +struct player; /* FIXME temporary hack */ + int journal_startup(void); void journal_shutdown(void); int journal_reopen(void); void journal_login(void); void journal_logout(void); void journal_prng(unsigned); +void journal_output(struct player *, int, char *); void journal_input(char *); +void journal_command(char *); void journal_update(int); #endif diff --git a/include/land.h b/include/land.h index f3923066..73ca0b82 100644 --- a/include/land.h +++ b/include/land.h @@ -51,6 +51,7 @@ struct lndstr { /* initial part must match struct empobj */ short ef_type; short lnd_uid; /* unit id (land unit) */ + unsigned lnd_seqno; time_t lnd_timestamp; /* Last time this unit was touched */ natid lnd_own; /* owner's country num */ coord lnd_x; /* x location in abs coords */ @@ -190,6 +191,7 @@ extern int lnd_hardtarget(struct lndstr *); extern int lnd_mar_one_sector(struct emp_qelem *, int, natid, int); extern int lnd_support(natid, natid, coord, coord, int); extern int lnd_can_attack(struct lndstr *); +extern void lnd_unlimber(struct lndstr *lp); extern int lnd_fortify (struct lndstr *lp, int hard_amt); extern void lnd_set_tech(struct lndstr *, int); diff --git a/include/loan.h b/include/loan.h index 542af6c8..d18c4d88 100644 --- a/include/loan.h +++ b/include/loan.h @@ -44,6 +44,7 @@ struct lonstr { /* initial part must match struct empobj */ short ef_type; short l_uid; + unsigned l_seqno; time_t l_timestamp; /* end of part matching struct empobj */ natid l_loner; /* loan shark */ diff --git a/include/lost.h b/include/lost.h index c371f936..a6624637 100644 --- a/include/lost.h +++ b/include/lost.h @@ -40,7 +40,8 @@ struct loststr { /* initial part must match struct empobj */ short ef_type; - int lost_uid; + short lost_uid; + unsigned lost_seqno; time_t lost_timestamp; /* When it was lost */ natid lost_owner; /* Who lost it */ /* end of part matching struct empobj */ diff --git a/include/nat.h b/include/nat.h index b2f2bd5f..58b8067f 100644 --- a/include/nat.h +++ b/include/nat.h @@ -70,6 +70,7 @@ struct realmstr { /* initial part must match struct empobj */ short ef_type; short r_uid; /* realm table index */ + unsigned r_seqno; time_t r_timestamp; /* Last time this realm was touched */ natid r_cnum; /* country number */ /* end of part matching struct empobj */ @@ -82,6 +83,7 @@ struct natstr { /* initial part must match struct empobj */ short ef_type; short nat_uid; /* equals nat_cnum */ + unsigned nat_seqno; time_t nat_timestamp; natid nat_cnum; /* our country number */ /* end of part matching struct empobj */ diff --git a/include/news.h b/include/news.h index c7361144..5665a92f 100644 --- a/include/news.h +++ b/include/news.h @@ -48,6 +48,7 @@ struct nwsstr { /* initial part must match struct empobj */ short ef_type; short nws_uid; + unsigned nws_seqno; time_t nws_timestamp; /* end of part matching struct empobj */ natid nws_ano; /* "actor" country # */ diff --git a/include/nuke.h b/include/nuke.h index 2f7fcdaa..054cdb4c 100644 --- a/include/nuke.h +++ b/include/nuke.h @@ -44,6 +44,7 @@ struct nukstr { /* initial part must match struct empobj */ short ef_type; short nuk_uid; + unsigned nuk_seqno; time_t nuk_timestamp; /* Last time this nuke was touched */ natid nuk_own; coord nuk_x, nuk_y; /* current loc of device */ diff --git a/include/plane.h b/include/plane.h index 2133f362..485ebb51 100644 --- a/include/plane.h +++ b/include/plane.h @@ -48,6 +48,7 @@ struct plnstr { /* initial part must match struct empobj */ short ef_type; short pln_uid; /* plane unit id */ + unsigned pln_seqno; time_t pln_timestamp; /* Last time this plane was touched */ natid pln_own; /* owning country */ coord pln_x; /* plane x-y */ diff --git a/include/player.h b/include/player.h index 584c613f..f6ab9b56 100644 --- a/include/player.h +++ b/include/player.h @@ -76,8 +76,9 @@ struct player { int simulation; /* e.g. budget command */ double dolcost; time_t curup; /* when last input was received */ - int aborted; /* interrupt cookie received? */ + int aborted; /* interrupt cookie or EOF received? */ int eof; /* EOF (cookie or real) received? */ + int recvfail; /* #recvclient() failures */ int curid; /* for pr, cur. line's id, -1 none */ char *map; /* pointer to in-mem map */ char *bmap; /* pointer to in-mem bmap */ diff --git a/include/prototypes.h b/include/prototypes.h index 5edadd96..450e69b7 100644 --- a/include/prototypes.h +++ b/include/prototypes.h @@ -282,8 +282,6 @@ extern int logreopen(void); extern void logerror(char *, ...) ATTRIBUTE((format (printf, 1, 2))); /* more in misc.h */ /* mapdist.c */ -extern int diffx(int, int); -extern int diffy(int, int); extern int deltax(int, int); extern int deltay(int, int); extern int mapdist(int, int, int, int); @@ -710,6 +708,7 @@ extern int wu(natid, natid, char *, ...) ATTRIBUTE((format (printf, 3, 4))); * src/lib/update/ *.c */ /* age.c */ +extern int age_people(int, int); extern void age_levels(int); /* anno.c */ extern void delete_old_announcements(void); diff --git a/include/sect.h b/include/sect.h index c0d78222..b0237a3c 100644 --- a/include/sect.h +++ b/include/sect.h @@ -46,6 +46,7 @@ struct sctstr { /* initial part must match struct empobj */ short ef_type; short sct_uid; /* equals XYOFFSET(sct_x, sct_y) */ + unsigned sct_seqno; time_t sct_timestamp; /* Last time this sector was written to */ natid sct_own; /* owner's country num */ coord sct_x; /* x coord of sector */ @@ -102,6 +103,7 @@ enum d_navigation { struct dchrstr { unsigned char d_uid; char d_mnem; /* map symbol */ + unsigned char d_terrain; /* terrain sector type */ int d_prd; /* product type */ int d_peffic; /* process efficiency, in percent */ float d_mob0, d_mob1; /* movement cost at 0 and 100% eff */ @@ -169,6 +171,9 @@ extern struct dchrstr dchr[SCT_TYPE_MAX + 2]; extern struct dchrstr bigcity_dchr; #define IS_BIG_CITY(type) (dchr[(type)].d_pkg == UPKG) +#define IS_BRIDGE_HEAD(type) \ + ((type) == SCT_BHEAD || (type) == SCT_HIWAY || (type) == SCT_BTOWER) + /* Minimal efficiency of sectors that can be knocked down (bridges) */ #define SCT_MINEFF 20 @@ -181,6 +186,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 @@ -216,5 +226,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/include/ship.h b/include/ship.h index 1f7fbf8a..d09a07b0 100644 --- a/include/ship.h +++ b/include/ship.h @@ -65,6 +65,7 @@ struct shpstr { /* initial part must match struct empobj */ short ef_type; short shp_uid; /* unit id (ship #) */ + unsigned shp_seqno; time_t shp_timestamp; /* Last time this ship was touched. */ natid shp_own; /* owner's country num */ coord shp_x; /* x location in abs coords */ diff --git a/include/trade.h b/include/trade.h index 307f990b..074a77da 100644 --- a/include/trade.h +++ b/include/trade.h @@ -44,6 +44,7 @@ struct trdstr { /* initial part must match struct empobj */ short ef_type; short trd_uid; + unsigned trd_seqno; time_t trd_timestamp; natid trd_owner; /* end of part matching struct empobj */ diff --git a/include/treaty.h b/include/treaty.h index d5efd77e..31491eef 100644 --- a/include/treaty.h +++ b/include/treaty.h @@ -41,6 +41,7 @@ struct trtstr { /* initial part must match struct empobj */ short ef_type; short trt_uid; + unsigned trt_seqno; time_t trt_timestamp; /* end of part matching struct empobj */ natid trt_cna; /* proposer */ diff --git a/include/xy.h b/include/xy.h index 9f1f06c2..574c1b67 100644 --- a/include/xy.h +++ b/include/xy.h @@ -43,9 +43,9 @@ #define XYOFFSET(x, y) (((y) * WORLD_X + (x)) / 2) #define XNORM(x) \ - (((x) < 0) ? (WORLD_X - (-(x) % WORLD_X)) : ((x) % WORLD_X)) + (((x) < 0) ? (WORLD_X - 1 - ((-(x) - 1) % WORLD_X)) : ((x) % WORLD_X)) #define YNORM(y) \ - (((y) < 0) ? (WORLD_Y - (-(y) % WORLD_Y)) : ((y) % WORLD_Y)) + (((y) < 0) ? (WORLD_Y - 1 - ((-(y) - 1) % WORLD_Y)) : ((y) % WORLD_Y)) struct range { coord lx; /* low-range x,y */ diff --git a/info/BTU.t b/info/BTU.t index ddc7e6c6..309a762e 100644 --- a/info/BTU.t +++ b/info/BTU.t @@ -14,7 +14,7 @@ do. BTUs are generated in real-time based on the number of civilians that are in your capital. Every time you log out and in again, the server calculates how long you've been away, and based on that time awards -you a certain number of BTUs. +you a certain number of BTUs. The same happens at the update. .s1 Here is the procedure for determining how many BTUs you get: .nf @@ -27,43 +27,46 @@ they are automatically set back to the maximum. (2) Find out how many civs are required to make one BTU in one time unit. The "version" command will tell you how many civilians are required to -produce one BTU in one Empire Time Unit (ETU). +produce one BTU in one Empire Time Unit (ETU) in a perfectly efficient +capital. -(3) Calculate how many BTUs your cap produces in one time unit. +(3) Calculate how efficiently your capital makes BTUs. A proper +capital's BTU efficiency is sector efficiency times work percentage, +but at least 0.5%. A mountain capital's BTU efficiency is always +0.5%. + +(4) Calculate how many BTUs your capital produces in one time unit. Divide the number of civs generating BTUs (step 1) by the number of -civs required to produce one BTU in one time unit (step 2). If your -capital is in a mountain or has zero efficiency, then multiply by -1/200. Otherwise, multiply by (sector efficiency) / 100. Note that -0% capitals and mountains generate BTUs as if they were 0.5% capitals. +civs required to produce one BTU in one time unit (step 2). Multiply +by efficiency (step 3). -(4) Find out how many time units have passed. +(5) Find out how many time units have passed since you last got BTUs. A fixed number of ETUs elapse between updates. The \*Qversion\*U command shows how many. -(5) Calculate how many BTUs you get. -Multiply the number of BTUs your cap produces in one ETU (step 3) by -the number of ETUs which have passed (step 4). This is how many BTUs +(6) Calculate how many BTUs you get. +Multiply the number of BTUs your cap produces in one ETU (step 4) by +the number of ETUs which have passed (step 5). This is how many BTUs you get. Note that there is a limit to how many BTUs you can have at any given time. This number is usually 640 but can be changed by the deity. -EXAMPLE: say you had a 100% capital containing 500 civs. -Suppose that version said: +EXAMPLE: say your capital is in perfect condition (100% efficiency and +work), and has 500 civilians. Suppose that version said: It takes 25.00 civilians to produce a BTU in one time unit Then first you would divide 500 by 25 to get 20. Now since your cap -is 100% efficient, you would multiply 20 by (100/100) and so the civs -in your cap would produce 20 BTUs per time unit. Now suppose that +is 100% efficient and 100% working, its BTU efficiency is 100% * 100% += 100%, i.e. your capital makes 20 BTUs per ETU. Now suppose that version said: An update consists of 60 empire time units. and suppose that exactly that many had passed since the last time you -logged on. - Lastly, we multiply 20 by 60 to get 1200 BTUs. But since the -maximum is 640, we would have 640 BTUs. Note that if your capital had -been in a mountain sector, then you would have only gotten 6 BTUs in -24 hours. +got BTUs. You multiply 20 by 60 and get 1200 BTUs. But since the +maximum is 640, you'd have 640 BTUs. Note that if your capital were a +mountain sector, it would make only 20 * 0.5% = 0.1 BTUs per time +unit. You'd get only 6 BTUs then. .fi .s1 .SA "Innards, version, Time, Playing" diff --git a/info/Citizens.t b/info/Citizens.t index b5177421..04e69a53 100644 --- a/info/Citizens.t +++ b/info/Citizens.t @@ -32,7 +32,7 @@ sector is too small, or if the sector starves, disloyalty will increase. .s1 If a sector is a conquered sector, -a star \*Q*\*U will be shown in the appropriate column +the old owner will be shown in the appropriate column on the census report. Friendly sectors allow enlistment of military, as well as the ability to move the civilians freely about. diff --git a/info/Hvy-Metal.t b/info/Hvy-Metal.t new file mode 100644 index 00000000..4d6ca557 --- /dev/null +++ b/info/Hvy-Metal.t @@ -0,0 +1,120 @@ +.TH Concept "Hvy-Metal" +.NA Hvy-Metal "Special rules for Hvy Metal" +.LV Basic +.s1 +Ship types are customized. Better check show for the details. +.s1 +Dreadnoughts are more heavily armored. Frigates are more fragile. +.s1 +More ships can carry choppers, and jf2 count as choppers. Some ships +are not ideal for this, and this is reflected by the lack of large +petrol and shell storage. But it gives players yet another +interesting avenue to explore, and reflects the ships better. Also +helps simulate irregular forces desperately equipping what they can for +a role. A chance for a creative player to spring surprises. +.s1 +New assault ship type: Think Tarawa or Iwo Jima class ships. These +put the ASSAULT back into landings. Huge mil capacity, can operate +choppers and Harriers. Able to drop 12 marine units and a horde of +mil onto a shore, or sustain para drops and air strikes inland with +its aircraft. Go ashore in a hurry, Big Time. A fat target too, so +guard it carefully. +.s1 +Ships can navigate bridge sectors regardless of owner. +.s1 +Ships can assault the sector they're in. +.s1 +Land unit types are heavily customized. Better check show for the +details. Non-mechanized units are slower and more vulnerable, +infantry units have capability security, marines can have amtraks, +artillery firing ranges differ, few units are light (but troop +transports and landing ships can carry non-light units), food +capacities are reduced. +.s1 +Tanks and AAA can fire. \*QHey guys, why don't we shoot this big gun +on this tank at those d*mn elves on that hill instead of hanging our +laundry on it\*U? Don't expect the range of an artillery unit, which +specializes in this and has forward observers built in to the unit +cost. Watch your magazines too! +.s1 +Artillery units with capability heavy can only fire at sectors and +fire offensive support. They can't target ships, return fire, +interdict, or fire defensive support. +.s1 +Artillery units in mountains receive a range bonus of +0.5. +.s1 +New experimental option RAILWAYS is enabled: trains run on highways +and sectors next to highways. Use sinfra to find out mobility costs. +Try sect # ?track>0 to visualize your track. Check info +\*QRailroad\*U for details. +.s1 +There are more train types, BIG siege guns, etc. +.\"And we got the +.\"Mother Of All Guns: something like what was attempted a few times in +.\"history, a huge costly gun with incredible range. It CAN be moved. +.\"Slowly. Has all the problems fixed positions have. If these are not +.\"obvious, any experienced player will gladly demonstrate them for you. +.s1 +Infrastructure is disabled. +.s1 +New experimental option LIMBER is enabled: slow guns and heavy railway +guns use mobility to unlimber. Limbering and unlimbering is +completely automatic. See info \*QUnit-types\*U for details. +.s1 +Planes are tweaked somewhat. show is your friend. +.s1 +Che age just like military reserves: 1% decay per 24 ETUs. In +sufficiently loyal sectors you don't own, however, a full half of them +(rounded up) vanish each update. The anti command is disabled. +.s1 +Mountains, fortresses and cities provide a respectable defensive +bonus. Cities cost real money to build. Banks cost some money, too. +Ever seen Fort Knox? Bridges cost only half as many hcms as usual. +.s1 +There is no bridge head sector type. Highways can serve as bridge +heads. +.s1 +New sector type freeway. You can turn plains into freeway and back, +but you can't turn either into anything else. Freeways can serve as +bridge heads. +.s1 +Plains produce food. +.s1 +Food is much harder to grow. Expect 20% of your population to be +farmers. +.s1 +Sectors without any food get one unit of food for free to avoid +starvation. In Hvy Metal, that free food is always completely +consumed. +.s1 +New sector type biofuel plant lets you make oil from food, at a cost. +.s1 +Your refineries make less petrol from each barrel of oil than usual. +.s1 +Natural resources gold, oil and uran deplete much slower than usual. +Fully depleting a sector will take you several updates. Oil +production does not depend on tech; you don't have to micro-manage to +conserve oil. Resources are scarce. Expect to fight over them. +.s1 +Oil derricks need to set up shop before they produce: no production +while mobility is below maximum. +.s1 +Automatic shell supply for sector flak, ship flak and ship missile +defense is disabled, because its bugs interfere with Hvy Metal +modifications. Same for automatic supply of land units on loading +them onto ships or land units. +.s1 +Missed updates due to server problems will be forced if caught within +15 minutes of planned update time or skipped otherwise. +.s1 +Everything you do in this game is logged by the deities for their own +nefarious purposes. However, the logs are treated confidentially. +.s1 +Make the deities laugh and get up to 5 gold bars. 5 bars per week up +for grabs. +.s1 +Source code and configuration is available from +.br +http://www.stdio.com/~rhyatt/empire/hvy_metal_II/ +.s1 +.SA "Introduction, Server" diff --git a/info/Options.t b/info/Options.t index 07f165c5..99450c9c 100644 --- a/info/Options.t +++ b/info/Options.t @@ -48,6 +48,11 @@ 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 them is reported in the news. + +The following options are new in Hvy Metal: + +LIMBER Artillery requires mobility to start firing +RAILWAYS Highways double as rail .fi .SA "Hidden, Server, version" diff --git a/info/Quick-ref.t b/info/Quick-ref.t index ea828824..df870788 100644 --- a/info/Quick-ref.t +++ b/info/Quick-ref.t @@ -22,7 +22,7 @@ Sector-types: (Petrol is per 10 units; others per unit) Products: -Item $ Lcm Hcm Iron Dust Oil Rad Tech Production Eff. +Item $ Lcm Hcm Iron Dust Oil Food Tech Production Eff. Shells: 3 2 1 0 0 0 0 20 (tech-20)/(tech-10) Guns: 30 5 10 0 0 1 0 20 (tech-20)/(tech-10) Iron: 0 0 0 0 0 0 0 0 0 @@ -30,6 +30,7 @@ Dust: 0 0 0 0 0 0 0 0 0 Bars: 10 0 0 0 5 0 0 0 0 Food: 0 0 0 0 0 0 0 0 (tech+10)/(tech+20) Oil: 0 0 0 0 0 0 0 0 (tech+10)/(tech+20) +Biofuel 10 0 0 0 0 0 5 150 (tech+150)/(tech+230) Petrol 1 0 0 0 0 1 0 20 (tech-20)/(tech-10) Lcm: 0 0 0 1 0 0 0 0 (tech+10)/(tech+20) Hcm: 0 0 0 2 0 0 0 0 (tech+10)/(tech+20) 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 1a0cca53..5c956734 100644 --- a/info/Unit-types.t +++ b/info/Unit-types.t @@ -163,6 +163,15 @@ The number of extra-light planes the land unit can carry. The number of land units the unit can carry (no 'heavy' units) .in .s1 +If option LIMBER is enabled, guns need to be \*Qlimbered\*U to march +and \*Qunlimbered\*U to fire or fortify. Limbering and unlimbering is +completely automatic. Limbering is free, but unlimbering can cost +mobility, depending on the type of artillery unit: heavy railway +artillery and any artillery with a speed below 22 pay unlimber +mobility equivalent to a march with path cost 0.2, plus 1. This gains +one point of fortification. Presence of engineer units in the same +sector cut the cost by a third. +.s1 Each land unit can carry a certain amount of products and has certain capabilities. These are listed under the cargoes & abilities section. The cargoes give the number of each product that can be carried. @@ -196,6 +205,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 @@ -238,4 +248,3 @@ marines 1 5f 10s xlight light marine .FI .s1 .SA "land, LandUnits" - diff --git a/src/lib/commands/add.c b/src/lib/commands/add.c index eba4d33e..fb83eacb 100644 --- a/src/lib/commands/add.c +++ b/src/lib/commands/add.c @@ -154,13 +154,8 @@ add(void) sect.sct_defense = 0; sect.sct_own = 0; sect.sct_oldown = 0; - if (sect.sct_type == SCT_BSPAN || - sect.sct_type == SCT_BTOWER) - sect.sct_newtype = sect.sct_type = SCT_WATER; - else if (sect.sct_type != SCT_MOUNT && - sect.sct_type != SCT_PLAINS) - sect.sct_newtype = sect.sct_type = SCT_RURAL; - /* No dist path */ + sect.sct_newtype = sect.sct_type + = dchr[sect.sct_type].d_terrain; sect.sct_dist_x = sect.sct_x; sect.sct_dist_y = sect.sct_y; memset(sect.sct_item, 0, sizeof(sect.sct_item)); diff --git a/src/lib/commands/buil.c b/src/lib/commands/buil.c index c9ea788a..bb3d2d6e 100644 --- a/src/lib/commands/buil.c +++ b/src/lib/commands/buil.c @@ -528,12 +528,10 @@ build_bridge(struct sctstr *sp, short *vec) char buf[1024]; if (opt_EASY_BRIDGES == 0) { /* must have a bridge head or tower */ - if (sp->sct_type != SCT_BTOWER) { - if (sp->sct_type != SCT_BHEAD) - return 0; - if (sp->sct_newtype != SCT_BHEAD) - return 0; - } + if (!IS_BRIDGE_HEAD(sp->sct_type)) + return 0; + if (sp->sct_newtype != sp->sct_type) + return 0; } if (sp->sct_effic < 60 && !player->god) { diff --git a/src/lib/commands/desi.c b/src/lib/commands/desi.c index cee4e2d8..20e27863 100644 --- a/src/lib/commands/desi.c +++ b/src/lib/commands/desi.c @@ -92,12 +92,17 @@ desi(void) pr("Only %s can make a %s!\n", cname(0), dchr[des].d_name); continue; } + if (dchr[des].d_terrain != dchr[sect.sct_type].d_terrain) { + pr("You can't change a %s into a %s\n", + dchr[sect.sct_type].d_name, dchr[des].d_name); + continue; + } } if (sect.sct_type == des && sect.sct_newtype == des) continue; if (sect.sct_type == SCT_SANCT) breaksanct++; - if ((des == SCT_HARBR || des == SCT_BHEAD) && !sect.sct_coastal) { + if (des == SCT_HARBR && !sect.sct_coastal) { pr("%s does not border on water.\n", xyas(nstr.x, nstr.y, player->cnum)); if (player->god) @@ -129,11 +134,8 @@ desi(void) if (sect.sct_x == cap_x && sect.sct_y == cap_y && des != SCT_CAPIT && des != SCT_SANCT && des != SCT_MOUNT) pr("You have redesignated your capital!\n"); - if (opt_EASY_BRIDGES == 0) { /* may cause a bridge fall */ - if (n != SCT_BHEAD) - continue; + if (!opt_EASY_BRIDGES && IS_BRIDGE_HEAD(n)) bridgefall(§); - } } if (changed) writemap(player->cnum); diff --git a/src/lib/commands/edit.c b/src/lib/commands/edit.c index 52279b84..a55a7856 100644 --- a/src/lib/commands/edit.c +++ b/src/lib/commands/edit.c @@ -191,12 +191,16 @@ edit(void) return err; break; case 'l': + if (!check_sect_ok(§)) + return RET_FAIL; if ((err = doland(thing, arg, ptr, §)) != RET_OK) return err; if (!putsect(§)) return RET_FAIL; break; case 's': + if (!check_ship_ok(&ship)) + return RET_FAIL; if ((err = doship(thing, arg, ptr, &ship)) != RET_OK) return err; if (!ef_ensure_space(EF_SHIP, ship.shp_uid, 50)) @@ -205,6 +209,8 @@ edit(void) return RET_FAIL; break; case 'u': + if (!check_land_ok(&land)) + return RET_FAIL; if ((err = dounit(thing, arg, ptr, &land)) != RET_OK) return err; if (!ef_ensure_space(EF_LAND, land.lnd_uid, 50)) @@ -213,6 +219,8 @@ edit(void) return RET_FAIL; break; case 'p': + if (!check_plane_ok(&plane)) + return RET_FAIL; if ((err = doplane(thing, arg, ptr, &plane)) != RET_OK) return err; if (!ef_ensure_space(EF_PLANE, plane.pln_uid, 50)) @@ -584,7 +592,7 @@ doland(char op, int arg, char *p, struct sctstr *sect) return RET_SYN; sect->sct_x = newx; sect->sct_y = newy; - sect->sct_uid = XYOFFSET(newx, newy); + ef_set_uid(EF_SECTOR, §, XYOFFSET(newx, newy)); break; case 'D': if (!sarg_xy(p, &newx, &newy)) @@ -767,7 +775,7 @@ doship(char op, int arg, char *p, struct shpstr *ship) ship->shp_rflags = arg; break; case 'U': - ship->shp_uid = arg; + ef_set_uid(EF_SHIP, ship, arg); break; case 'O': if (ship->shp_own) @@ -871,7 +879,7 @@ dounit(char op, int arg, char *p, struct lndstr *land) land->lnd_land = arg; break; case 'U': - land->lnd_uid = arg; + ef_set_uid(EF_LAND, land, arg); break; case 'O': if (land->lnd_own) @@ -994,7 +1002,7 @@ doplane(char op, int arg, char *p, struct plnstr *plane) plane->pln_nuketype = arg; break; case 'U': - plane->pln_uid = arg; + ef_set_uid(EF_PLANE, plane, arg); break; case 'l': if (!sarg_xy(p, &newx, &newy)) diff --git a/src/lib/commands/fly.c b/src/lib/commands/fly.c index ed55fbca..1fdfef90 100644 --- a/src/lib/commands/fly.c +++ b/src/lib/commands/fly.c @@ -87,14 +87,6 @@ fly(void) cno = -1; if (pln_onewaymission(&target, &cno, &wantflags) < 0) return RET_SYN; - if (cno < 0) { - dst_ptr = ⌖ - dst_type = EF_SECTOR; - } else { - getship(cno, &ship); - dst_ptr = &ship; - dst_type = EF_SHIP; - } if (ip && ip->i_uid == I_CIVIL && target.sct_own != target.sct_oldown) { pr("Can't fly civilians into occupied sectors.\n"); @@ -142,6 +134,14 @@ fly(void) pr("No planes got through fighter defenses\n"); } else { getsect(tx, ty, &target); + if (cno < 0) { + dst_ptr = ⌖ + dst_type = EF_SECTOR; + } else { + getship(cno, &ship); + dst_ptr = &ship; + dst_type = EF_SHIP; + } pln_dropoff(&bomb_list, ip, tx, ty, dst_ptr, dst_type); pln_newlanding(&bomb_list, tx, ty, cno); pln_newlanding(&esc_list, tx, ty, cno); diff --git a/src/lib/commands/load.c b/src/lib/commands/load.c index b677d3ea..67f9b60e 100644 --- a/src/lib/commands/load.c +++ b/src/lib/commands/load.c @@ -603,7 +603,14 @@ load_land_ship(struct sctstr *sectp, struct shpstr *sp, int noisy, land.lnd_ship = sp->shp_uid; land.lnd_harden = 0; land.lnd_mission = 0; +#if 0 + /* + * FIXME if this supplies from the sector, the putsect in + * load() / lload() duplicates those supplies, causing a + * seqno mismatch + */ resupply_all(&land); +#endif sp->shp_nland++; putland(land.lnd_uid, &land); if (!has_supply(&land)) @@ -1034,7 +1041,10 @@ load_land_land(struct sctstr *sectp, struct lndstr *lp, int noisy, land.lnd_land = lp->lnd_uid; land.lnd_harden = 0; land.lnd_mission = 0; +#if 0 + /* FIXME same issue as in load_land_ship() */ resupply_all(&land); +#endif lp->lnd_nland++; putland(land.lnd_uid, &land); if (!has_supply(&land)) diff --git a/src/lib/commands/mfir.c b/src/lib/commands/mfir.c index 0e0aa697..5c65583c 100644 --- a/src/lib/commands/mfir.c +++ b/src/lib/commands/mfir.c @@ -188,7 +188,7 @@ multifire(void) } fx = fship.shp_x; fy = fship.shp_y; - } else if (type == EF_SECTOR) { + } else { if (!getsect(item.sect.sct_x, item.sect.sct_y, &fsect)) continue; if (item.sect.sct_own != player->cnum) @@ -330,6 +330,12 @@ multifire(void) pr("Unit %d cannot fire!\n", fland.lnd_uid); continue; } + if (lchr[(int)fland.lnd_type].l_flags & L_HEAVY + && target != targ_land) { + pr("%s is too ponderous to target ships!\n", + prland(&fland)); + continue; + } if (fland.lnd_item[I_GUN] == 0) { pr("%s -- not enough guns\n", prland(&fland)); continue; @@ -349,6 +355,7 @@ multifire(void) pr("Klick! ...\n"); continue; } + if (target == targ_ship) { if (chance(lnd_acc(&fland) / 100.0)) dam = ldround(dam / 2.0, 1); @@ -687,6 +694,9 @@ quiet_bigdef(int type, struct emp_qelem *list, natid own, natid aown, /* Don't shoot yourself */ if (land.lnd_own == aown) continue; + /* Too ponderous for counter-battery fire */ + if (lchr[(int)land.lnd_type].l_flags & L_HEAVY) + continue; rel = getrel(getnatp(land.lnd_own), own); rel2 = getrel(getnatp(land.lnd_own), aown); diff --git a/src/lib/commands/miss.c b/src/lib/commands/miss.c index abf9d16f..f44656db 100644 --- a/src/lib/commands/miss.c +++ b/src/lib/commands/miss.c @@ -226,11 +226,16 @@ mission(void) continue; } - if ((mission == MI_INTERDICT) && (type == EF_LAND)) + if ((mission == MI_INTERDICT) && (type == EF_LAND)) { if (lchr[(int)gp->type].l_dam == 0) { pr("%s: cannot fire at range!\n", obj_nameof(gp)); continue; } + if (lchr[(int)gp->type].l_flags & L_HEAVY) { + pr("%s: too ponderous to interdict!\n", obj_nameof(gp)); + continue; + } + } if ((mission == MI_INTERDICT) && (type == EF_PLANE)) { struct plchrstr *pcp; diff --git a/src/lib/commands/orig.c b/src/lib/commands/orig.c index 4063893e..d8c91f76 100644 --- a/src/lib/commands/orig.c +++ b/src/lib/commands/orig.c @@ -47,7 +47,7 @@ orig(void) struct natstr *np; p = getstarg(player->argp[1], "New origin (sector or country) : ", buf); - if (!p) + if (!p || !*p) return RET_SYN; if (!isalpha(*p) && strchr(p, ',')) { /* sector */ diff --git a/src/lib/commands/sinf.c b/src/lib/commands/sinf.c index e17cf3f7..43780c6e 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 291ab46b..020b42c8 100644 --- a/src/lib/commands/spy.c +++ b/src/lib/commands/spy.c @@ -224,7 +224,7 @@ spyline(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/commands/swap.c b/src/lib/commands/swap.c index a28b8991..309977c5 100644 --- a/src/lib/commands/swap.c +++ b/src/lib/commands/swap.c @@ -63,14 +63,14 @@ swaps(void) /* change the location of secta to that of sectb */ secta.sct_x = sectb.sct_x; secta.sct_y = sectb.sct_y; - secta.sct_uid = sectb.sct_uid; + ef_set_uid(EF_SECTOR, §a, sectb.sct_uid); secta.sct_dist_x = sectb.sct_x; secta.sct_dist_y = sectb.sct_y; secta.sct_coastal = sectb.sct_coastal; /* change the location of sectb to where secta was */ sectb.sct_x = tmp.sct_x; sectb.sct_y = tmp.sct_y; - sectb.sct_uid = tmp.sct_uid; + ef_set_uid(EF_SECTOR, §b, tmp.sct_uid); sectb.sct_dist_x = tmp.sct_x; sectb.sct_dist_y = tmp.sct_y; sectb.sct_coastal = tmp.sct_coastal; diff --git a/src/lib/common/ef_verify.c b/src/lib/common/ef_verify.c index 62c5cab2..3c51d588 100644 --- a/src/lib/common/ef_verify.c +++ b/src/lib/common/ef_verify.c @@ -173,6 +173,8 @@ pln_zap_transient_flags(void) /* laziness: assumes plane file is EFF_MEM */ for (i = 0; (pp = getplanep(i)) != NULL; i++) { + if (!pp->pln_own) + continue; if (pp->pln_flags & PLN_LAUNCHED && (plchr[pp->pln_type].pl_flags & (P_M | P_O)) != P_O) { pp->pln_flags &= ~PLN_LAUNCHED; diff --git a/src/lib/common/file.c b/src/lib/common/file.c index 9586bc4d..6a3fd28a 100644 --- a/src/lib/common/file.c +++ b/src/lib/common/file.c @@ -48,7 +48,10 @@ static int ef_realloc_cache(struct empfile *, int); static int fillcache(struct empfile *, int); +static int do_read(struct empfile *, void *, int, int); static int do_write(struct empfile *, void *, int, int, time_t); +static unsigned get_seqno(struct empfile *, int); +static void new_seqno(struct empfile *, void *); static void do_blank(struct empfile *, void *, int, int); /* @@ -300,11 +303,28 @@ ef_read(int type, int id, void *into) */ static int fillcache(struct empfile *ep, int id) +{ + int ret; + + if (CANT_HAPPEN(!ep->cache)) + return -1; + + ret = do_read(ep, ep->cache, id, MIN(ep->csize, ep->fids - id)); + if (ret >= 0) { + /* cache changed */ + ep->baseid = id; + ep->cids = ret; + } + return ret; +} + +static int +do_read(struct empfile *ep, void *buf, int id, int count) { int n, ret; char *p; - if (CANT_HAPPEN(ep->fd < 0 || !ep->cache)) + if (CANT_HAPPEN(ep->fd < 0 || id < 0 || count < 0)) return -1; if (lseek(ep->fd, id * ep->size, SEEK_SET) == (off_t)-1) { @@ -313,21 +333,21 @@ fillcache(struct empfile *ep, int id) return -1; } - p = ep->cache; - n = MIN(ep->csize, ep->fids - id) * ep->size; + p = buf; + n = count * ep->size; while (n > 0) { ret = read(ep->fd, p, n); if (ret < 0) { if (errno != EINTR) { logerror("Error reading %s elt %d (%s)", ep->file, - id + (int)((p - ep->cache) / ep->size), + id + (int)((p - (char *)buf) / ep->size), strerror(errno)); break; } } else if (ret == 0) { logerror("Unexpected EOF reading %s elt %d", - ep->file, id + (int)((p - ep->cache) / ep->size)); + ep->file, id + (int)((p - (char *)buf) / ep->size)); break; } else { p += ret; @@ -335,12 +355,7 @@ fillcache(struct empfile *ep, int id) } } - if (p == ep->cache) - return -1; /* nothing read, old cache still ok */ - - ep->baseid = id; - ep->cids = (p - ep->cache) / ep->size; - return ep->cids; + return (p - (char *)buf) / ep->size; } /* @@ -425,6 +440,7 @@ ef_write(int type, int id, void *from) ep->prewrite(id, from); if (CANT_HAPPEN((ep->flags & EFF_MEM) ? id >= ep->fids : id > ep->fids)) return 0; /* not implemented */ + new_seqno(ep, from); if (!(ep->flags & EFF_PRIVATE)) { if (do_write(ep, from, id, 1, time(NULL)) < 0) return 0; @@ -442,6 +458,77 @@ ef_write(int type, int id, void *from) return 1; } +/* + * Change element id. + * BUF is an element of table TYPE. + * ID is its new element ID. + * If table is EFF_TYPED, change id and sequence number stored in BUF. + * Else do nothing. + */ +void +ef_set_uid(int type, void *buf, int uid) +{ + struct emptypedstr *elt; + struct empfile *ep; + + if (ef_check(type) < 0) + return; + ep = &empfile[type]; + if (!(ep->flags & EFF_TYPED)) + return; + elt = buf; + if (elt->uid == uid) + return; + elt->uid = uid; + elt->seqno = get_seqno(ep, uid); +} + +/* + * Return sequence number of element ID in table EP. + * Return zero if table is not EFF_TYPED (it has no sequence number + * then). + */ +static unsigned +get_seqno(struct empfile *ep, int id) +{ + struct emptypedstr *elt; + + if (!(ep->flags & EFF_TYPED)) + return 0; + if (id < 0 || id >= ep->fids) + return 0; + if (id >= ep->baseid && id < ep->baseid + ep->cids) + elt = (void *)(ep->cache + (id - ep->baseid) * ep->size); + else { + /* need a buffer, steal last cache slot */ + if (ep->cids == ep->csize) + ep->cids--; + elt = (void *)(ep->cache + ep->cids * ep->size); + if (do_read(ep, elt, id, 1) < 0) + return 0; /* deep trouble */ + } + return elt->seqno; +} + +/* + * Increment sequence number in BUF, which is about to be written to EP. + * Do nothing if table is not EFF_TYPED (it has no sequence number + * then). + */ +static void +new_seqno(struct empfile *ep, void *buf) +{ + struct emptypedstr *elt = buf; + unsigned old_seqno; + + if (!(ep->flags & EFF_TYPED)) + return; + old_seqno = get_seqno(ep, elt->uid); + if (CANT_HAPPEN(old_seqno != elt->seqno)) + old_seqno = MAX(old_seqno, elt->seqno); + elt->seqno = old_seqno + 1; +} + /* * Extend table TYPE by COUNT elements. * Any pointers obtained from ef_ptr() become invalid. @@ -504,9 +591,17 @@ ef_extend(int type, int count) void ef_blank(int type, int id, void *buf) { + struct empfile *ep; + struct emptypedstr *elt; + if (ef_check(type) < 0) return; - do_blank(&empfile[type], buf, id, 1); + ep = &empfile[type]; + do_blank(ep, buf, id, 1); + if (ep->flags & EFF_TYPED) { + elt = buf; + elt->seqno = get_seqno(ep, elt->uid); + } } /* diff --git a/src/lib/common/mapdist.c b/src/lib/common/mapdist.c index cac702f4..50d90c00 100644 --- a/src/lib/common/mapdist.c +++ b/src/lib/common/mapdist.c @@ -41,34 +41,6 @@ #include "optlist.h" #include "prototypes.h" -int -diffx(int x1, int x2) -{ - int dx; - - dx = x1 - x2; - dx = dx % WORLD_X; - if (dx > WORLD_X / 2) - dx = dx - WORLD_X; - if (dx < -WORLD_X / 2) - dx = dx + WORLD_X; - return dx; -} - -int -diffy(int y1, int y2) -{ - int dy; - - dy = y1 - y2; - dy = dy % WORLD_Y; - if (dy > WORLD_Y / 2) - dy = dy - WORLD_Y; - if (dy < -WORLD_Y / 2) - dy = dy + WORLD_Y; - return dy; -} - int deltax(int x1, int x2) { diff --git a/src/lib/common/move.c b/src/lib/common/move.c index 18d748fe..7cbabf82 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/nstreval.c b/src/lib/common/nstreval.c index 7c2f5e8b..10a44110 100644 --- a/src/lib/common/nstreval.c +++ b/src/lib/common/nstreval.c @@ -145,7 +145,7 @@ nstr_exec_val(struct valstr *val, natid cnum, void *ptr, enum nsc_type want) val->val_as.lng = -1; if (CANT_HAPPEN(((struct natstr *)ptr)->ef_type != EF_NATION)) break; - if (!opt_HIDDEN && cnum != NATID_BAD) { + if (opt_HIDDEN && cnum != NATID_BAD) { natp = getnatp(cnum); if (natp->nat_stat != STAT_GOD && !(getcontact(natp, idx) && getcontact(ptr, idx))) diff --git a/src/lib/common/path.c b/src/lib/common/path.c index 61bf4e7b..53149a6e 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/nsc.c b/src/lib/global/nsc.c index 205ae005..0abe19ac 100644 --- a/src/lib/global/nsc.c +++ b/src/lib/global/nsc.c @@ -47,6 +47,7 @@ static void *nsc_ver(struct valstr *, struct natstr *, void *); static void *nsc_ver_maxnoc(struct valstr *, struct natstr *, void *); +static void *nsc_sct_track(struct valstr *, struct natstr *, void *); static void *nsc_pln_att(struct valstr *, struct natstr *, void *); static void *nsc_pln_def(struct valstr *, struct natstr *, void *); static void *nsc_lnd_att(struct valstr *, struct natstr *, void *); @@ -153,6 +154,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"), @@ -178,6 +180,7 @@ struct castr dchr_ca[] = { {"uid", fldoff(d_uid), NSC_UCHAR, 0, NULL, EF_SECTOR_CHR, 0}, {"name", fldoff(d_name), NSC_STRING, 0, NULL, EF_BAD, 0}, {"mnem", fldoff(d_mnem), NSC_STRINGY, 1, NULL, EF_BAD, NSC_CONST}, + {"terrain", fldoff(d_terrain), NSC_UCHAR, 0, NULL, EF_SECTOR_CHR, 0}, {"prd", fldoff(d_prd), NSC_INT, 0, NULL, EF_PRODUCT, 0}, {"peffic", fldoff(d_peffic), NSC_INT, 0, NULL, EF_BAD, 0}, {"mob0", fldoff(d_mob0), NSC_FLOAT, 0, NULL, EF_BAD, 0}, @@ -764,6 +767,13 @@ nsc_ver_maxnoc(struct valstr *val, struct natstr *np, void *ptr) return NULL; } +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_pln_def(struct valstr *val, struct natstr *np, void *ptr) { diff --git a/src/lib/global/options.c b/src/lib/global/options.c index 02753e45..67dd12be 100644 --- a/src/lib/global/options.c +++ b/src/lib/global/options.c @@ -49,6 +49,7 @@ int opt_GUINEA_PIGS = 0; int opt_HIDDEN = 0; int opt_INTERDICT_ATT = 1; int opt_LANDSPIES = 1; +int opt_LIMBER = 0; int opt_LOANS = 1; int opt_LOSE_CONTACT = 0; int opt_MARKET = 0; @@ -58,6 +59,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_SHOWPLANE = 1; diff --git a/src/lib/global/sect.config b/src/lib/global/sect.config index 2e4032fd..4608ce6e 100644 --- a/src/lib/global/sect.config +++ b/src/lib/global/sect.config @@ -28,7 +28,7 @@ # sect.config: Sector characteristics # # Known contributors to this file: -# Markus Armbruster, 2006 +# Markus Armbruster, 2006-2008 # # Derived from sect.c; known contributors: # Dave Pare, 1986 @@ -38,14 +38,19 @@ # Steve McClure, 1998 # +# Sector terrain (column terra) is the uid of the underlying terrain +# sector type. Sector types that occur in that column are terrain +# types, and must have themselves as terrain. + # Available products (column prd) are in product.config. Navigation # types (column nav) are in sector_navigation[]. Packing types # (column pkg) are in packing[]. # Players can only designate sectors with a non-negative value in -# column cost. All such sectors should have the same population limit -# (column maxp), or else players can abuse redesignation to -# mass-murder people. +# column cost, and only to something with the same terrain. All +# sectors with the same terrain and non-negative cost should have the +# same population limit (column maxp), or else players can abuse +# redesignation to mass-murder people. # A sector with urban packing (urba in column pkg) is a big city. # Column maxp applies at 0% efficiency. The limit at 100% is ten @@ -56,41 +61,41 @@ # econfig key custom_tables. config sect-chr -uid mnem prd peff mob0 mob1 nav pkg ostr dstr val cost bui lcm hcm maxp name - 0 "." -1 0 -1 -1 sea norm 0.0 0.0 0 -1 0 0 0 0 "sea" - 1 "^" dust 75 2.4 1.2 land norm 1.0 4.0 5 -1 1 0 0 100 "mountain" - 2 "s" -1 0 -1 -1 land norm 0.0 99.0 127 -1 0 0 0 1000 "sanctuary" - 3 "\134" -1 0 -1 -1 land norm 0.0 99.0 0 -1 0 0 0 0 "wasteland" - 4 "-" -1 0 0.4 0.4 land norm 1.0 2.0 1 0 0 0 0 1000 "wilderness" +uid mnem terra prd peff mob0 mob1 nav pkg ostr dstr val cost bui lcm hcm maxp name + 0 "." 0 -1 0 -1 -1 sea norm 0.0 0.0 0 -1 0 0 0 0 "sea" + 1 "^" 1 dust 75 2.4 1.2 land norm 1.0 4.0 5 -1 1 0 0 100 "mountain" + 2 "s" 4 -1 0 -1 -1 land norm 0.0 99.0 127 -1 0 0 0 1000 "sanctuary" + 3 "\134" 3 -1 0 -1 -1 land norm 0.0 99.0 0 -1 0 0 0 0 "wasteland" + 4 "-" 4 -1 0 0.4 0.4 land norm 1.0 2.0 1 0 0 0 0 1000 "wilderness" # Uncomment one of the following two. The second one is for big cities. - 5 "c" -1 0 0.4 0.2 land norm 1.0 2.0 30 0 1 0 0 1000 "capital" -# 5 "c" -1 0 0.4 0.2 cana urba 1.0 2.0 30 0 10 1 2 1000 "city" - 6 "u" rad 100 0.4 0.2 land norm 1.0 2.0 15 0 1 0 0 1000 "uranium mine" - 7 "p" hap 100 0.4 0.2 land norm 1.0 1.5 5 0 1 0 0 1000 "park" - 8 "d" gun 100 0.4 0.2 land norm 1.0 1.5 7 0 1 0 0 1000 "defense plant" - 9 "i" sh 100 0.4 0.2 land norm 1.0 1.5 6 0 1 0 0 1000 "shell industry" - 10 "m" iron 100 0.4 0.2 land norm 1.0 2.0 5 0 1 0 0 1000 "mine" - 11 "g" dust 100 0.4 0.2 land norm 1.0 2.0 8 0 1 0 0 1000 "gold mine" - 12 "h" -1 0 0.4 0.2 harb ware 1.0 1.5 12 0 1 0 0 1000 "harbor" - 13 "w" -1 0 0.4 0.2 land ware 1.0 1.5 7 0 1 0 0 1000 "warehouse" - 14 "*" -1 0 0.4 0.2 land norm 1.0 1.25 12 0 1 0 0 1000 "airfield" - 15 "a" food 900 0.4 0.2 land norm 1.0 1.5 2 0 1 0 0 1000 "agribusiness" - 16 "o" oil 100 0.4 0.2 land norm 1.0 1.5 5 0 1 0 0 1000 "oil field" - 17 "j" lcm 100 0.4 0.2 land norm 1.0 1.5 3 0 1 0 0 1000 "light manufacturing" - 18 "k" hcm 100 0.4 0.2 land norm 1.0 1.5 4 0 1 0 0 1000 "heavy manufacturing" - 19 "f" -1 0 0.4 0.2 land norm 2.0 4.0 10 0 5 0 1 1000 "fortress" - 20 "t" tech 100 0.4 0.2 land norm 1.0 1.5 10 0 1 0 0 1000 "technical center" - 21 "r" med 100 0.4 0.2 land norm 1.0 1.5 9 0 1 0 0 1000 "research lab" - 22 "n" -1 0 0.4 0.2 land norm 1.0 2.0 10 0 1 0 0 1000 "nuclear plant" - 23 "l" edu 100 0.4 0.2 land norm 1.0 1.5 4 0 1 0 0 1000 "library/school" - 24 "+" -1 0 0.4 0.0 land norm 1.0 1.0 3 0 1 0 0 1000 "highway" - 25 ")" -1 0 0.4 0.2 land norm 1.0 1.5 4 0 1 0 0 1000 "radar installation" - 26 "!" -1 0 0.4 0.2 land norm 1.0 1.5 12 0 1 0 0 1000 "headquarters" - 27 "#" -1 0 0.4 0.0 land norm 1.0 1.0 3 0 1 0 0 1000 "bridge head" - 28 "=" -1 0 0.4 0.0 brid norm 1.0 1.0 5 -1 1 0 0 100 "bridge span" - 29 "b" bars 100 0.4 0.2 land bank 1.0 2.25 10 0 1 0 0 1000 "bank" - 30 "%" pet 1000 0.4 0.2 land norm 1.0 1.5 2 0 1 0 0 1000 "refinery" - 31 "e" -1 0 0.4 0.2 land norm 1.0 2.0 7 0 1 0 0 1000 "enlistment center" - 32 "~" -1 0 0.4 0.2 land norm 1.0 1.5 1 -1 1 0 0 100 "plains" - 33 "@" -1 0 0.4 0.0 land norm 1.0 1.5 4 -1 1 0 0 100 "bridge tower" + 5 "c" 4 -1 0 0.4 0.2 land norm 1.0 2.0 30 0 1 0 0 1000 "capital" +# 5 "c" 4 -1 0 0.4 0.2 cana urba 1.0 2.0 30 0 10 1 2 1000 "city" + 6 "u" 4 rad 100 0.4 0.2 land norm 1.0 2.0 15 0 1 0 0 1000 "uranium mine" + 7 "p" 4 hap 100 0.4 0.2 land norm 1.0 1.5 5 0 1 0 0 1000 "park" + 8 "d" 4 gun 100 0.4 0.2 land norm 1.0 1.5 7 0 1 0 0 1000 "defense plant" + 9 "i" 4 sh 100 0.4 0.2 land norm 1.0 1.5 6 0 1 0 0 1000 "shell industry" + 10 "m" 4 iron 100 0.4 0.2 land norm 1.0 2.0 5 0 1 0 0 1000 "mine" + 11 "g" 4 dust 100 0.4 0.2 land norm 1.0 2.0 8 0 1 0 0 1000 "gold mine" + 12 "h" 4 -1 0 0.4 0.2 harb ware 1.0 1.5 12 0 1 0 0 1000 "harbor" + 13 "w" 4 -1 0 0.4 0.2 land ware 1.0 1.5 7 0 1 0 0 1000 "warehouse" + 14 "*" 4 -1 0 0.4 0.2 land norm 1.0 1.25 12 0 1 0 0 1000 "airfield" + 15 "a" 4 food 900 0.4 0.2 land norm 1.0 1.5 2 0 1 0 0 1000 "agribusiness" + 16 "o" 4 oil 100 0.4 0.2 land norm 1.0 1.5 5 0 1 0 0 1000 "oil field" + 17 "j" 4 lcm 100 0.4 0.2 land norm 1.0 1.5 3 0 1 0 0 1000 "light manufacturing" + 18 "k" 4 hcm 100 0.4 0.2 land norm 1.0 1.5 4 0 1 0 0 1000 "heavy manufacturing" + 19 "f" 4 -1 0 0.4 0.2 land norm 2.0 4.0 10 0 5 0 1 1000 "fortress" + 20 "t" 4 tech 100 0.4 0.2 land norm 1.0 1.5 10 0 1 0 0 1000 "technical center" + 21 "r" 4 med 100 0.4 0.2 land norm 1.0 1.5 9 0 1 0 0 1000 "research lab" + 22 "n" 4 -1 0 0.4 0.2 land norm 1.0 2.0 10 0 1 0 0 1000 "nuclear plant" + 23 "l" 4 edu 100 0.4 0.2 land norm 1.0 1.5 4 0 1 0 0 1000 "library/school" + 24 "+" 4 -1 0 0.4 0.0 land norm 1.0 1.0 3 0 1 0 0 1000 "highway" + 25 ")" 4 -1 0 0.4 0.2 land norm 1.0 1.5 4 0 1 0 0 1000 "radar installation" + 26 "!" 4 -1 0 0.4 0.2 land norm 1.0 1.5 12 0 1 0 0 1000 "headquarters" + 27 "#" 4 -1 0 0.4 0.0 land norm 1.0 1.0 3 0 1 0 0 1000 "bridge head" + 28 "=" 0 -1 0 0.4 0.0 brid norm 1.0 1.0 5 -1 1 0 0 100 "bridge span" + 29 "b" 4 bars 100 0.4 0.2 land bank 1.0 2.25 10 0 1 0 0 1000 "bank" + 30 "%" 4 pet 1000 0.4 0.2 land norm 1.0 1.5 2 0 1 0 0 1000 "refinery" + 31 "e" 4 -1 0 0.4 0.2 land norm 1.0 2.0 7 0 1 0 0 1000 "enlistment center" + 32 "~" 32 -1 0 0.4 0.2 land norm 1.0 1.5 1 -1 1 0 0 100 "plains" + 33 "@" 0 -1 0 0.4 0.0 land norm 1.0 1.5 4 -1 1 0 0 100 "bridge tower" /config diff --git a/src/lib/player/dispatch.c b/src/lib/player/dispatch.c index 10d16c17..edf50371 100644 --- a/src/lib/player/dispatch.c +++ b/src/lib/player/dispatch.c @@ -38,6 +38,7 @@ #include "com.h" #include "empio.h" #include "file.h" +#include "journal.h" #include "match.h" #include "misc.h" #include "nat.h" @@ -102,6 +103,7 @@ dispatch(char *buf, char *redir) uprnf(buf); pr("\n"); } + journal_command(command->c_form); switch (command->c_addr()) { case RET_OK: player->btused += command->c_cost; diff --git a/src/lib/player/player.c b/src/lib/player/player.c index f6f1e34e..f87db4da 100644 --- a/src/lib/player/player.c +++ b/src/lib/player/player.c @@ -110,9 +110,8 @@ player_main(struct player *p) } while (status()) { - if (command() == 0 && !player->aborted) - break; - player->aborted = 0; + command(); + player->aborted = player->eof; empth_yield(); } /* #*# I put the following line in to prevent server crash -KHS */ @@ -157,13 +156,9 @@ status(void) int old_nstat, minute; char buf[128]; - if (player->state == PS_SHUTDOWN) + if (player->eof || player->state == PS_SHUTDOWN) return 0; natp = getnatp(player->cnum); - if (io_error(player->iop) || io_eof(player->iop)) { - putnat(natp); - return 0; - } if (player->dolcost > 100.0) pr("That just cost you $%.2f\n", player->dolcost); else if (player->dolcost < -100.0) diff --git a/src/lib/player/recvclient.c b/src/lib/player/recvclient.c index 169a1424..32decde7 100644 --- a/src/lib/player/recvclient.c +++ b/src/lib/player/recvclient.c @@ -46,10 +46,11 @@ * Else receive one line and store it in CMD[SIZE]. * This may block for input, yielding the processor. Flush buffered * output when blocking, to make sure player sees the prompt. - * If the player's connection has the I/O error indicator set, or the - * line is "aborted", set the player's aborted flag and return -2. - * If the player's connection has the EOF indicator set, or the line - * is "ctld", set the player's eof flag and return -1. + * If the player's connection has the I/O error or EOF indicator set, + * or the line is "ctld", set the player's eof and aborted flag and + * return -1. + * If the line is "aborted", set the player's aborted flag and return + * -2. * Else return the length of the line. * Design bug: there is no way to indicate truncation of a long line. */ @@ -59,13 +60,13 @@ recvclient(char *cmd, int size) int count; count = -1; - while (!player->aborted && !player->eof) { + while (!player->aborted) { /* Try to get a line of input */ count = io_gets(player->iop, cmd, size); if (count >= 0) { /* got it */ if (strcmp(cmd, "ctld") == 0) - player->eof = 1; + player->aborted = player->eof = 1; if (strcmp(cmd, "aborted") == 0) player->aborted = 1; journal_input(cmd); @@ -84,16 +85,23 @@ recvclient(char *cmd, int size) /* Await more input */ io_input(player->iop, IO_WAIT); - if (io_error(player->iop)) - player->aborted = 1; - else if (io_eof(player->iop)) - player->eof = 1; + if (io_error(player->iop) || io_eof(player->iop)) + player->aborted = player->eof = 1; } - if (player->eof) - return -1; - if (player->aborted) - return -2; + if (player->aborted) { + player->recvfail++; + if (player->recvfail > 255) { + /* + * Looks like the thread is stuck in a loop that fails to + * check errors; oops once, then slow it down drastically. + */ + CANT_HAPPEN(player->recvfail == 256); + empth_sleep(time(NULL) + 60); + } + return player->eof ? -1 : -2; + } + player->recvfail = 0; return count; } diff --git a/src/lib/subs/aircombat.c b/src/lib/subs/aircombat.c index 11616ee9..ad6d8439 100644 --- a/src/lib/subs/aircombat.c +++ b/src/lib/subs/aircombat.c @@ -770,12 +770,18 @@ ac_doflak(struct emp_qelem *list, struct sctstr *from) gun = MIN(FLAK_GUN_MAX, from->sct_item[I_GUN]); shell = from->sct_item[I_SHELL]; +#if 0 + /* + * FIXME can supply from itself, causing seqno mismatch oops + * further down + */ if (gun > shell * 2) { shell += supply_commod(from->sct_own, from->sct_x, from->sct_y, I_SHELL, (gun + 1) / 2 - shell); from->sct_item[I_SHELL] = shell; putsect(from); } +#endif if (gun > shell * 2) gun = shell * 2; @@ -823,12 +829,19 @@ ac_shipflak(struct emp_qelem *list, coord x, coord y) gun = shp_usable_guns(&ship); if (gun) { shell = ship.shp_item[I_SHELL]; +#if 0 + /* + * FIXME if this supplies the ship while it is being + * interdicted, we get a seqno mismatch when interdiction + * damage is applied. + */ if (shell <= 0) { shell = supply_commod(ship.shp_own, ship.shp_x, ship.shp_y, I_SHELL, 1); ship.shp_item[I_SHELL] = shell; putship(ship.shp_uid, &ship); } +#endif } if (gun == 0 || shell == 0) continue; diff --git a/src/lib/subs/attsub.c b/src/lib/subs/attsub.c index 5183b2a5..c2dacacf 100644 --- a/src/lib/subs/attsub.c +++ b/src/lib/subs/attsub.c @@ -461,6 +461,7 @@ att_abort(int combat_mode, struct combat *off, struct combat *def) if (att_get_combat(off, 0) < 0) return abort_attack(); if (off->type == EF_SHIP && + !(off->x == def->x && off->y == def->y) && (!getsect(off->x, off->y, §) || sect.sct_type != SCT_WATER)) { pr("%s can not %s from that far inland!\n", @@ -1007,6 +1008,12 @@ ask_olist(int combat_mode, struct combat *off, struct combat *def, pr("Land units are not able to board this kind of ship\n"); return; } + if (def->type == EF_SHIP + && (def->shp_mcp->m_flags & (M_SUPPLY | M_SUB)) != M_SUPPLY + && !(lcp->l_flags & L_LIGHT)) { + pr("Only light land units can board this kind of ship\n"); + continue; + } if (land.lnd_mobil <= 0) { pr("%s is out of mobility, and cannot %s\n", prland(&land), att_mode[combat_mode]); @@ -1229,11 +1236,8 @@ get_dlist(struct combat *def, struct emp_qelem *list, int a_spy, continue; if (def->type == EF_LAND && land.lnd_land != def->lnd_uid) continue; - if (!list) { /* Just estimating the enemy strength */ - intelligence_report(player->cnum, &land, a_spy, - "Scouts report defending unit:"); - continue; - } + intelligence_report(player->cnum, &land, a_spy, + "Scouts report defending unit:"); if (!(llp = malloc(sizeof(struct ulist)))) { logerror("Malloc failed in attack!\n"); abort_attack(); diff --git a/src/lib/subs/bridgefall.c b/src/lib/subs/bridgefall.c index f407f32f..624b0a95 100644 --- a/src/lib/subs/bridgefall.c +++ b/src/lib/subs/bridgefall.c @@ -70,7 +70,7 @@ bridge_damaged(struct sctstr *sp) des = sp->sct_type; if (des == SCT_BSPAN || des == SCT_BTOWER) knockdown(sp); - if ((des == SCT_BHEAD || des == SCT_BTOWER) && !opt_EASY_BRIDGES) + if (IS_BRIDGE_HEAD(des) && !opt_EASY_BRIDGES) bridgefall(sp); } @@ -98,17 +98,16 @@ bridgefall(struct sctstr *sp) if (nnx == sp->sct_x && nny == sp->sct_y) continue; getsect(nnx, nny, &bh_sect); - if (bh_sect.sct_type == SCT_BHEAD && - bh_sect.sct_newtype == SCT_BHEAD) - break; - if (bh_sect.sct_type == SCT_BTOWER) - break; /* With EASY_BRIDGES, it just has to be next to any land */ if (opt_EASY_BRIDGES) { if (bh_sect.sct_type != SCT_WATER && bh_sect.sct_type != SCT_BSPAN) break; + } else { + if (IS_BRIDGE_HEAD(bh_sect.sct_type) + && bh_sect.sct_newtype == bh_sect.sct_type) + break; } } if (j > 6) { diff --git a/src/lib/subs/journal.c b/src/lib/subs/journal.c index 7f450524..bc37262c 100644 --- a/src/lib/subs/journal.c +++ b/src/lib/subs/journal.c @@ -43,7 +43,9 @@ * prng NAME SEED * login CNUM HOSTADDR USER * logout CNUM + * command NAME * input INPUT + * output THREAD ID OUTPUT * update ETU */ @@ -64,6 +66,9 @@ static char journal_fname[] = "journal.log"; static FILE *journal; +static void journal_entry(char *fmt, ...) ATTRIBUTE((format (printf, 1, 2))); +static void journal_output_1(struct player *, int, char *, char *, int); + static FILE * journal_open(void) { @@ -80,8 +85,12 @@ journal_entry(char *fmt, ...) if (journal) { time(&now); - fprintf(journal, "%.24s %p ", ctime(&now), empth_self()); - + fprintf(journal, "%.24s ", ctime(&now)); + if (player && player->state == PS_PLAYING) + fprintf(journal, "%d ", player->cnum); + else + fprintf(journal, "%p ", empth_self()); + va_start(ap, fmt); vsnprintf(buf, sizeof(buf) - 1, fmt, ap); va_end(ap); @@ -93,7 +102,8 @@ journal_entry(char *fmt, ...) fprintf(journal, "\\%03o", *p); } fputs("\n", journal); - fflush(journal); + if (fmt[0] != 'o') /* FIXME disgusting hack */ + fflush(journal); if (ferror(journal)) { logerror("Error writing journal (%s)", strerror(errno)); clearerr(journal); @@ -162,12 +172,61 @@ journal_logout(void) journal_entry("logout %d", player->cnum); } +void +journal_output(struct player *pl, int id, char *output) +{ + static char buf[1024]; + static struct player *bpl; + static int bid; + char *s, *e; + + if (keep_journal < 2) + return; + + if (buf[0] && (pl != bpl && id != bid)) { + journal_output_1(bpl, bid, buf, "", -1); + buf[0] = 0; + } + + for (s = output; (e = strchr(s, '\n')); s = e + 1) { + journal_output_1(pl, id, buf, s, (int)(e + 1 - s)); + buf[0] = 0; + } + if (strlen(buf) + strlen(s) < 1024) { + strcpy(buf + strlen(buf), s); + bpl = pl; + bid = id; + } else { + journal_output_1(pl, id, buf, s, -1); + buf[0] = 0; + } +} + +void +journal_output_1(struct player *pl, int id, + char *buf1, char *buf2, int buf2prec) +{ + if (pl && pl->state == PS_PLAYING) + journal_entry("output %d %d %s%.*s", + pl->cnum, id, buf1, buf2prec, buf2); + else + journal_entry("output %p %id %s%.*s", + pl, id, buf1, buf2prec, buf2); +} + void journal_input(char *input) { journal_entry("input %s", input); } +void +journal_command(char *cmd) +{ + char *eptr = strchr(cmd, ' '); + journal_entry("command %.*s", eptr ? (int)(eptr - cmd) : -1, cmd); +} + void journal_update(int etu) { diff --git a/src/lib/subs/landgun.c b/src/lib/subs/landgun.c index 151679a4..160b403d 100644 --- a/src/lib/subs/landgun.c +++ b/src/lib/subs/landgun.c @@ -207,6 +207,7 @@ lnd_fire(struct lndstr *lp) I_SHELL, ammo - shells); if (shells == 0) return -1; + lnd_unlimber(lp); d = landunitgun(lp->lnd_effic, guns); if (shells < ammo) { d *= (double)shells / (double)ammo; @@ -287,7 +288,11 @@ shp_torp_hitchance(struct shpstr *sp, int range) double lnd_fire_range(struct lndstr *lp) { - return effrange(lnd_frg(lp), lp->lnd_tech); + struct sctstr sect; + + getsect(lp->lnd_x, lp->lnd_y, §); + return effrange(lnd_frg(lp), lp->lnd_tech) + + (sect.sct_type == SCT_MOUNT ? 0.5 : 0.0); } int diff --git a/src/lib/subs/lndsub.c b/src/lib/subs/lndsub.c index 395ffcf2..12f8ecc7 100644 --- a/src/lib/subs/lndsub.c +++ b/src/lib/subs/lndsub.c @@ -991,7 +991,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)); @@ -1126,6 +1126,9 @@ lnd_support(natid victim, natid attacker, coord x, coord y, int defending) while (nxtitem(&ni, &land)) { if ((land.lnd_x == x) && (land.lnd_y == y)) continue; + /* Too ponderous for defensive support */ + if (defending && (lchr[(int)land.lnd_type].l_flags & L_HEAVY)) + continue; rel = getrel(getnatp(land.lnd_own), attacker); rel2 = getrel(getnatp(land.lnd_own), victim); if ((land.lnd_own != attacker) && @@ -1170,31 +1173,81 @@ lnd_can_attack(struct lndstr *lp) return 1; } +/* + * Return mobility required to unlimber LP. + */ +static double +lnd_mob_to_unlimber(struct lndstr *lp) +{ + struct lchrstr *lcp = &lchr[(int)lp->lnd_type]; + + if (!opt_LIMBER || lcp->l_dam == 0 || lp->lnd_harden != 0) + return 0; + + if (lnd_spd(lp) < 22 + || (lcp->l_flags & (L_HEAVY | L_TRAIN)) == (L_HEAVY | L_TRAIN)) { + /* + * Slow artillery is towed and needs to be unlimbered. + * Heavy railway guns need to be assembled. + */ + return lnd_pathcost(lp, 0.2); + } + + return 0; +} + +/* + * Unlimber land unit LP. + * No effect unless LP is limbered artillery. + */ +void +lnd_unlimber(struct lndstr *lp) +{ + double unlimber_mob, mult, newmob; + + unlimber_mob = lnd_mob_to_unlimber(lp); + if (unlimber_mob == 0) + return; + + mult = has_helpful_engineer(lp->lnd_x, lp->lnd_y, lp->lnd_own) + ? 1.5 : 1.0; + newmob = lp->lnd_mobil - (1 + unlimber_mob) / mult; + lp->lnd_mobil = (signed char)MAX(-127, floor(newmob)); + CANT_HAPPEN(lp->lnd_harden != 0); + lp->lnd_harden = 1; +} + /* * Increase fortification value of LP. * Fortification costs mobility. Use up to MOB mobility. + * Automatically unlimbers. * Return actual fortification increase. */ int lnd_fortify(struct lndstr *lp, int mob) { int hard_amt; - double mob_used, mult; + double mob_used, unlimber_mob, mult; if (lp->lnd_ship >= 0 || lp->lnd_land >= 0) return 0; mob_used = MIN(lp->lnd_mobil, mob); - if (mob_used < 0) - return 0; - mult = has_helpful_engineer(lp->lnd_x, lp->lnd_y, lp->lnd_own) ? 1.5 : 1.0; + unlimber_mob = lnd_mob_to_unlimber(lp); + + hard_amt = (int)(mob_used * mult - unlimber_mob); + if (hard_amt < 1) + /* + * FIXME unlimber_mob > land_mob_max breaks fortify command + * FIXME > etu_per_update * land_mob_scale breaks auto-fort. at update + */ + return 0; - hard_amt = (int)(mob_used * mult); if (lp->lnd_harden + hard_amt > land_mob_max) { hard_amt = land_mob_max - lp->lnd_harden; - mob_used = ceil(hard_amt / mult); + mob_used = ceil((hard_amt + unlimber_mob) / mult); } lp->lnd_mobil -= (int)mob_used; diff --git a/src/lib/subs/mission.c b/src/lib/subs/mission.c index e038c12c..2f147f47 100644 --- a/src/lib/subs/mission.c +++ b/src/lib/subs/mission.c @@ -344,10 +344,10 @@ build_mission_list_type(struct genlist *mi, coord x, coord y, int mission, if (getrel(getnatp(gp->own), sect.sct_own) > AT_WAR) { /* - * If the player->owner of the unit isn't at war + * If the owner of the unit isn't at war * with the victim, and doesn't own the * sect being acted upon, and isn't the - * old player->owner of that sect, bounce them. + * old owner of that sect, bounce them. */ if (sect.sct_type != SCT_WATER && sect.sct_own != gp->own && @@ -412,6 +412,7 @@ perform_mission(coord x, coord y, natid victim, struct emp_qelem *list, natid plane_owner = 0; int md, range, air_dam = 0; double prb, hitchance, vrange; + int targeting_ships = *s == 's'; /* "subs" or "ships" FIXME gross! */ getsect(x, y, §); @@ -436,6 +437,10 @@ perform_mission(coord x, coord y, natid victim, struct emp_qelem *list, (md > land_max_interdiction_range)) continue; + /* Too ponderous for interdiction fire */ + if (lchr[(int)lp->lnd_type].l_flags & L_HEAVY) + continue; + range = roundrange(lnd_fire_range(lp)); if (md > range) continue; @@ -445,12 +450,12 @@ perform_mission(coord x, coord y, natid victim, struct emp_qelem *list, if (dam2 < 0) continue; - if (sect.sct_type == SCT_WATER) { + if (targeting_ships) { if (chance(lnd_acc(lp) / 100.0)) dam2 = ldround(dam2 / 2.0, 1); } dam += dam2; - if (sect.sct_type == SCT_WATER) + if (targeting_ships) nreport(lp->lnd_own, N_SHP_SHELL, victim, 1); else nreport(lp->lnd_own, N_SCT_SHELL, victim, 1); @@ -487,10 +492,8 @@ perform_mission(coord x, coord y, natid victim, struct emp_qelem *list, continue; } if (mcp->m_flags & M_SUB) { -/* If we aren't shooting at "subs" or "ships" don't fire at all from - a sub. */ - if (*s != 's') - continue; + if (!targeting_ships) + continue; /* subs interdict only ships */ range = roundrange(torprange(sp)); if (md > range) continue; @@ -547,7 +550,7 @@ perform_mission(coord x, coord y, natid victim, struct emp_qelem *list, if (chance(prb)) dam2 /= 2; dam += dam2; - if (sect.sct_type == SCT_WATER) + if (targeting_ships) nreport(sp->shp_own, N_SHP_SHELL, victim, 1); else nreport(sp->shp_own, N_SCT_SHELL, victim, 1); @@ -680,7 +683,7 @@ perform_mission(coord x, coord y, natid victim, struct emp_qelem *list, if (air_dam > 0) { dam += air_dam; - if (sect.sct_type == SCT_WATER) + if (targeting_ships) nreport(plane_owner, N_SHP_BOMB, victim, 1); else nreport(plane_owner, N_SCT_BOMB, victim, 1); diff --git a/src/lib/subs/mslsub.c b/src/lib/subs/mslsub.c index 3345b2a5..ebd5af5b 100644 --- a/src/lib/subs/mslsub.c +++ b/src/lib/subs/mslsub.c @@ -194,6 +194,9 @@ msl_sel(struct emp_qelem *list, coord x, coord y, natid victim, continue; if (mission && plane.pln_mission != mission) continue; + if (mission && + plane.pln_radius < mapdist(x, y, plane.pln_opx, plane.pln_opy)) + continue; if (getrel(getnatp(plane.pln_own), victim) >= NEUTRAL) continue; /* missiles go one way, so we can use all the range */ diff --git a/src/lib/subs/nreport.c b/src/lib/subs/nreport.c index 8a53cbee..2dffb268 100644 --- a/src/lib/subs/nreport.c +++ b/src/lib/subs/nreport.c @@ -111,7 +111,7 @@ delete_old_news(void) for (j = 0; getnews(i + j, &news); j++) { if (news.nws_vrb == 0) break; - news.nws_uid = j; + ef_set_uid(EF_NEWS, &news, j); putnews(j, &news); } CANT_HAPPEN(i + j != news_tail); diff --git a/src/lib/subs/nuke.c b/src/lib/subs/nuke.c index fe71e7ba..363621f3 100644 --- a/src/lib/subs/nuke.c +++ b/src/lib/subs/nuke.c @@ -89,6 +89,9 @@ nuk_prewrite(int n, void *ptr) } getnuke(n, &nuke); + if (nuke.nuk_plane != np->nuk_plane) + logerror("phantom nuke debug: nuk#%d plane %d->%d", + np->nuk_uid, nuke.nuk_plane, np->nuk_plane); return 1; } diff --git a/src/lib/subs/plane.c b/src/lib/subs/plane.c index aaaaf845..17554dd1 100644 --- a/src/lib/subs/plane.c +++ b/src/lib/subs/plane.c @@ -132,6 +132,9 @@ pln_prewrite(int n, void *ptr) } } getplane(n, &plane); + if (plane.pln_nuketype != pp->pln_nuketype) + logerror("phantom nuke debug: pln#%d nuketype %d->%d", + pp->pln_uid, plane.pln_nuketype, pp->pln_nuketype); return 1; } diff --git a/src/lib/subs/plnsub.c b/src/lib/subs/plnsub.c index d2b811f5..9d0037fa 100644 --- a/src/lib/subs/plnsub.c +++ b/src/lib/subs/plnsub.c @@ -559,6 +559,7 @@ pln_arm(struct emp_qelem *list, int dist, char mission, struct ichrstr *ip, next = qp->q_forw; plp = (struct plist *)qp; pp = &plp->plane; + getplane(pp->pln_uid, pp); if ((pp->pln_flags & PLN_LAUNCHED) || pln_equip(plp, ip, flags, mission) < 0) { emp_remque(qp); diff --git a/src/lib/subs/pr.c b/src/lib/subs/pr.c index 711dd3f4..496bde49 100644 --- a/src/lib/subs/pr.c +++ b/src/lib/subs/pr.c @@ -56,6 +56,7 @@ #include "com.h" #include "empio.h" #include "file.h" +#include "journal.h" #include "misc.h" #include "nat.h" #include "player.h" @@ -124,6 +125,7 @@ pr_id(struct player *p, int id, char *format, ...) if (p->curid >= 0) { io_puts(p->iop, "\n"); + journal_output(p, p->curid, "\n"); p->curid = -1; } va_start(ap, format); @@ -245,6 +247,7 @@ pr_player(struct player *pl, int id, char *buf) bp += len; } } + journal_output(pl, id, buf); } /* @@ -299,6 +302,7 @@ upr_player(struct player *pl, int id, char *buf) io_puts(pl->iop, printbuf); } } + journal_output(pl, id, buf); } /* diff --git a/src/lib/subs/retreat.c b/src/lib/subs/retreat.c index 57eceeae..b3fb5ef7 100644 --- a/src/lib/subs/retreat.c +++ b/src/lib/subs/retreat.c @@ -251,6 +251,7 @@ retreat_ship1(struct shpstr *sp, char code, int orig) getsect(newx, newy, §); if (shp_check_nav(§, sp) != CN_NAVIGABLE || (sect.sct_own && sect.sct_own != sp->shp_own && + sect.sct_type != SCT_BSPAN && getrel(getnatp(sect.sct_own), sp->shp_own) < FRIENDLY)) { wu(0, sp->shp_own, "%s %s,\nbut could not retreat to %s!\n", prship(sp), conditions[findcondition(code)].desc[orig], diff --git a/src/lib/subs/satmap.c b/src/lib/subs/satmap.c index cc4b06db..15a06020 100644 --- a/src/lib/subs/satmap.c +++ b/src/lib/subs/satmap.c @@ -177,15 +177,8 @@ satmap(int x, int y, int eff, int range, int flags, int type) } /* If we are imaging *and* drawing the map */ if ((flags & P_I) && (type == EF_BAD)) { - /* Figure out where to put the ship */ - /* First, figure out the distance from the two */ - rx = diffx((int)ship.shp_x, x); - ry = diffy((int)ship.shp_y, y); - /* Next, determine which direction to add it to the center */ - /* We can only do this if imaging and we have gotten the center - up above by imaging the sectors. */ - rx = deltax(x, ns.range.lx) + rx; - ry = deltay(y, ns.range.ly) + ry; + rx = deltx(&ns.range, ship.shp_x); + ry = delty(&ns.range, ship.shp_y); /* &~0x20 makes it a cap letter */ rad[ry][rx] = (*mchr[(int)ship.shp_type].m_name) & ~0x20; } @@ -230,15 +223,8 @@ satmap(int x, int y, int eff, int range, int flags, int type) } /* If we are imaging *and* drawing the map */ if ((flags & P_I) && (type == EF_BAD)) { - /* Figure out where to put the unit */ - /* First, figure out the distance from the two */ - rx = diffx((int)land.lnd_x, x); - ry = diffy((int)land.lnd_y, y); - /* Next, determine which direction to add it to the center */ - /* We can only do this if imaging and we have gotten the center - up above by imaging the sectors. */ - rx = deltax(x, ns.range.lx) + rx; - ry = deltay(y, ns.range.ly) + ry; + rx = deltx(&ns.range, land.lnd_x); + ry = delty(&ns.range, land.lnd_y); /* &~0x20 makes it a cap letter */ rad[ry][rx] = (*lchr[(int)land.lnd_type].l_name) & ~0x20; } @@ -285,7 +271,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), diff --git a/src/lib/subs/shpsub.c b/src/lib/subs/shpsub.c index 7ceebb7a..9a97c2c5 100644 --- a/src/lib/subs/shpsub.c +++ b/src/lib/subs/shpsub.c @@ -237,13 +237,13 @@ shp_sweep(struct emp_qelem *ship_list, int verbose, int takemob, natid actor) } sect.sct_mines = mines; mlp->unit.ship.shp_item[I_SHELL] = shells; + putship(mlp->unit.ship.shp_uid, &mlp->unit.ship); + putsect(§); if (shp_check_one_mines(mlp)) { stopping = 1; emp_remque(qp); free(qp); } - putship(mlp->unit.ship.shp_uid, &mlp->unit.ship); - putsect(§); } if (changed) writemap(actor); @@ -753,6 +753,7 @@ shp_nav_one_sector(struct emp_qelem *list, int dir, natid actor, navigate = shp_check_nav(§, &mlp->unit.ship); if (navigate != CN_NAVIGABLE || (sect.sct_own && actor != sect.sct_own && + sect.sct_type != SCT_BSPAN && getrel(getnatp(sect.sct_own), actor) < FRIENDLY)) { if (dchr[sect.sct_type].d_nav == NAV_CANAL && !(((struct mchrstr *)mlp->chrp)->m_flags & M_CANAL) && @@ -867,12 +868,18 @@ shp_missile_defense(coord dx, coord dy, natid bombown, int hardtarget) shell = ship.shp_item[I_SHELL]; if (ship.shp_item[I_MILIT] < 1) /* do we have mil? */ continue; +#if 0 + /* + * FIXME can supply from itself, causing seqno mismatch oops + * further down + */ if (shell < 2) { /* do we need shells */ shell += supply_commod(ship.shp_own, ship.shp_x, ship.shp_y, I_SHELL, 2); if (shell < 2) continue; } +#endif if (ship.shp_item[I_GUN] < 1) /* we need at least 1 gun */ continue; diff --git a/src/lib/subs/supply.c b/src/lib/subs/supply.c index 96b2d2cc..ed5ae769 100644 --- a/src/lib/subs/supply.c +++ b/src/lib/subs/supply.c @@ -124,7 +124,7 @@ resupply_commod(struct lndstr *lp, i_type type) int supply_commod(int own, int x, int y, i_type type, int total_wanted) { - if (total_wanted < 0) + if (total_wanted <= 0) return 0; return s_commod(own, x, y, type, total_wanted, !player->simulation); } @@ -135,7 +135,7 @@ supply_commod(int own, int x, int y, i_type type, int total_wanted) static int try_supply_commod(int own, int x, int y, i_type type, int total_wanted) { - if (total_wanted < 0) + if (total_wanted <= 0) return 0; return s_commod(own, x, y, type, total_wanted, 0); @@ -361,6 +361,7 @@ s_commod(int own, int x, int y, i_type type, int total_wanted, save = land; land.lnd_item[type] = 0; putland(land.lnd_uid, &land); + save.lnd_seqno = land.lnd_seqno; land.lnd_item[type] = save.lnd_item[type] + s_commod(own, land.lnd_x, land.lnd_y, diff --git a/src/lib/update/age.c b/src/lib/update/age.c index 29dca549..98cecaab 100644 --- a/src/lib/update/age.c +++ b/src/lib/update/age.c @@ -35,6 +35,13 @@ #include "update.h" +int +age_people(int n, int etu) +{ + /* age by 1% per 24 etus */ + return roundavg(n * (1.0 - etu / 2400.0)); +} + void age_levels(int etu) { @@ -43,7 +50,6 @@ age_levels(int etu) int i; double level; double delta; - int deltares; best_tech = 0.0; best_res = 0.0; @@ -61,14 +67,7 @@ age_levels(int etu) delta = np->nat_level[NAT_TLEV] * etu / (100 * level_age_rate); np->nat_level[NAT_TLEV] -= delta; } - /* - * age reserves by 1% per every 24 etus - */ - deltares = -roundavg(np->nat_reserve * etu / 2400.0); - if (deltares != 0) - np->nat_reserve += deltares; - /* Chad Zabel - above number is negative ( was a -= there - which was wrong. */ + np->nat_reserve = age_people(np->nat_reserve, etu); } best_tech /= 5; best_res /= 5; diff --git a/src/lib/update/human.c b/src/lib/update/human.c index 1a7b1c5f..d36b9e15 100644 --- a/src/lib/update/human.c +++ b/src/lib/update/human.c @@ -61,6 +61,7 @@ do_feed(struct sctstr *sp, struct natstr *np, short *vec, int starved, sctwork; int needed; int maxpop; + int manna; /* grow people & stuff */ sctwork = sp->sct_work; @@ -72,12 +73,16 @@ do_feed(struct sctstr *sp, struct natstr *np, short *vec, maxpop)); if (sp->sct_type != SCT_SANCT) { + manna = 0; if (opt_NOFOOD == 0) { needed = (int)ceil(food_needed(vec, etu)); if (vec[I_FOOD] < needed) { /* need to grow "emergency rations" */ work_avail -= 2 * growfood(sp, vec, work_avail / 2, etu); /* It's twice as hard to grow those than norm */ + if (vec[I_FOOD] == 0) + /* Conjure up 1f to make life easier for the player */ + manna = vec[I_FOOD] = 1; } if (vec[I_FOOD] < needed && sp->sct_own == sp->sct_oldown) { /* steal food from warehouses, headquarters, @@ -109,7 +114,21 @@ do_feed(struct sctstr *sp, struct natstr *np, short *vec, if (!player->simulation) sp->sct_work = sctwork; grow_people(sp, etu, np, &work_avail, sctwork, vec); + /* age che */ + if (!player->simulation) { + int oldche = sp->sct_che; + sp->sct_che = age_people(sp->sct_che, etu); + if (sp->sct_che_target == sp->sct_own && sp->sct_loyal < 40) + /* make them fade away eventually, for playability */ + sp->sct_che /= 2; + if (sp->sct_che != oldche) + logerror("che in %d,%d aged from %d to %d", + sp->sct_x, sp->sct_y, oldche, sp->sct_che); + } } + if (manna) + /* Take away food we conjured up */ + vec[I_FOOD] = 0; } else sctwork = sp->sct_work = 100; /* Here is where we truncate extra people, always */ @@ -141,13 +160,7 @@ growfood(struct sctstr *sp, short *vec, int work, int etu) food = MIN(food_workers, food_fertil); if (food > ITEM_MAX - vec[I_FOOD]) food = ITEM_MAX - vec[I_FOOD]; - /* - * Be nice; grow minimum one food unit. - * This makes life simpler for the player. - */ vec[I_FOOD] += food; - if (vec[I_FOOD] == 0) - vec[I_FOOD] = 1; work_used = food / fcrate; return work_used; } diff --git a/src/lib/update/ship.c b/src/lib/update/ship.c index 59546ddb..cccbe260 100644 --- a/src/lib/update/ship.c +++ b/src/lib/update/ship.c @@ -157,7 +157,8 @@ upd_ship(struct shpstr *sp, int etus, /* produce oil */ if (np->nat_money >= 0 - && (mp->m_flags & M_OIL) && sectp->sct_type == SCT_WATER) { + && (mp->m_flags & M_OIL) && sectp->sct_type == SCT_WATER + && sp->shp_mobil >= ship_mob_max) { product = &pchr[dchr[SCT_OIL].d_prd]; oil_gained = roundavg(total_work(100, etus, sp->shp_item[I_CIVIL], diff --git a/src/util/fairland.c b/src/util/fairland.c index 8a5696d1..6e2e8eb3 100644 --- a/src/util/fairland.c +++ b/src/util/fairland.c @@ -1098,6 +1098,7 @@ write_sects(void) sct->sct_type = SCT_MOUNT; sct->sct_elev = total; sct->sct_newtype = sct->sct_type; + sct->sct_dterr = own[sct->sct_x][y] + 1; if (ORE) add_resources(sct); sct++; diff --git a/src/util/files.c b/src/util/files.c index 4618ba6e..a7c802b7 100644 --- a/src/util/files.c +++ b/src/util/files.c @@ -155,6 +155,7 @@ main(int argc, char *argv[]) for (i = 1; i < MAXNOC; i++) { nat.ef_type = EF_NATION; nat.nat_cnum = nat.nat_uid = i; + nat.nat_seqno = 0; putnat((&nat)); } memset(&realm, 0, sizeof(realm)); @@ -164,6 +165,7 @@ main(int argc, char *argv[]) for (j = 0; j < MAXNOR; j++) { realm.r_realm = j; realm.r_uid = (i * MAXNOR) + j; + realm.r_seqno = 0; putrealm(&realm); } } @@ -209,6 +211,7 @@ file_sct_init(coord x, coord y, struct sctstr *ptr) sp->ef_type = EF_SECTOR; sp->sct_uid = XYOFFSET(x, y); + sp->sct_seqno = 0; sp->sct_x = x; sp->sct_y = y; sp->sct_dist_x = x;