Area.getClosestPointTo scans every point of the water search area, and
it was called up to count * retryFactor times in placeDocks.
The nearest point of a set to a point outside of it is always a
boundary point, so compute the boundary once and scan only that. The
logic lives in two small helpers, getBoundaryPoints (Uint8Array mask
based) and getClosestPointInList, so it can be reused and read apart
from placeDocks. getBoundaryPoints returns the original point objects,
while getClosestPointInList returns a clone of the point it finds, so
callers cannot mutate the points stored in the list.
Create the water search area first and return before computing the land
search area if there is no water at all, so that work is not wasted.
Also return if the land search area is empty. Previously pickRandom
would have been called on an empty array and returned undefined, making
getClosestPointTo fail on an undefined position.
Cache the start points instead of calling getPoints() on every try.
areaLand and areaWater were created with a MapBoundsPlacer, which
evaluates the tileclass constraint on every tile of the map. However,
createArea only has an effect if either a painter is specified or the
returned Area is used. Here no painter was passed and the returned
values were never read, so these calls did nothing except waste time.
If a point at squared distance 0 is found, no closer point can exist, so
return it immediately instead of scanning the rest of the points. The
returned point is the same as before, since the first point with the
smallest distance was already the one kept.
Iterate with an indexed loop over a local reference to the points array
instead of for...of over this.points, which avoids iterator and property
lookup overhead in a loop that can run over thousands of points.
Every neighbor sample created a new Vector2D and went through
validHeight() and getHeight(). Use plain integer coordinates, an inline
bounds check and direct access to the height array instead, which also
removes the per-vertex allocation and the setHeight() call.
The distance of each brush point and the check for the center vertex
were recomputed for every vertex. Compute them once per paint() call
into typed arrays instead.
Replace the array of Uint8Arrays, which was rebuilt with fill().map()
on every iteration, by one flat Uint8Array that is allocated once and
cleared at the start of each iteration.
The clone of g_Map.height was only used to read its length, while all
neighbor heights are read from g_Map itself. Cloning the whole heightmap
on every iteration was therefore pure overhead.
The same "Placing docks" log call is already used inside placeDocks
function, which is called just after these log calls.
Remove as it is useless and confusing.
Since they got introduced, there were separate documents for the Terms
of Service and Terms of Use for the multiplayer lobby. However, these
documents were rather short and it wasn't immediately clear, what the
difference between them is.
To simplify the process of reading and acknowledging the terms of the
multiplayer lobby, this consolidates the former Terms of Service and
Terms of Use in a single document. This results in players having to
read two documents (Terms of Service and Privacy Policy) instead of
three when registering for the multiplayer lobby. This makes the
registration process smoother and will in the best case result in more
players actively reading the documents.
This patch fixes the issue that the CinemaOverlay was hiding one turn
after the cinema paths stopped playing.
That was happening because `CinemaOverlay.OnSimulationUpdate` was called
before `OnSimulationUpdate` in session.js, so it read last turn's sim
state. Making it a simulation update handler ensures the sim state is
up-to-date once it's called.
This patch fixes the previously broken implementation of the timescale
property of cinema paths, now cinema paths with timescale 0.5, for
example, play twice as fast.
Unlike the comment stated, negative timescales weren't actually
supported, which is now enforced.
For demonstration, the timescale of the demo map's path is changed to
0.85.
Before this, starting a replay after a deletion loaded and ran the
replay in the background without dismissing the replay list, and
starting another one crashed on an assertion.
Fixes#9246
After #7164, entities in the ActorViewer are loaded through the
preview filter, which disables shadows by design. As a result, the
shadow toggle no longer has any visible effect.
Adds a dedicated viewer.xml template so the ActorViewer no longer
inherits the preview filter's shadow behaviour, keeping preview.xml
untouched for the entities that legitimately need shadows off.
Refs #7164
Reported by: Vladislav
To follow other maps name syntax (`<name> (<number of players>)`), "players" is
removed from the name on the following maps to keep only the number:
- Cycladic Archipelago (2p, 3p)
- Libyan Oasis
- North African Savanna.
Auras.CanApply called Visibility's GetPreview(), which doesn't exist,
so auras were applying in preview. Introduced in eb2ff98883.
Add GetPreview() to Visibility.
Fixes#9225
f39e71cca0 moved the player data refresh after a diplomacy change
into a diplomacy colors change handler, which only runs when
diplomacy colors are enabled. With them disabled, the diplomacy
dialog's stance buttons didn't reflect the new stances.
Refresh the player data directly in the diplomacy notification
handler, before the diplomacy colors are recomputed, and remove
the now redundant diplomacy colors handler registration.
To follow other maps name syntax (`<name> (<number of players>)`), "players" is
removed from the name on the Watering Holes map to keep only the number.
sortEntitiesForEngagement projects both attackers and targets from
the same origin (avgAttackers) onto the same vector,
so reversing the target array was incorrect.
Follows db23584fc3 - the caller of GetEffectiveAttackRange's didn't
really use the NEVER_IN_RANGE result properly.
The C++ engine does not handle Infinity (treated as 0).
This also changes MoveToTargetAttackRange to explicitly try to get
closer if the parabolic range is not in range, but it seems like a flat
range could - on the assumption that once we get closer, the terrain
height difference is lower.
Since 38b33b0484, the range check happened in CanAttack, however the
check there is too permissive.
This meant it was possible for towers to attack certain targets outside
their actual range, depending on the query results and unit AI focus
fire.
Following 38b33b0484, units on aggressive stance no longer chase beyond
their own vision, including on player-forced orders. This fixes that
regression.
(Note that approaching and chasing are a bit inconsistent).
38b33b0484 partly rewrote GetQueryRange but ended up with two
bugs/regressions:
- in standGround, melee units used an effective max-range of 0 instead
of their melee attack max range
- units that have a min-range did not see units within their min-range
(even though they can move).
This fixes these issues and rewrite the function in a simpler fashion
for readability.
When temporarly leaving a multiplayer game the "I will return" button
didn't work. This was because there was no valid `closeSession`.
Since it requires accessing `this` the function can't be an arrow
function anymore.
Introduced in 2520c45220Fixes: #9086
GATHER.FINDINGNEWTARGET inserts a ReturnResource order ahead of the
next queued order when the unit is still carrying resources, but
skips doing so if the next order already returns those resources.
Unfortunately, the check was incorrectly written, missing a `.type`.
This meant we always added a fresh order.
Units could then sometimes return resources to the closest dropsites,
then move to the actually queued dropsite, which could lead to
unexpected pathing.
Introduced in 0dda3b579c where the feature was added.
The cache invalidation was incorrectly using forEach, which failed to invalidate the cache.
This never actually caused a bug because the game currently does not silently swap active player entities.