empserver
22 months agom4: Refresh macros from autoconf-archive commit fd1d25c148
Markus Armbruster [Sat, 26 Dec 2020 07:20:57 +0000 (08:20 +0100)]
m4: Refresh macros from autoconf-archive commit fd1d25c148

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoconfig: Enlarge configuration tables that have variable size
Markus Armbruster [Sat, 26 Dec 2020 05:37:07 +0000 (06:37 +0100)]
config: Enlarge configuration tables that have variable size

Configuration tables product, sect-chr, ship-chr, plane-chr, land-chr,
nuke-chr have a bit of extra space deities can use for customizing
their games.  Give them more: enlarge product from 23 to 31
entries (plus one sentinel), sect-chr from 39 to 63, ship-chr from 47
to 127, plane-chr from 47 to 127, land-chr from 31 to 127, and
nuke-chr from 20 to 63.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoconfig: Slightly neater configuration table size macros
Markus Armbruster [Sat, 26 Dec 2020 05:30:24 +0000 (06:30 +0100)]
config: Slightly neater configuration table size macros

Macro SHP_TYPE_MAX is the largest possible ship type number.  It is
only used to size mchr[], and we need + 2 there.  Replace by MCHR_SZ
for simplicity.

Same for LND_TYPE_MAX, PLN_TYPE_MAX, P_MAX, and SCT_TYPE_MAX: replace
by LCHR_SZ, PLCHR_SZ, PCHR_SZ, and DCHR_SZ.

Same for N_MAXNUKE, except that one is more than the largest type
number.  Replace by NCHR_SZ.

For consistency, define ICHR_SZ and INTRCHR_SZ for sizing ichr[] and
intrchr_sz[].

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoAvoid SCT_TYPE_MAX, use ARRAY_SIZE() instead
Markus Armbruster [Sat, 26 Dec 2020 05:27:18 +0000 (06:27 +0100)]
Avoid SCT_TYPE_MAX, use ARRAY_SIZE() instead

The next commit will get rid of SCT_TYPE_MAX.  Prepare.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoNew macro ARRAY_SIZE()
Markus Armbruster [Sat, 26 Dec 2020 05:15:40 +0000 (06:15 +0100)]
New macro ARRAY_SIZE()

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoeconfig: Fix documentation of key assault_penalty
Markus Armbruster [Fri, 25 Dec 2020 12:15:14 +0000 (13:15 +0100)]
econfig: Fix documentation of key assault_penalty

Messed up when keys got documented in 4.2.0.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoinfo/Empire4.4: Fix more 4.4.0 change log entry typos
Markus Armbruster [Mon, 21 Dec 2020 16:01:29 +0000 (17:01 +0100)]
info/Empire4.4: Fix more 4.4.0 change log entry typos

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Tidy up comments
Markus Armbruster [Sun, 26 Jul 2020 05:52:20 +0000 (07:52 +0200)]
fairland: Tidy up comments

Don't use ALL CAPS for headings.  Reformat them in the style
recommended by doc/coding.  Drop a few comments that aren't pulling
their weight, and fix the odd typo.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Rename fl_move() to drift_capital()
Markus Armbruster [Fri, 16 Oct 2020 14:26:10 +0000 (16:26 +0200)]
fairland: Rename fl_move() to drift_capital()

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Move qprint() and NUMTRIES
Markus Armbruster [Mon, 10 Aug 2020 13:52:02 +0000 (15:52 +0200)]
fairland: Move qprint() and NUMTRIES

qprint() was carelessly added at the end of the file in Empire 2,
where it's under the "Write a script for placing capitals" headline.
Move it to a more sensible place.

NUMTRIES was added behind the global variables in Empire 2.  Move it
before them, and reformat its comment.

The order of forward declarations of static functions looks random.
Reorder them to match the order of definitions.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Fuse capx[], capy[] into cap[]
Markus Armbruster [Fri, 21 Aug 2020 11:42:51 +0000 (13:42 +0200)]
fairland: Fuse capx[], capy[] into cap[]

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Fuse sectx[], secty[] into sect[]
Markus Armbruster [Fri, 21 Aug 2020 11:42:51 +0000 (13:42 +0200)]
fairland: Fuse sectx[], secty[] into sect[]

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Simplify how write_sect() iterates over sectors
Markus Armbruster [Fri, 21 Aug 2020 11:12:11 +0000 (13:12 +0200)]
fairland: Simplify how write_sect() iterates over sectors

Iterating over IDs is slightly simpler than iterating over
coordinates, so do that.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Represent sector ownership more efficiently
Markus Armbruster [Fri, 21 Aug 2020 06:41:14 +0000 (08:41 +0200)]
fairland: Represent sector ownership more efficiently

Replace own[x][y] by own[XYOFFSET(x, y)], and narrow the element type
from int to short.  Takes a fourth the space.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Tweak rural iron, fert, oil for simplicity
Markus Armbruster [Fri, 21 Aug 2020 06:31:56 +0000 (08:31 +0200)]
fairland: Tweak rural iron, fert, oil for simplicity

The previous commit's interpolation tables reproduce the historical
linear functions faithfully.  This exact fit requires an awkward data
point in each of the tables for iron fertility and oil.  Drop them.

No double-valued data points remain.  Adjust struct resource_point
member @res to int.

This results in slightly less rural iron (33 elevations down one
point), fertility (10 elevations down one point) and oil (14
elevations down one point, 5 down two points).  Only resource values
less than 100 are affected.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Compute resources by interpolating tables
Markus Armbruster [Fri, 21 Aug 2020 05:32:36 +0000 (07:32 +0200)]
fairland: Compute resources by interpolating tables

The functions mapping elevation to resources are piecewise linear.
Replace them by linear interpolation on data points defined in tables.
Tables are easier to tweak for deities than code.

The data points are a bit awkward because the functions they replace
are.  The next commit will tweak them to address that.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Tweak sea oil & mountain gold for simplicity
Markus Armbruster [Fri, 21 Aug 2020 06:18:49 +0000 (08:18 +0200)]
fairland: Tweak sea oil & mountain gold for simplicity

The functions mapping elevation to resources are piecewise linear,
except for set_oil(), which randomly adds 1 to sea oil, and
set_gold(), which is isn't linear for mountains, but very close.

