Before Empire 2, nukes could be delivered only with bomb (special
mission 'n', airburst only) and launch (targeting sectors or
satellites only).
Empire 2 made nukes available for any kind of bombing, and for any
missile strike on sectors or ships. This included interdiction and
support missions. Nuclear-tipped anti-sats and bomb mission n were
removed.
Unfortunately, this was done in a messy way, which led to
inconsistencies and bugs. The problem is that ordinary damage affects
just the target, while nuke damage affects an area. Code dealing with
plane damage was designed for the former. Instead of rewriting it to
cope with area damage cleanly, nuke damage got shoehorned into
pln_damage(), the function to compute conventional plane damage, as a
side effect: computing damage blasted sectors in the area.
If the plane carried a nuke, pln_damage() returned zero (conventional)
damage. Without further logic, this simply bypassed the code to apply
damage to the target. This worked out okay when the target already
got damaged correctly by the side effect.
However, some targets are immune to the side effect: when interdicting
a move or explore command, the commodities being moved are not in any
sector.
For other targets, damage has effects other than damaging the target:
offensive and defensive support don't apply the (conventional) damage
to the target sector. Instead, they turn it into a combat bonus.
Without further logic, nuclear damage doesn't contribute to that.
To make all that work, pln_damage() returned the nuclear damage for
ground zero as well. Because a plane does either conventional or
nuclear damage, one of them is always zero.
Most callers simply ignored the nuclear damage, and applied only the
conventional damage.
Bug: land units and ships failed to retreat when pin-bombed or
missiled with a nuke. That's because they received zero conventional
damage.
The mission code flies planes and missiles and tallies their damage.
This mission damage included nuclear damage at ground zero (except for
missiles sometimes, see below), to make support and commodity
interdiction work. Unfortunately, this broke other things.
Bug: when bombers interdicted ships or land units, nukes first damaged
the ships or land units by the side effect, then again through mission
damage. Interdicting missiles had a special case to avoid this.
Bug: when interdicting move, explore or transport, nukes first damaged
the sector by the side effect, then again through mission damage's
collateral damage.
There may well be more bugs hiding in this mess.
The mess is not worth fixing. While the idea of interdicting and
supporting with nukes sounds kind of cool, I believe it's pretty
irrelevant in actual play.
Instead, go back to a variation of the original rules: nukes can be
delivered only through bomb mission 's' and launch at sectors.
Make arm reject marine missiles in addition to satellites, ABMs and
SAMs, and clear the mission. Make mission reject planes armed with
nukes. Oops when they show up in mission_pln_equip() anyway.
Make pln_equip() allow planes with nukes only for missions 's' and
't'.
Clean up pln_damage() to just compute damage, without side effect.
Change strat_bomb() and launch_missile() to detonate nukes. Simplify
the other callers. Parameter mission of msl_launch_mindam() is now
unused, remove it.
Missiles exploding on launch no longer set off their nukes. That was
pretty ridiculous anyway.
Pinpointed assignments within if conditionals with spatch -sp_file
tests/bad_assign.cocci (from coccinelle-0.1.4). Cherry-picked diff
hunks affecting conditionals split over multiple lines, and cleaned
them up.
getsect() can fail here only when the coordinates are invalid. The
first check uses coordinates from a successful sarg_xy(), so they
can't be invalid. The second check uses coordinates of an object to
be put on the mission. If these are invalid, game state is corrupt,
and failing the mission command doesn't improve the situation a bit.
Before, the escort mission didn't support an op-area, and planes on
escort mission escorted anywhere. Change mission() to define the
op-area for escort missions as well. Show it in mission() and
show_mission(). Check it in find_escorts().
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).
The old code used getstarg() to get an argument with a different
prompt than snxtitem() uses, then passed the value to snxtitem()
unchecked. If the player aborts, getstarg() returns a null pointer,
and snxtitem() prompts again. Affected:
* load/lload plane/land third argument; load_plane_ship(),
load_land_ship(), load_plane_land(), load_land_land()
* bomb, drop, fly, paradrop, recon and sweep second argument;
get_planes()
* tend and ltend second and fourth argument; ltend(), tend(),
tend_land()
* mission second argument; mission()
Fix by making snxtitem() taking a prompt argument, null pointer
requests the old prompt.
Use that to simplify multifire() and torp(). Change the other callers
to pass NULL.
This takes care of a number of bugs / inconsistencies:
* Ships with zero firing range could return fire and fire support, but
not fire actively or interdict. Fix by testing for gun limit
instead in multifire() and mission(). No ships in the stock game
are affected.
* Required gun crew was inconsistent: multifire() let N military fire
max(1,floor(N/2)) guns for active fire. Ditto perform_mission() for
interdiction. quiet_bigdef() let them fire N guns for returning gun
fire. Ditto sd() for firing support and firing at boarding parties.
fire_dchrg() let them fire floor(N/2) for returning fire to
torpedoes. Unify to let N military fire floor((N+1)/2) guns.
* Shell use was inconsistent: sd() and perform_mission() used one
shell per gun, everything else one per two guns. Unify to one shell
per two guns.
* Shell resupply bugs: multifire() got two shells regardless of actual
ammo use. quiet_bigdef() got one shell (but use_ammo() uses only
one, which is a bug). sd() and perform_mission() resupplied before
checking all other requirements and could thus get more shells than
actually needed.
put_empobj() used struct empobj member uid, which is valid only for
units. Existing users pass only units, fortunately. Fix by making it
take type and uid parameters.
(get_empobj_chr): New. Get characteristics for empobj.
(mission, build_mission_list_type): Switch to use new
functions put_empobj() and get_empobj_chr().
other. Ensure headers in include/ can be included in any order
(except for econfig-spec.h, which is special). New header types.h to
help avoid inclusion cycles. Sort include directives. Remove some
superflous includes.
type parameter as it can be determined from the empobj parameter.
(oprange): Remove getting the unit information again as it is contained
in the empobj parameter passed in.
oprange, show_mission, nameofitem, build_mission_list_type,
unit_map, xdvisible, trdswitchown, ontradingblock, trad, check_trade,
unit_type_name, start_stop_unit, scut, scra, mission, multifire,
perform_mission, fuel, NSC_GENITEM): Replace struct genitem with
struct empobj. Remove genitem.h and create a new file empobj.h.
Replace multiple instances of unions of ef_type structures with
one standard union empobj_storage which is a superset of the individual
instances.
(ltend, multifire, quite_bigdef, mine, landmine)
(do_loan, prod, printdiff, sell, sona, stre)
(tend, fire_dchrg, vers, work, ac_planedamage)
(ac_shipflak, ask_off, get_mine_dsupport, att_fight)
(ask_move_in_off, detonate, sd, land_gun)
(land_unitgun, lnd_fort_interdiction, lnd_fortify)
(perform_mission, pln_mine, pln_mobcost)
(retreat_ship1, retreat_land1, shp_sweep)
(shp_fort_interdiction, shp_missle_defense)
(new_work, growfood, upd_land, land_repair)
(get_materials, do_mob_ship, do_mob_land)
(load_it, unload_it, prod_plane, produce)
(guerrilla, upd_buildeff, spread_fallout)
(upd_ship, ship_repair, min, dmin, MIN):
Remove min() and dmin() functions and replace
with a MIN macro in misc.h. Remove local MIN
macros and use the new one in misc.h. This
change removes the need for the special
case for _WIN32.
(fuel, look_ship, multifire, mission, sona)
(plane_sona, ef_open, player_accept, player_main)
(ac_dog, att_get_combat, calc_mobcost)
(ask_move_in_off, intelligence_report)
(build_mission_list_type, perform_mission)
(show_mission, use_supply, dodistribute)
(allocate_memory, max, dmax, MAX):
Remove max() and dmax() functions and replace
with a MAX macro in misc.h. Remove local MAX
macros and use the new one in misc.h. This
change removes the need for the special
case for _WIN32.
(set, mission, multifire, fuel, cons_choose, cede): Use it instead of
ef_byname(). Remove the hacks to map unwanted results to wanted
results. The hacks in set() were broken: bad input made the code cast
arbitrary items to union trdgenstr, with potentially disastrous
consequences. Closes#906483 and #906486.
(set): Less cryptic prompt and diagnostics.