New tel_read_header(), tel_read_body(). Use them in rea(),
show_first_tel(), copy_and_expire().
rea() now stops when it encounters a corrupt telegram, and logs the
problem. Before, error detection was incomplete, and errors were not
logged. Corrupt mailboxes could make it crash.
show_first_tel() and copy_and_expire() can now cope with telegrams of
arbitrary length, like rea(), and sanity-check the header fields they
don't actually use.
Move the gamedown() check from status to may_play_now() so it is
checked upon login, before a command is executed and after command
completion. This fixes the situation where a player to could execute
one more command after the game was down.
Report to the deities that the game is down.
Remove to duplicate gamedown message.
The ancients designed interception dead simple: when you overfly a
sector, you get intercepted by the sector owner. Fine print
interception rules govern which planes intercept.
Then complexity got piled on top of it.
Chainsaw 2 added an extra interception by surface ship owners, in the
target sector only.
Chainsaw 3 added an extra interception by land unit owners, in the
target sector only (Empire 4 later merged this extra land unit
interception with the extra surface ship interception).
Chainsaw 3 added an entirely separate kind of interception: air
defense missions. When you enter a sector in some air defense op
area, you get intercepted. Fine print air defense rules govern which
planes intercept. These rules differ significantly from the
interception fine print.
Additional complexity comes from these facts:
* Air defense mission interception happens in addition to non-mission
interception. You can boost your total interception by setting up
air defense. Which means you must set it up, or forgo an advantage.
* Air defense planes are not available for non-mission interception
duty. You need to decide on a split.
* In contrast to non-mission interception, interceptors flying air
defense get intercepted.
Moreover, the air defense code breaks one of the plane code's design
assumptions, namely that just one plane sortie is active at a time.
The air defense sortie runs while the sortie it intercepts is in
progress. This leads to two interceptions being active at the same
time: the one intercepting the original sortie, and the one
intercepting the air defense sortie. The same plane can fly in both
interceptions, and damage received in the interception of the air
defense sortie is wiped out, triggering a seqno mismatch oops.
The previous commit already simplified non-mission interception: you
get intercepted by anyone who owns the sector, or a surface ship or a
land unit there, whether it's the target sector or not.
Now simplify mission interception, by merging air defense back into
ordinary interception: when you overfly a sector, you get intercepted
by anyone who owns the sector, or a surface ship or land unit there,
or has an air defense mission covering the sector. That's all. No
multiple interceptions, no separate air defense rules.
Remove air_defense(). Simplify ac_encounter() and sam_intercept()
accordingly; both lose their last parameter.
Change sam_intercept() and ac_intercept() to intercept in mission op
areas. New parameter only_mission to suppress non-mission
interception. Pass zero when the intercepting country owns the sector
or a surface ship or land unit in the sector.
ac_encounter() can't efficiently predict whether a country intercepts,
so it needs to call ac_intercept() unconditionally. This kills the
optimization to collect interceptors only when needed; simplify
accordingly, replacing getilist() by getilists().
Since the previous two commits, ac_encounter() checks its
mission_flags argument only for proper mission flags PM_R and PM_S,
not for plane flags P_A, P_S, P_I.
This makes the code to put plane flags into mission flags useless.
Remove it from bomb(), drop(), fly(), para(), reco(),
perform_mission(), mission_pln_arm(), air_defense(), pln_arm().
Much of that code was useless even before: P_X and P_H since Chainsaw
3 option STEALTHV became mandatory in Empire 2, and P_MINE since
commit cc0c3e4f (v4.3.0) cleaned up mine drops.
Remove the KillIdle thread. Add timeout to struct iop, initialized in
io_open(). Obey it in io_input() by passing it to empth_select(). If
empth_select() times out, report that back through io_input() to
recvclient() and player_login(). If player_login() receives a timeout
indication, print a message and terminate the session. If
recvclient() receives a timeout indication, flash a message to the
player and initiate a shut down the player's session.
Create WIN32 sys/time.h to define struct timeval. This creates some
conflicts with WIN32 windows.h definitions. Including windows.h in
show.c and info.c creates conflicts, so remove that. Modify service.c
to include sys/socket.h instead of windows.h to remove the conflict
with sys/time.h.
Add check to ensure a country by that name does not exist.
Ensure the length is not too long. Note this is a change
behaviour for edit and change commands which used to silently
truncate long names. Enforce that a country name can not have
control characters in it. Ensure that a country name is not
blank or just spaces.
Change nstr_mkselval() to generate values with promoted types only,
and replace nstr_coerce_val() by new and simpler nstr_optype() in
nstr_comp().
Replace the only remaining use of nstr_coerce_val() in surv() by
nstr_promote(), and remove nstr_coerce_val().
This loses one half of the unimplemented sketch of coercions to
NSC_STRING. Drop the other half from nstr_exec_val().
Land unit reactions are overly complex because we have two different
concepts controlling them: reaction radius (set with lrange) and
reserve mission (set with mission). You need to deal with both to set
up or query reactions.
Commit 8d0e1af5 "fixed" this by making reserve missions meaningless.
The previous commit made reserve missions meaningful again: they
support an op-area now. This brought back the problem of having to
deal with two separate commands to accomplish one thing.
Fix this for good by removing non-mission land unit reaction
alltogether. The only feature we lose by that is the ability to order
land units to react until the order is explicitely cancelled. That's
because missions are implicitely cleared by many commands and events,
while non-mission reaction wasn't. Closes#858121 and #858122.
Remove the non-mission reaction case from att_reacting_units().
Don't limit reserve missions to the land unit's reaction radius: make
lnd_reaction_range() return the type's maximum radius instead of
lnd_rad_max.
The reaction radius is now useless. Remove the lrange command, and
struct lndstr member lnd_rad_max along with its selector react.
Remove land command's column rd. Make ldump show column react as
zero. Deprecate edit key 'P' in dounit(), and don't show it in
pr_land().
Before, they always reacted to their maximum range, and the op-area
was unused. Change mission() to define the op-area for reserve
missions as well. Remove the special-case for showing reserve
missions from mission() and show_mission(). New lnd_reaction_range()
factored out of att_reacting_units(). Use it in oprange() to cover
reserve missions. Pass the mission as separate parameter to oprange()
for now, because miss() doesn't set it in the object until later.
The mission command limits op area radius to the possible range.
That's okay, as it doesn't actually restrict possible op areas. When
the mission is executed, it was limited again. Don't do that; remove
the limiting code from build_mission_list_type() and show_mission().
The removed limiting had no effect, except when the range shrunk.
Then limiting reduced the op area more than necessary. For instance,
consider an object O with initial range 3 on a mission around M with
range 3:
- - - y - - -
- - z y - -
- - z x y - -
- O x x M -
- - z x y - -
- - z y - -
- - - y - - -
Initially, all sectors not marked - are in range and in the op area.
If the range drops to two, sectors marked O, x and z are still in
range of O. But only the x are still in range of M. The O and z got
excluded.
Range can currently shrink when plane range is reduced (range
command), or a ship, plane or land unit somehow loses tech (deity
intervention).
Replace daychange() and gettimeleft() by update_timeused_login(),
update_timeused() and enforce_minimum_session_time(). The new
code doesn't assume the day is always 24 hours long which can
occur when transitioning into or out of DST and such. Logging
in after more a multiple of 128 days now resets nat_timeused
properly.
Fix nat_timeused calculation on midnight rollover to include
the time since midnight.
struct natstr member nat_dayno and struct player member timeleft
are now unused, remove them.
With RAILWAYS, highway-like sectors double as rail. They need to be
at least 5% efficient to be operational, and then they additionally
extend rail into adjacent sectors that are at least 60% efficient.
New opt_RAILWAYS, SCT_HAS_RAIL(), sct_rail_track(). Update
sector_mcost(), bp_neighbors(), lnd_mar_one_sector() for RAILWAYS
mobility rules. Update sinfra(), spyline(), satdisp_sect() to show
rail track instead of rail infrastructure for RAILWAYS.
New virtual sector selector track, implemented by nsc_sct_track().
A sector type's terrain (struct dchrstr member d_terrain) is the
sector type of its underlying terrain. Sector types occuring in
d_terrain are terrain types, and must have their own type in
d_terrain. Players can change sector types only to those with the
same terrain.
The builtin configuration defines terrain types sea, mountain,
wasteland, wilderness and plains. It gives bridge span and tower
terrain sea, and everything else terrain wilderness. Hence, the stock
game remains unchanged.
Deities can use terrain to create sector types that can be developed
only in limited ways.
This simplifies things. In particular, it gets rid of random rounding
in getcommand(), which created a variation in the nightly build
depending on whether the update starts before or after the deity logs
out.
Replace struct natstr member nat_minused by nat_timeused, and update
cou_ca[] accordingly (this affects xdump nat). Replace player member
minleft by timeleft, and getminleft() by gettimeleft(). Update
getcommand(), daychange(), player_main(), status() accordingly, taking
care not to change player output. Change edit country key 'u' to work
in seconds.
Planes and land units on ships are sold along with the ship, but trade
showed them only when SHOWPLANE was enabled. Show them always.
Planes on land units are not sold along with the land unit, but trade
showed them when SHOWPLANE was enabled. Don't.
Use new unit_wipe_orders() for violent takeover (takeover_unit() on
behalf of assault, attack, board, lboard, paradrop and pboard), and
peaceful takeover (unit_give_away() on behalf of arm, disarm, load,
unload, lload, lunload, scrap, scuttle, tend, trade).
Before, takeover_unit() cleared only group, mission and ship retreat
orders, and unit_give_away() only group and mission. Orders that
weren't cleared:
* Mission op area (visible in xdump)
* Ship autonav orders
* Ship sail path including ship to follow and mobility quota
* Plane range limit
* Land unit retreat orders and retreat percentage
When giving away cargo by scrapping or scuttling its carrier, the
cargo's cargo wasn't given away. Happened for instance when a ship
carrying a land unit carrying a SAM got scrapped.
Also, wing, army and mission weren't cleared.
To fix, create unit_give_away() and use it in unit_drop_cargo().
Factor unit_drop_cargo() out of scra(), scuttle_ship(),
scuttle_land(), fix it up:
* Some messages were sent as bulletins instead of printing them.
* Nukes were always destroyed. They're now treated exactly like other
cargo.
* scuttle destroyed some cargo silently, and listed other cargo as
"scuttled". It now simply lets unit_update_cargo() running from
carrier prewrite callbacks list all cargo "lost".
Simplify its callers. scuttle_ship() and scuttle_land() are now
trivial, inline and remove.
The old code did not move a carrier's cargo (planes, land units,
nukes) when the carrier moved. Instead, it fixed up the location in
the postread callback. Anything not going through ef_read(), in
particular the update, saw it in its old, incorrect location, until a
fixed up copy got written back.
Moreover, the timestamp did not change when cargo moved, so
incremental dumps did not pick up the movement.
The new code moves the cargo along with the carrier.
New unit_update_cargo() moves or destroys a carrier's cargo (planes,
land units, nukes) along with the carrier. Call it from
shp_prewrite(), pln_prewrite() and lnd_prewrite() when the carrier
moves or gets destroyed.
Remove the code to destroy cargo from shp_prewrite(), pln_prewrite(),
lnd_prewrite().
Remove the code to fix up cargo location from pln_postread(),
lnd_postread(), nuk_postread().
This changes the message for ship and land unit cargo getting
destroyed from "sunk" and "MIA" to "lost".
New snxtitem_cargo() initializes an iterator for a cargo list, with
new enum ns_seltype member NS_GROUP and new struct nstr_item member
next. Extend nxtitem() and nxtitemp() to step through the list.
Members read were always set to ef_read, remove and call directly.
Member flag was only assigned to, never used, remove.
Change member group to char to match struct empobj.
Load counters are redundant; they can be computed from the carrier
uids. Keeping them up-to-date as the carriers change is a pain, and
we never got that quite complete.
Computing load counters straight from the carrier uids every time we
need them would be rather inefficient, but computing them from cargo
lists is not. So do that.
Remove the load counters: struct shpstr members shp_nplane,
shp_nchoppers, shp_nxlight, shp_nland, and struct lndstr members
lnd_nxlight and lnd_nland.
Don't compute/update load counters in build_ship(), build_land(),
land(), ldump(), load_plane_ship(), load_land_ship(),
load_plane_land(), load_land_land(), lstat(), sdump(), shi(), sstat(),
tend_land(), check_trade(), put_combat(), pln_oneway_to_carrier_ok),
pln_newlanding(), fit_plane_on_ship(), fit_plane_on_land(),
unit_list().
Nothing left in fit_plane_off_ship(), fit_plane_off_land(), so remove
them.
load_land_ship(), load_land_land(), check_trade(), pln_newlanding(),
put_plane_on_ship(), take_plane_off_ship(), put_plane_on_land(),
take_plane_off_land() no longer change the carrier, so don't put it.
Remove functions to recompute the load counters from carrier uids:
count_units(), lnd_count_units(), count_planes(), count_land_planes(),
pln_fixup() and lnd_fixup(), along with the latter two's private
copies of fit_plane_on_ship() and fit_plane_on_land().
New cargo list functions to compute load counts: unit_cargo_count()
and unit_nplane(), with convenience wrappers shp_nplane(),
shp_nland(), lnd_nxlight(), lnd_nland().
Use them to make ship selectors nplane, nchoppers, nxlight, nland
virtual. They now reflect what is loaded, not how the load uses the
available slots. This makes a difference when x-light planes or
choppers use plane slots.
Use them to make land unit selectors nxlight and nland virtual.
Use them to get load counts in land(), ldump(), load_plane_ship(),
load_land_ship(), load_plane_land(), load_land_land(), sdump(), shi(),
tend_land(), fit_plane_on_land(), trade_desc(), unit_list().
Rewrite fit_plane_on_ship() and could_be_on_ship() to use
shp_nplane(). could_be_on_ship() now takes load count arguments, as
computed by shp_nplane(), so it can be used for checking against an
existing load as well.
pln_nuktype is redundant; it can be computed from the nuke's
nuk_plane.
Make plane selector nuketype virtual and NSC_EXTRA. It should have
been NSC_EXTRA all along. This changes xdump plane.
Don't set it in arm(), disarm(), build_plane(), pln_damage() and
nuk_fixup(). The latter no longer does anything, remove it.
Deprecate edit key 'n' in doplane(), and don't show it in pr_plane().
The key never made much sense.
eff_bomb(), comm_bomb(), ship_bomb(), plane_bomb(), land_bomb(),
strat_bomb(), mission_pln_equip(), air_damage(), msl_hit(),
pln_equip() tested pln_nuketype to check whether a plane carries a
nuke. Test nuk_on_plane() instead.
pdump(), plan(), trade_desc() print whether and what kind of nuke a
plane carries. Adapt that to use nuk_on_plane().
Persistent game state encodes "who carries what" by storing the
carrier uid in the cargo. Cargo lists augment that: they store lists
of cargo for each carrier. They are not persistent.
New unit_cargo_init() to compute the cargo lists from game state.
Call it in ef_init_srv() and at the end of update_main().
New unit_onresize() to resize the cargo list data structure.
Installed as units' struct empfile callback onresize to make them
resize automatically with the unit files.
New unit_carrier_change() to update cargo lists when carriers change
in game state. Convenience wrappers pln_carrier_change(),
lnd_carrier_change() and nuk_carrier_change(). Call them from
prewrite callbacks to keep cargo lists in sync with game state.
To make that work, unused units must not point to a carrier. Add new
pln_oninit(), lnd_oninit() and nuk_oninit() take care of newly created
units. Change lnd_prewrite() and nuk_prewrite() to take dead land
units and nukes off their carrier. pln_prewrite() did that already.
New unit_cargo_first(), unit_cargo_next() to traverse cargo lists.
Convenience wrappers lnd_first_on_ship(), lnd_first_on_land(),
lnd_next_on_unit(), pln_first_on_ship(), pln_first_on_land(),
pln_next_on_unit() and nuk_on_plane(). The latter is disabled for now
because it clashes with an existing function.