Drop the random oil bit, and replace the non-liner gold function by a
linear one.  Both resources decrease at most by one point.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Rewrite complicated, buggy & boring elevation code
Markus Armbruster [Sat, 5 Sep 2020 04:44:12 +0000 (06:44 +0200)]
fairland: Rewrite complicated, buggy & boring elevation code

Land elevation is computed by first placing mountains, then elevating
land to fit.

Mountains placement is a weighted random sampling.  A sector's weight
is its distance to sea capped at five and squared.

Non-mountain, non-capital sectors get assigned equidistant elevations
from 1 up to 97 rounded to integer in an order that depends on
distance to mountain and distance to sea, both capped at 5.  Mountains
get equidistant elevations starting at 98 (see recent commit
"fairland: Fair mountain resources").  Capitals get 36.

Sea elevation is randomly chosen from a range that depends on the
sector's distance to land.  The range increases from [-27,-1] next to
land to [-127,-1] for distance 5 and up.

Without mountains, the result is boring: elevation increases pretty
much linearly with the distance from the coast, i.e. each islands is a
single cone.  With mountains, we get one cone per mountain.  The sea
sea elevations are basically noise.

Worse, it's buggy: mountain placement can place fewer mountains than
requested.  The weighted random sampling assigns weights even to
sectors that cannot be picked (capitals and sectors that have been
picked already).  When the dice land on such a sector, it instead
picks the next one (in island growth order) that can be picked.  If
there is no next one, the mountain is not placed.  This is a fairness
issue.

Impact varies with fairland parameters.  For 60 sector islands with 5%
mountains, around one in 10000 islands is short one mountain in my
testing.  For 10 sector islands with 50% mountains, some 760 out of
10000 islands are short one sector, 80 short two, and five short
three.  The numbers get worse as spikiness increases.

Undocumented misfeature: the placement loop is limited to 1000
iterations, supposedly to catch a runaway loop.  Since the loop
iterates exactly once per mountain, all this accomplishes it limiting
mountains to 1000 per island.

When too few mountains are placed, the loop assigning elevations to
non-mountain, non-capital decrements elevations below 1.  Since
Chainsaw 3, such elevations then get mapped to 1, plastering over the
bug.  We get non-mountain sectors with elevation 1 instead of
mountains.

Rewrite as follows.

Use a simple random hill algorithm to assign raw elevations:
initialize elevation to zero, then randomly raise circular hills on
land / lower circular depressions at sea.  Their size and height
depends on the distance to the coast, capped at 3.  After a sufficient
number of iterations, the resulting terrain has a "natural" look.
This is elevate_prep().

elevate_land() then sorts non-capital sectors by raw elevation.  The
highest become mountains.  Sectors get assigned the same equidistant
elevations as before, just in raw elevation order.

elevate_sea() simply normalizes raw elevation to [-127,-1].

Elevations now show a nice undulating pattern independent of mountain
percentage.

Implementation detail: replace elev[x][y] by elev[XYOFFSET(x, y)], and
narrow the element type from int to short.  Takes a fourth the space.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Tweak rural elevations for simplicity
Markus Armbruster [Wed, 19 Aug 2020 11:31:09 +0000 (13:31 +0200)]
fairland: Tweak rural elevations for simplicity

elevate_land() computes the sequence of elevations 97, 97 - delta,
... in fixed point with a scaling factor of 100.  Switch to
floating-point, because it's simpler.  Elevations (and thus resources)
change slightly due to reduced rounding errors.

Note that we map elevations less than 1 to 1 both before and after the
patch.  Odd.  Turns out this mitigates a bug: mountain placement can
place fewer mountains than it should, and when that happens,
elevations go below 1 here.  The next commit will fix this.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Drop unused elevation band for coastal mountains
Markus Armbruster [Wed, 19 Aug 2020 11:44:00 +0000 (13:44 +0200)]
fairland: Drop unused elevation band for coastal mountains

Elevations 34 and 35 are reserved for coastal mountains.  When an
elevation computed for a non-mountain falls in that band, it's
adjusted to 36.

This is unnecessary since the previous commit; drop.  Test output
shows the dropped elevation adjustment.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Fair mountain resources
Markus Armbruster [Wed, 19 Aug 2020 12:16:14 +0000 (14:16 +0200)]
fairland: Fair mountain resources

For fairness, fairland aims to make islands of the same size consist
of the exact same sectors, just individually arranged.

It fails to achieve its aim for mountains: their elevation is random,
34 or 35 for coastal mountains, and between 98 and 254 (two dice) for
non-coastal ones.  Elevation determines resources.  Mountains get 89
gold on average (between 80 and 93), except for coastal mountains,
which get none.  It didn't really matter until Empire 3 made mountains
produce gold dust.  Since then, it's unfair.

Set mountain elevation to 98 + i * delta, where i is the mountain
number (starting at zero), delta is (127 - 98) / max_mn, and max_mn is
the maximum number of mountains an island could have.  This puts the
highest mountains on the largest islands.

Non-coastal mountains have slightly less gold than before, coastal
mountains have much more.

Mountains can still be unfair, because fairland can screw up their
number.  To be fixed soon.

Avoid elevations 34 and 35 for non-mountain, non-capital sectors
doesn't make sense anymore.  The next commit will get rid of it.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Use zero elevation for "not yet elevated"
Markus Armbruster [Sun, 16 Aug 2020 15:46:54 +0000 (17:46 +0200)]
fairland: Use zero elevation for "not yet elevated"

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Eliminate sectc[][], use adj_land[] instead
Markus Armbruster [Fri, 21 Aug 2020 11:25:16 +0000 (13:25 +0200)]
fairland: Eliminate sectc[][], use adj_land[] instead

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Replace distance_to_land(), distance_to_sea()
Markus Armbruster [Sun, 16 Aug 2020 12:01:32 +0000 (14:01 +0200)]
fairland: Replace distance_to_land(), distance_to_sea()

distance_to_land() searches for closest land sector, and
distance_to_sea() for the closest sea sector.  We already have a more
efficient alternative: the breadth-first search recently added for
spheres of influence can precompute these distances.  Put it to use,
and retire distance_to_land() and distance_to_sea().

distance_to_what() could now be simplified.  Don't bother, because
it'll soon be deleted entirely.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Eliminate macro ELEV, it's an abomination
Markus Armbruster [Mon, 17 Aug 2020 06:39:22 +0000 (08:39 +0200)]
fairland: Eliminate macro ELEV, it's an abomination

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Prefer placing islands away from the edge of the sphere
Markus Armbruster [Mon, 14 Sep 2020 05:27:45 +0000 (07:27 +0200)]
fairland: Prefer placing islands away from the edge of the sphere

When an island gets placed too close to the edge of the sphere of
influence, its side facing the edge will likely be formed by the edge.
Looks unnatural, and can give a clue on the location of the other
continent.

Make place_island() prefer sectors away from the edge: instead of
picking one of the admissible sectors with equal probability, reduce
the probability as we get closer to the edge.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Try harder to deliver the requested amount of land
Markus Armbruster [Tue, 11 Aug 2020 15:58:23 +0000 (17:58 +0200)]
fairland: Try harder to deliver the requested amount of land

Planned island sizes are random with an expected value that matches
the average size requested by the user.  Can be off quite a bit when
the number of islands is small.  Also, actual island size can be
smaller than planned size when space is tight.

Instead of picking random island sizes independently, pick a random
split of their requested total size.

To reduce the probability of islands not growing to their planned
size, grow large islands before smaller ones.

To compensate for inability to grow, carry the difference over to the
next island size.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Make actual island sizes fair
Markus Armbruster [Tue, 11 Aug 2020 14:41:20 +0000 (16:41 +0200)]
fairland: Make actual island sizes fair

The previous commit reduced the difference in island size within the
same batch of islands to at most one.  Eliminate the remaining
difference by shrinking the bigger islands by one sector.

This invalidates the precomputed exclusive zones, so recompute them.

fairland-test needs a tweak to avoid loss of test coverage.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Grow islands interleaved
Markus Armbruster [Tue, 11 Aug 2020 14:16:30 +0000 (16:16 +0200)]
fairland: Grow islands interleaved

The previous commits changed grow_island() to create islands in
batches consisting of one island per continent, all of the same
planned size.  grow_island() still places and grows one island after
the other.  When an island can't grow to the actual size, the others
in the same batch are not affected.  Island size can therefore differ
a lot within the same batch.

Change grow_island() to interleave the work on a batch's island: first
place them all, then add one sector to each in turn.  Stop after all
reached the planned size, or one or more could not be grown further.

This is similar to how we grow continents: drift() places them all,
and grow_continent() adds one sector to each continent in turn.

Island size within the same batch can now differ at most by one
sector.  The next commit will eliminate that remaining difference.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Make planned island sizes fair
Markus Armbruster [Tue, 11 Aug 2020 13:39:31 +0000 (15:39 +0200)]
fairland: Make planned island sizes fair

The previous two commits put the same number of islands closest to
each continent.  This one makes their planned sizes match: instead of
rolling dice separately for each island's size, we roll dice only for
the first continent's islands.  The other continent's islands get the
same sizes.

Actual island sizes still differ when islands can't be grown to their
planned size.  To be addressed next.

fairland-test needs a tweak to avoid loss of test coverage.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Fail when island can't be placed, for fairness
Markus Armbruster [Tue, 11 Aug 2020 14:16:30 +0000 (16:16 +0200)]
fairland: Fail when island can't be placed, for fairness

The previous commit made island distribution more fair by placing
islands close to a continent in turn.  This is still unfair when
fairland can't place all the islands.

Make grow_islands() fail when it can't place all islands, and main()
start over then, just like it does when grow_continents() fails.

Deities can no longer fill the world with islands by asking for a
impossibly high number of islands.  Tolerable loss, I think.

fairland-test needs a tweak to avoid loss of test coverage.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Distribute islands more fairly among continents
Markus Armbruster [Mon, 10 Aug 2020 06:22:21 +0000 (08:22 +0200)]
fairland: Distribute islands more fairly among continents

fairland places islands of random size in random places, subject to
minimum distances.  Results are often less than fair, in particular
when the number of islands per continent is low: some continents have
more land nearby than others.  Increasing distances between islands
doesn't help much.  Deities commonly run fairland until they find the
result acceptable.

The next few commits will tackle this issue.  As a first step, this
one places islands closest to continents in turn, so that each
continent is closest to the same number of islands.  A continent is
closest to an island when it is closest to each of its sectors.

The number of islands must be a multiple of the number of continents
now.

Since fairland may be unable to place all islands, a continent may
still get fewer islands than it should.  The next commit will address
that.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Compute spheres of influence
Markus Armbruster [Sun, 9 Aug 2020 15:13:33 +0000 (17:13 +0200)]
fairland: Compute spheres of influence

A continent's sphere of influence is the set of sectors closer to it
than to any other continent.  The next commit needs this.  Compute the
spheres with a breadth-first search.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Eliminate dirx[], diry[]
Markus Armbruster [Tue, 18 Aug 2020 20:20:56 +0000 (22:20 +0200)]
fairland: Eliminate dirx[], diry[]

dirx[] and diry[] are redundant with diroff[][].  Convert the
remaining uses, and drop the arrays.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Fix island growth and correct its bias
Markus Armbruster [Thu, 13 Aug 2020 06:10:37 +0000 (08:10 +0200)]
fairland: Fix island growth and correct its bias

grow_one_sector() picks a coastal start sector, then moves along the
coast trying to add an adjacent sector to the island.

It operates in spiking mode with a chance of @sp percent.

When spiking, the neighbors with sea on both sides are preferred.  For
instance, when the area around the sector being considered looks like
this

     - .
    - - .
     - .

then the neighbor in direction j is preferred, because it has sea in
directions u and n.  No other neighbor is preferred.

The start sector is the first coastal sector found in a linear search
in growth order, starting at the last sector grown when spiking, or
else at a random sector.  This is new_try().

grow_one_sector() tries adding a neighbor in clockwise order, starting
with a random direction.  When spiking, it goes around the clock two
times, trying only preferred sectors in the first round.

When it can't add a neighbor, it moves to the next coastal sector with
next_coast().

Taken together, this randomly picks one element from the set of
pairs (S, D) where the sector in direction D off base sector S can be
added to the island.  How does the probability distribution look like?

Bias: a sector's probability to be added to the island land increases
with the number of base sectors it is adjacent to.  This tends to fill
in bays and lakes, which is fine.

Bias: coastal sectors following runs of non-coastal ones are more
likely to be picked as start sector.  Perhaps less of an issue when
spiking, where the pick is not intended to be random.

Bias: a pair (S, D) is more likely to be picked when base sector S
follows a run of coastal sectors that aren't base sectors, or
direction D follows a a run of directions that don't permit growth.

The impact of these two biases is not obvious.  I suspect they are the
reason behind the tendency of large islands to curve around obstacles
in a counterclockwise direction.  This can result in multiple islands
wrapping around a central one like layers of an onion.

Bug: the move along the coast is broken.  next_coast() searches for
the first adjacent sea in clockwise order, starting in direction g,
then from there clockwise for a sector belonging to the island.
Amazingly, this does move along the coast in a clockwise direction.
It can get caught in a loop, though.  Consider this island:

        -
     - - -
      -

If we start at the central sector (marked 0), the search along the
coast progresses like this:

        1
     - 0 2
      -

It reaches the central sector again after three moves (to 1, to 2,
back to 0), and stops without having reached the two sectors on the
left.

If we start with the leftmost sector, the search loops: 0, 1, 2, 3, 1,
...

        2
     0 1 3
      -

grow_one_sector() ensures termination by giving up after 200 moves.
Nuts!

Because of this, grow_one_sector() can fail when it shouldn't, either
because the search along the coast stops early, or goes into a loop,
or simply because there's too much coast.  The latter should not
happen in common usage, where island sizes are in the tens, not the
hundreds.

Clean up this hot mess by rewriting grow_one_sector() to pick a sector
adjacent to the island with a clearly defined probability, as follows.

Use weighted random sampling to pick one sector from the set of
possible adjacent sectors.

When spiking, a sector's weight increases with number of adjacent sea
sectors.  This directs the growth away from land, resulting in spikes.

When not spiking, the weight increases with the number of adjacent
land sectors.  This makes the island more rounded.

To visit the adjacent sectors, grow_one_sector() iterates over the
neighbors of all island sectors, skipping neighbors that have been
visited already.

This produces comparable results for low spike percentages.  The weird
onions are gone, though.

Noticeable differences emerge as the spike percentage grows.  Whereas
the old one produces long snakes that tend to curve into themselves,
the new one produces shorter spikes extending from a core, a bit like
tentacles.  Moreover, islands are more centered on their first sector
now.  The probability for coastal capitals is lower, in particular for
moderate spike percentages.

I consider this improvements.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Track adjacent land sectors
Markus Armbruster [Sun, 6 Sep 2020 06:44:09 +0000 (08:44 +0200)]
fairland: Track adjacent land sectors

The next commit will put it to use.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Drop try_to_grow() parameter @extra_dist
Markus Armbruster [Sun, 9 Aug 2020 16:01:19 +0000 (18:01 +0200)]
fairland: Drop try_to_grow() parameter @extra_dist

The previous commit removed the only call passing non-zero
@extra_dist.  Drop the parameter.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Correct island placement bias
Markus Armbruster [Sun, 9 Aug 2020 15:38:36 +0000 (17:38 +0200)]
fairland: Correct island placement bias

A sector is admissible for island placement when land can be grown in
every sector within a certain distance.

place_island() picks a random start sector, then searches linearly for
an admissible sector.  If it finds one, it places the island there.
Else, it reduces the distance by one and tries again.  It fails when
none is found even for distance zero.

Trying with extra distance is obviously meant to reduce the risk of
islands from running into each other without need.  Initial distance
is @di, the minimum distance between continents, which doesn't really
make sense, and is undocumented.

Bug: place_island() never tries the start sector.

Bias: placement probability is higher for sectors immediately
following inadmissible sectors.  Because of that, islands are more
often placed to the right of existing islands.  Players could exploit
that to guide their search for land.

Rewrite place_island() to pick sectors with equal probability,
dropping the undocumented extra distance feature.  If it's missed, we
can bring it back.

The new code visits each sector once.  The old code visits only one
sector in the best case, but each sector several times in the worst
case.  fairland performance improves measurably for crowded setups
with large @di, else it suffers.  For instance, Hvy Fever example
given in the commit before previous runs seven times faster for me.
With @di reduced to 2, its run time more than doubles.  Not that it
matters.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Drop place_island() parameters @xp, @yp
Markus Armbruster [Sun, 9 Aug 2020 15:13:33 +0000 (17:13 +0200)]
fairland: Drop place_island() parameters @xp, @yp

There is no need to pass coordinates back to the caller.  Replace
parameters @xp, @yp by local variables @x, @y.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Precompute "exclusive zone" for speed
Markus Armbruster [Sun, 2 Aug 2020 15:46:26 +0000 (17:46 +0200)]
fairland: Precompute "exclusive zone" for speed

grow_one_sector() and place_island() pass candidate sectors to
try_to_grow() until it succeeds.

try_to_grow() fails when the candidate isn't water, or there are
sectors belonging to other islands within a minimum distance.
It does that the obvious way: it searches for such sectors.

When there is plenty of space, try_to_grow() rarely fails when it
searches.  For each land sector, we visit the sectors within minimum
distance, plus a little extra work for the rare failures.

In a more crowded setup, however, try_to_grow() fails a lot, and we
visit sectors many times more.

Example: Hvy Fever

    8 continents
    continent size: 60
    number of islands: 64
    average size of islands: 10
    spike: 0%
    0% of land is mountain (each continent will have 0 mountains)
    minimum distance between continents: 6
    minimum distance from islands to continents: 3
    World dimensions: 140x68

This is a crowded setup.  With -R 1, try_to_grow() fails almost
750,000 times due to minimum distance, and takes more than 18 Million
iterations.

With default minimum distances 2 and 1, it fails less than 700 times,
taking less than 14,000 iterations.

Instead of searching the surrounding sectors every time we check a
candidate, precompute an "exclusive zone" around each island where
only this island may grow the obvious way: when adding a sector, visit
the sectors within minimum distance and add them to the island's
exclusive zone.

When @extra_distance is non-zero, try_to_grow() still has to search,
Only place_island() passes non-zero @extra_distance.  The next few
commits will get rid of that.

Complication: since the minimum distance for growing islands may
differ from the minimum distance for growing continents, we have to
recompute exclusive zones between growing continents and islands.

For the Hvy Fever example above, this reduces the number of sector
visits by more than 90%, and run time by more than 70% for me.  With
default distances, it's a wash.

Of course, fairland performance is hardly an issue on today's
machines: it takes fairly impractical setups to push run time over a
second.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Change try_to_grow()'s last argument to extra_dist
Markus Armbruster [Sun, 2 Aug 2020 15:38:29 +0000 (17:38 +0200)]
fairland: Change try_to_grow()'s last argument to extra_dist

Callers pass @di for continents, @id for islands, possibly plus some
extra distance.  Pass just the extra distance, and compute the rest in
try_to_grow().  This  prepares for the next commit.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Rename INFINITY to INFINITE_ELEVATION
Markus Armbruster [Sat, 8 Aug 2020 11:59:37 +0000 (13:59 +0200)]
fairland: Rename INFINITY to INFINITE_ELEVATION

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Simplify search for next wilderness to elevate
Markus Armbruster [Sun, 16 Aug 2020 15:42:02 +0000 (17:42 +0200)]
fairland: Simplify search for next wilderness to elevate

To find the wilderness sector to elevate next, elevate_land() searches
the non-mountain, non-capital sectors of the island for one that
maximizes a function of its distance to mountains and to sea.

The search ignores already elevated sectors in a less than obvious
way: 1. it never picks a sector where the function yields -INFINITY or
less, and 2. when elevating a wilderness, its (cached) distances get
reset to values that make the function return a more negative value.

Use a more direct check of "not yet elevated": elevation is still the
initial -INFINITY.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Use INT_MAX as initial minimal distance in iso()
Markus Armbruster [Sun, 26 Jul 2020 05:30:58 +0000 (07:30 +0200)]
fairland: Use INT_MAX as initial minimal distance in iso()

Simpler and more obviously correct than WORLD_X + WORLD_Y.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Nicer & much faster replacement for next_vector()
Markus Armbruster [Sat, 25 Jul 2020 12:08:38 +0000 (14:08 +0200)]
fairland: Nicer & much faster replacement for next_vector()

next_vector() is kind of cute, but it is also unobvious, cumbersome to
use, and slow for arguments greater than one.

Replace it by hexagon_first(), hexagon_next().  The new code takes
O(1) time, whereas the old code takes O(n).  Iterating over a hexagon
changes from

       for (i = 0; i < n; ++i)
           vector[i] = 0;
       do {
           x = x0;
           y = y0;
           for (i = 0; i < n; ++i) {
               x = new_x(x + dirx[vector[i]]);
               y = new_y(y + diry[vector[i]]);
           }
   do stuff with @x, @y...
       } while (next_vector(n));

to

       hexagon_first(&hexit, x0, y0, n, &x, &y);
       do {
   do stuff with @x, @y...
       } while (hexagon_next(&hexit, &x, &y));

In my measurements, it's ~8% slower for n == 1, 25% faster for n == 2,
and more than three times faster for n == 6.

fairland's performance is dominated by it.  Creating worlds for the
Common Fever blitz (distance arguments 3 and 2) and the Hvy Fever
Blitz (distance arguments 6 and 3) on my machine speeds up by a factor
of 1.6 and 2.1, respectively.

Of course, fairland performance is hardly an issue on today's
machines: it takes fairly impractical setups to push run time over a
second.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Report missing and stunted islands
Markus Armbruster [Sat, 25 Jul 2020 06:13:54 +0000 (08:13 +0200)]
fairland: Report missing and stunted islands

fairland can create fewer and smaller islands than the user requested.
Report like this:

    No room for island #13
    6 stunted islands

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Check first two continent sectors properly
Markus Armbruster [Tue, 11 Aug 2020 05:52:45 +0000 (07:52 +0200)]
fairland: Check first two continent sectors properly

grow_continents() places the first two continent sectors without
checking for collisions or minimum distance.  Unlikely to be an issue
in practice, as growing such a continent will almost certainly fail.
Fix it anyway.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Move "is water" check into try_to_grow()
Markus Armbruster [Tue, 11 Aug 2020 05:51:30 +0000 (07:51 +0200)]
fairland: Move "is water" check into try_to_grow()

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Move capital initialization to drift() & simplify
Markus Armbruster [Mon, 10 Aug 2020 15:55:15 +0000 (17:55 +0200)]
fairland: Move capital initialization to drift() & simplify

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Move initialization of elev into create_elevations()
Markus Armbruster [Mon, 10 Aug 2020 15:48:21 +0000 (17:48 +0200)]
fairland: Move initialization of elev into create_elevations()

Move it so the next commit can inline init() into drift().

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Move global variables mc[] into stable(), eliminate mcc
Markus Armbruster [Sun, 26 Jul 2020 10:35:15 +0000 (12:35 +0200)]
fairland: Move global variables mc[] into stable(), eliminate mcc

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Fix "Only managed to grow" error message off by one
Markus Armbruster [Sat, 25 Jul 2020 07:20:09 +0000 (09:20 +0200)]
fairland: Fix "Only managed to grow" error message off by one

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Eliminate global variable @fl_status
Markus Armbruster [Sat, 25 Jul 2020 07:01:56 +0000 (09:01 +0200)]
fairland: Eliminate global variable @fl_status

grow_one_sector() sets @fl_status when it fails to grow a continent.
grow_continents() uses @fl_status to find out whether
grow_one_sector() succeeded, and main() uses it to find out whether
grow_continents() succeeded.

Change grow_continents() to return success / failure.
grow_one_sector() already does.  Use the return values, and eliminate
@fl_status.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Eliminate global variable @secs
Markus Armbruster [Sat, 25 Jul 2020 15:40:19 +0000 (17:40 +0200)]
fairland: Eliminate global variable @secs

Move global variable @secs into grow_islands() and grow_continents().
Its other users can use isecs[c] instead.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Move global variable @spike into grow_one_sector()
Markus Armbruster [Sat, 25 Jul 2020 15:30:51 +0000 (17:30 +0200)]
fairland: Move global variable @spike into grow_one_sector()

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Global variable @mind is write-only, drop
Markus Armbruster [Sat, 25 Jul 2020 07:22:35 +0000 (09:22 +0200)]
fairland: Global variable @mind is write-only, drop

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Set isecs[] for continents as well & simplify
Markus Armbruster [Sat, 25 Jul 2020 05:28:28 +0000 (07:28 +0200)]
fairland: Set isecs[] for continents as well & simplify

isecs[] is left zero for continents.  Set it to @sc instead, and
simplify two loops over land sectors.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: De-duplicate map from elevation to sector type
Markus Armbruster [Sat, 8 Aug 2020 11:26:43 +0000 (13:26 +0200)]
fairland: De-duplicate map from elevation to sector type

Both write_sects() and map_symbol() map from elevation to sector type.
Factor out as elev_to_sct_type().  Inline map_symbol() into output()
and simplify.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Fix checking of distance arguments
Markus Armbruster [Mon, 10 Aug 2020 13:39:45 +0000 (15:39 +0200)]
fairland: Fix checking of distance arguments

main() rejects the distance between continents when it exceeds WORLD_X
/ 2 and WORLD_Y / 2, and the distance between continents and islands
when it exceeds WORLD_Y and WORLD_Y.  Nuts.  Has always been that way.

Reject either when it exceeds the maximum distance between sectors.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoRevert "Make fairland finish argument parsing before reading econfig"
Markus Armbruster [Mon, 10 Aug 2020 13:39:45 +0000 (15:39 +0200)]
Revert "Make fairland finish argument parsing before reading econfig"

This reverts commit d2a7bb6b6fed02d2cf48682d7d2a52a16d5d90e9.

parse_args() uses WORLD_X and WORLD_Y to check the distance arguments.
Calling it before reading econfig is wrong, because at that time
WORLD_X and WORLD_Y still have the compiled-in default values instead
of the actual ones set in econfig.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Reject continent size 1
Markus Armbruster [Sun, 26 Jul 2020 11:47:44 +0000 (13:47 +0200)]
fairland: Reject continent size 1

fairland creates continents of size 1 just fine, but the newcap_script
it emits doesn't work: the newcap command requires a second wilderness
sector to the right of the first sector.

Reject continent size 1.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Simplify defaulting optional positional arguments
Markus Armbruster [Mon, 10 Aug 2020 13:24:30 +0000 (15:24 +0200)]
fairland: Simplify defaulting optional positional arguments

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Check positional arguments more thoroughly
Markus Armbruster [Mon, 10 Aug 2020 13:24:30 +0000 (15:24 +0200)]
fairland: Check positional arguments more thoroughly

fairland silently "corrects" some bad arguments.  Reject them instead.
It neglects to check others completely.  Fix that.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Consistent error message format
Markus Armbruster [Sun, 26 Jul 2020 11:15:03 +0000 (13:15 +0200)]
fairland: Consistent error message format

fairland prefixes error messages with several variations of "fairland:
error -- ", but also with "ERROR: " and nothing at all.  Consistently
prefix them with just the program name.

Some error messages end with a period, most don't.  Drop the periods.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Report errors to stderr, not stdout
Markus Armbruster [Sun, 26 Jul 2020 11:15:03 +0000 (13:15 +0200)]
fairland: Report errors to stderr, not stdout

fairland reports some errors to stdout instead of stderr.  Fix that.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Tweak progress messages
Markus Armbruster [Sat, 8 Aug 2020 11:34:02 +0000 (13:34 +0200)]
fairland: Tweak progress messages

"fairland: unstable drift -- try increasing DRIFT_MAX" is confusing:
it looks like an error, but isn't, and increasing DRIFT_MAX requires a
recompile.  I'm not sure it can happen.  Replace by just "unstable
drift".

"fairland: error -- continent %c had no room to grow!" is pretty
redundant: it's always followed by "Only managed to grow %d out of %d
sectors."  and then "ERROR: World not large enough to hold
continents".  All it adds is which of the continents failed to grow,
and that's not actionable.  Drop the message.

The message sequence "designating sectors...", "adding resources...",
"setting coastal flags...", and "writing to sectors file..." is a bit
of a lie as these four tasks aren't actually done one after the other.
Replace by just "writing to sectors file..."

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Drop "might be too small to fit continents" warning
Markus Armbruster [Sun, 2 Aug 2020 05:02:20 +0000 (07:02 +0200)]
fairland: Drop "might be too small to fit continents" warning

Commit de81e4e20 "Change fairland not to reject small worlds without
trying" (v4.3.25) downgraded it from error to warning, pointing out it
the size may well work, and when it doesn't, fairland fails cleanly.

When it works, the warning is pointless.  When it doesn't, it's
redundant.  Drop it.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Collect command line global variables in one place
Markus Armbruster [Sun, 2 Aug 2020 06:23:16 +0000 (08:23 +0200)]
fairland: Collect command line global variables in one place

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoman/fairland: Fix typos, polish markup, clarify text
Markus Armbruster [Sun, 26 Jul 2020 15:24:24 +0000 (17:24 +0200)]
man/fairland: Fix typos, polish markup, clarify text

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Drop option -o
Markus Armbruster [Tue, 11 Aug 2020 11:11:15 +0000 (13:11 +0200)]
fairland: Drop option -o

With -o, fairland doesn't add resources.  This is pretty redundant;
the deity can unset resources with "edit l * i 0 g 0 f 0 c 0 u 0".
Drop the option.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Drop option -a
Markus Armbruster [Sun, 26 Jul 2020 15:29:09 +0000 (17:29 +0200)]
fairland: Drop option -a

With -a, fairland makes the capital sector an airfield to "mark the
continents [...] so that you can tell them from the islands".  This is
pretty redundant since commit afc0ef94e "Make fairland record the
island number in the deity territory", v4.3.31.  Drop it.

The map fairland prints is not affected.  The continents are clearly
visible there.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Make write_newcap_script() failure fatal
Markus Armbruster [Sat, 25 Jul 2020 07:03:03 +0000 (09:03 +0200)]
fairland: Make write_newcap_script() failure fatal

When write_newcap_script() fails, it complains to stderr and fails.
main() doesn't bother to check for failure.  Has always been that way.
Fix main() to check.  Also adjust write_newcap_script() to return one
on success, zero on failure, like the other functions do.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Move #include to the beginning where they belong
Markus Armbruster [Sun, 26 Jul 2020 11:48:26 +0000 (13:48 +0200)]
fairland: Move #include to the beginning where they belong

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Add a comment describing how fairland works
Markus Armbruster [Sat, 25 Jul 2020 06:06:19 +0000 (08:06 +0200)]
fairland: Add a comment describing how fairland works

A comment describing how fairland works was lost some time after
Chainsaw 3.31.  This is the last known version:

    The algorithm starts out by placing the "capitols" on the torus in
    such a way so as to maximize their distances from one another (this
    uses the perterbation technique of calculus of variations).  Then from
    these seeds, the continents are grown.  The kind of shape they grow
    into is determined by the "spike" argument <sp>--the higher the spike,
    the more spindly they will be.  If you lower the spike, the continents
    will be more round.  The continents never touch one another, the
    argument <di> specifies how many sectors of water should be between
    the continents at all times.  The continents grow to the size you
    specify with <sc> and they all get the same number of mountains
    (specified by <pm>).  The other arguments should be self explanitory.
    If #define ORE 1, then resources will be placed in the sectors as well.
    You can alter the #define ORE_MAX, IRON_MIN, GOLD_MIN, FERT_MAX and
    URAN_MIN to affect what kind of resources the world gets.

It leaves much to be desired.  Add a more thorough one.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Add print_own_map(), print_elev_map() for debugging
Markus Armbruster [Tue, 11 Aug 2020 06:26:36 +0000 (08:26 +0200)]
fairland: Add print_own_map(), print_elev_map() for debugging

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agotests/fairland: Cover "plenty of space" better
Markus Armbruster [Wed, 29 Jul 2020 04:49:39 +0000 (06:49 +0200)]
tests/fairland: Cover "plenty of space" better

Reduce number of islands in fairland run "plain" so there's plenty of
space for them.  Scarce space is still covered by run "stunted".

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agotests/fairland: Improve coverage
Markus Armbruster [Tue, 28 Jul 2020 04:44:08 +0000 (06:44 +0200)]
tests/fairland: Improve coverage

Rename the existing fairland run to "plain".

New run "stunted" to cover larger minimal distances, islands
that can't fully grow, and islands that can't be placed.

New run "no-spike" to cover 0% spike.

New run "spike" to cover high spike percentage, mountains and -i.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agofairland: Show a more useful map
Markus Armbruster [Mon, 17 Aug 2020 10:39:04 +0000 (12:39 +0200)]
fairland: Show a more useful map

The map fairland shows has absolute 0,0 in the top-left corner, while
POGO's map * has it in the center.  Shift fairland's map to match
POGO's.

The map shows sea as '.', island sectors as '%', capitals as '#',
mountains as '^', and other continental sectors as a letter or digit
that encodes the continent number modulo 62.  When a continent has no
"other" sectors, its continent number is not shown.  Remove this
pathological case by using letter/digit for capitals, and '#' for
other continental sectors.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agotests/empdump: Fix to honor "make check-accept"
Markus Armbruster [Tue, 28 Jul 2020 04:41:41 +0000 (06:41 +0200)]
tests/empdump: Fix to honor "make check-accept"

Delete stray EMPIRE_CHECK_ACCEPT=.  Goes back all the way to commit
5a1544f92 "tests/empdump: New; exercising the empdump utility".

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agotests: Make "make check-accept" accept new files
Markus Armbruster [Tue, 28 Jul 2020 04:24:17 +0000 (06:24 +0200)]
tests: Make "make check-accept" accept new files

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agotests: Drop some dead code from test-common.sh
Markus Armbruster [Tue, 28 Jul 2020 04:21:46 +0000 (06:21 +0200)]
tests: Drop some dead code from test-common.sh

Left behind by commit c594b6120 "tests: New helper cmp_out1".

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoUpdate copyright notice
Markus Armbruster [Sun, 19 Jul 2020 06:37:48 +0000 (08:37 +0200)]
Update copyright notice

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agodamage: Shield embarked planes and land units from sector damage
Markus Armbruster [Thu, 10 May 2018 15:55:50 +0000 (17:55 +0200)]
damage: Shield embarked planes and land units from sector damage

Damage to a ship or land unit, say via pinpoint bombing, doesn't
damage loaded planes and land units.

Damage to a sector, say via strategic bombing, doesn't damage ships
there, but it does damage planes, even when loaded on a land unit (but
not when loaded on a ship), and land units, even when loaded on a land
unit or a ship.

This makes no sense.  Sector damage spills over to land units that way
since Chainsaw 3 added them, and to planes since 4.0.9.

Change sectdamage() not to damage land units and planes loaded on
ships or land units.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoload lload: Refuse to load satellites in space
Markus Armbruster [Sat, 12 May 2018 07:50:17 +0000 (09:50 +0200)]
load lload: Refuse to load satellites in space

Doesn't affect the stock game, because none of its satellites are
light, let alone x-light.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agolaunch edit: Take satellite off carrier on launch
Markus Armbruster [Sat, 12 May 2018 07:46:51 +0000 (09:46 +0200)]
launch edit: Take satellite off carrier on launch

Doesn't affect the stock game, because none of its satellites are
light, let alone x-light.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoload: Move check for hardened plane into plane_loadable()
Markus Armbruster [Fri, 11 May 2018 16:58:51 +0000 (18:58 +0200)]
load: Move check for hardened plane into plane_loadable()

This de-duplicates the check, and skips it when unloading.  It never
made sense there, and can't happen anymore since the previous commit.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agosubs: Maintain plane and land unit fortification invariant
Markus Armbruster [Fri, 11 May 2018 05:58:34 +0000 (07:58 +0200)]
subs: Maintain plane and land unit fortification invariant

Make lnd_prewrite() and pln_prewrite() ensure that land units and
missiles loaded on a land unit or ship are never fortified / hardened.

This takes care of edit neglecting to zap fortification on load.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoedit: Fix fortification limit of embarked planes and land units
Markus Armbruster [Thu, 10 May 2018 16:32:54 +0000 (18:32 +0200)]
edit: Fix fortification limit of embarked planes and land units

We generally preserve the invariant "land units and missiles loaded on
a land unit or ship are never fortified / hardened": fortify and
harden refuse to touch embarked land units and missiles, load and
lload zap land unit fortification on load, and refuse to load hardened
missiles.

The exception is edit, which happily fortifies embarked land units
(edit u key 'F'), hardens embarked missiles (edit p key 'F'), loads
fortified land units (edit u keys 'S' and 'Y') and hardened planes
(edit p keys 's' and 'y').

Fix the first two by correcting the new value's upper limit to zero
for embarked land units and planes.  The next commit will take care of
the rest.

The fix is visible in tests where test setup fortifies land units with
"edit u * F ?..." without excepting embarked ones.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agotests/actofgod: Cover loading hardened missiles
Markus Armbruster [Fri, 11 May 2018 06:27:12 +0000 (08:27 +0200)]
tests/actofgod: Cover loading hardened missiles

Demonstrates that edit leaves a missile's hardening intact on load.

Loading fortified land units is already covered, and also leaves
fortification intact.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoload: Factor out plane_loadable(), land_loadable()
Markus Armbruster [Sun, 3 Sep 2017 15:58:42 +0000 (17:58 +0200)]
load: Factor out plane_loadable(), land_loadable()

load_plane_ship() and load_plane_land() duplicate code to check
whether a plane can be loaded, except for the phrasing of one message.
Factor out into plane_loadable().  tran_plane() has equivalent code,
but wants different messages, which makes de-duplication unattractive.

load_land_ship() and load_land_land() duplicate code to check whether
a land unit can be loaded.  Factor out into land_loadable().

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoRevert "Don't let trains load trains"
Markus Armbruster [Thu, 10 May 2018 15:00:24 +0000 (17:00 +0200)]
Revert "Don't let trains load trains"

This reverts commit b1a0ff2fbd87d3c332de1e0a41b3ddea133c77b1.

Land units with capability heavy can't be loaded on anything, and land
units carrying land units can't be loaded regardless of capabilities.
Commit b1a0ff2fbd8 additionally outlawed loading of trains on land
units, but not on ships.  Bad idea, because it complicates matters for
no good reason.  Revert it.

Doesn't affect the stock game, because its only train is heavy.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agomarket: Simplify check for dead trades and document how it fails
Markus Armbruster [Thu, 10 May 2018 09:15:29 +0000 (11:15 +0200)]
market: Simplify check for dead trades and document how it fails

We fail to delete trades right away when the unit on sale dies.
Instead, we delete trades of the dead whenever we look at the market.
Doesn't work when new builds reuse the IDs of such dead.  If the new
unit's owner differs from the dead one's, we still delete the trade,
and log "Something weird".  If they are the same, the newly built unit
takes the dead one's place on the market.  Has been that way since the
market was added in 4.0.0.

I can't fix this right now, so mark as FIXME, and drop the logerror().
The first of the two trade deletions is now redundant, so drop that,
too.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agobuy set trade: Fix taking lots off the market
Markus Armbruster [Thu, 10 May 2018 08:28:49 +0000 (10:28 +0200)]
buy set trade: Fix taking lots off the market

To find out whether a lot is in use, some places check for zero
trd_owner, others for negative trd_unitid.  The former is reliable,
the latter is not: set() fails to change trd_unitid when it takes a
lot off the market.  The next trade-related command then runs
check_trade(), which logs "Something weird" and cleans up the mess.
Broken in commit e16e38dfabc (v4.2.18).

Replace the unreliable checks by reliable ones.

Clean up set() not to implictly rely on unused lots having negative
trd_unitid.

The trd_unitid = -1 are unnecessary now, so drop them.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoset: Use the first unused lot instead of the last
Markus Armbruster [Thu, 10 May 2018 08:16:10 +0000 (10:16 +0200)]
set: Use the first unused lot instead of the last

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoload unload: Don't treat unowned sectors specially
Markus Armbruster [Tue, 1 May 2018 16:54:14 +0000 (18:54 +0200)]
load unload: Don't treat unowned sectors specially

load and unload silently skip unowned sectors, unlike lload and
lunload.  Probably goes back to Chainsaw option ALLYHARBOR.

Drop that.  Deities can now load and unload in unowned harbors and
canals.  Mortals are now notified they can't.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoload lload unload lunload: Tweak suppression of error messages
Markus Armbruster [Tue, 1 May 2018 08:59:33 +0000 (10:59 +0200)]
load lload unload lunload: Tweak suppression of error messages

These commands suppress some error messages when ships, planes or land
units involved aren't explicitly selected by UID.  Without this, a
command like "unload plane 80 *" would complain about every plane not
on ship#80.

Correct a few issues with this error suppression:

1. We don't suppress the error when we can't load/unload a ship or
land unit because it's on the trading block.  Do suppress it.

2. We suppress the error message when we can't load/unload due to
foreign sector ownership in all but one places.  Fix that place.

3. Messages about explicitly selected planes and land units to load
are still suppressed when the carrier isn't selected explicitly.
Change this to suppress regardless of the carrier.

3. We suppress the error when a carrier has no room.  Don't, because
it's a potentially confusing silent failure.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoload: Drop dead recomputation of load_spy
Markus Armbruster [Tue, 1 May 2018 08:03:30 +0000 (10:03 +0200)]
load: Drop dead recomputation of load_spy

load_land_ship() recomputes load_spy "since [the carrier] may have
changed".  Has been that way since the feature was added in 4.2.0, but
it's nonsense.  Drop it.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agoload lload: Drop dead "cannot carry land units" checks
Markus Armbruster [Tue, 1 May 2018 07:19:43 +0000 (09:19 +0200)]
load lload: Drop dead "cannot carry land units" checks

load_land_ship() and load_land_land() fail for carriers that can't
carry any land units before prompting for land units to load.  They
then iterate over land units to load, and fail when the carrrier has
no room.  They either report "cannot carry land units" or "doesn't
have room for any more land units" then.  The former cannot happen.
Crept into load_land_ship() in Empire 2, blindly copied to
load_land_land() in 4.0.0.  load_plane_ship() and load_plane_land()
don't have this issue.

Drop the dead code.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agodoc/contributing: Fix a greengrocers' apostrophe
Markus Armbruster [Mon, 4 Dec 2017 18:38:27 +0000 (19:38 +0100)]
doc/contributing: Fix a greengrocers' apostrophe

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
22 months agounload lunload: Fail early when carrier can't carry land units
Markus Armbruster [Sun, 17 Sep 2017 04:53:37 +0000 (06:53 +0200)]
unload lunload: Fail early when carrier can't carry land units

Attempting to unload land units from a carrier that can't carry any
prompts for land units to unload, while attempting to unload planes or
load land units or planes fails without prompting.

Fix this inconsistency by making unload and lunload fail early for
land units, just like they do for planes.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>