Sync upstream into main - #4
Open
github-actions[bot] wants to merge 179 commits into
Open
github-actions[bot] wants to merge 179 commits into
github-actions[bot] wants to merge 179 commits into
Conversation
A handful of SolLua bindings still referenced sol::nil / sol::type::nil while the rest of the same files already use sol::lua_nil. sol::nil is a thin alias for lua_nil (see lib/sol2/sol.hpp) and collides with the `nil` macro that Objective-C headers define, so lua_nil is the portable spelling. This brings the remaining spots in line with the prevailing convention. Extracted from cc3cad9 in beyond-all-reason#2991; cross-platform, no behavior change.
float3.h included lib/streflop/streflop_cond.h directly, ahead of FastMath.h. FastMath.h defines MATH_SQRT_OVERRIDE before it includes streflop_cond.h (so streflop does not define its own math::sqrt(float) -- FastMath provides a faster one). The direct include meant streflop_cond.h could be processed before that override was set. Drop the direct include; FastMath.h pulls in streflop_cond.h transitively with the override in place. creg_cond.h keeps its original position -- it does not pull in streflop, so it does not need to move. Surfaced by the macOS port (beyond-all-reason#2991, commit cc3cad9). Co-authored-by: Mark Kropf <markkropf@gmail.com>
…son#3025) `SafeUtil.h` uses `<memory>` for `std::addressof`, and `<type_traits>` for `std::is_trivially_copyable` / `std::is_trivially_constructible_v`, but pulled them in only transitively. Include them directly so the header is self-contained and does not rely on include order elsewhere. Also use `std::is_trivially_default_constructible` for the default-construction. Extracted from cc3cad9 in beyond-all-reason#2991; cross-platform, no behavior change. Co-authored-by: Mark Kropf <markkropf@gmail.com> Co-authored-by: sprunk <spr.ng@o2.pl>
LuaTextures::Create returned an empty string on glTexImage failure with no diagnostics. Log target/size/format/dataFormat/dataType/glError so texture-creation failures can be diagnosed. Extracted from 1e75080 in beyond-all-reason#2991; cross-platform, no behavior change beyond the added log.
Engine crash thread: https://discordapp.com/channels/549281623154229250/1516994684591931535 Bugged Scenario: A reclaimer A is guarding another reclaimer B, while both reclaiming the same wreck. A can be notified of wreck death BEFORE the guarded unit B is notified, leading to trying to reclaim the same (actively-deleting) wreck because B's reference hasn't been cleaned up yet. This leads to an eventual segfault. So: the short term/easy fix is to skip reclaiming of a dead/dying target.
util_fileSelector was declared with a non-const struct dirent* on __APPLE__ and const elsewhere. macOS scandir() expects the selector argument as int(*)(const struct dirent*), so the non-const Apple variant failed to compile under GCC: Util.c:500: error: passing argument 3 of 'scandir' from incompatible pointer type Both branches were otherwise identical, so drop the __APPLE__ split and use const struct dirent* unconditionally (matches POSIX scandir). No effect on Linux/Windows. Assisted by Claude Code; verified by compiling the macOS headless build.
The legacy build did find_package(X11 REQUIRED) under a plain if(UNIX) guard. macOS is UNIX in CMake but does not use X11 (it uses Cocoa), so configuring the engine on macOS failed at find_package(X11). An if(APPLE) block already follows for Foundation, so exclude Apple from the X11 branch: if(UNIX AND NOT APPLE). Surfaced configuring the spring-headless target on macOS (which reuses the legacy Game target). No effect on Linux/Windows. Assisted by Claude Code; verified by configuring the macOS build.
Modern 7-Zip ships its CLI as '7zz' (Homebrew's 'sevenzip' formula installs /opt/homebrew/bin/7zz; recent Linux distros likewise package '7zz'). FindSevenZip only searched for '7z'/'7za', so configure failed with 'Could NOT find SevenZip (missing: SEVENZIP_BIN)' on such systems. Add '7zz' to the searched NAMES. No effect where 7z/7za already exist.
rts/System/Platform/Mac/SDLMain.m and SDLMain.h are the classic SDL 1.2 Cocoa main wrapper (the Darrell Walisser / Max Horn template). They are: - not listed in any CMakeLists (never compiled) - not #included by any source file - built on Carbon (<Carbon/Carbon.h>), which is 32-bit-only and unavailable on modern macOS / Apple Silicon SDL2 provides its own SDL_main, so this wrapper is obsolete. Remove the dead files so the macOS platform layer reflects what is actually built.
Updated the link for the SplinterFaction card to include the 'https://' prefix. This has been broken for a very long time. I've asked Skyrbunny to fix it multiple times, but it has never gotten fixed.
beyond-all-reason#3052) setMinLevel() only searched for an existing entry on the "set back to default" (erase) path. Setting a section to a *non-default* level appended a new row unconditionally, so repeatedly changing one section's level (e.g. via Spring.SetLogSectionFilterLevel) accumulated duplicate entries and eventually filled the fixed 64-slot sectionMinLevels table -- after which every section-level change silently failed with "too many section-levels". Fix: Look the section up before appending and, if it already has an entry, update it in place. This bounds the table at one entry per section. This likely never happens in practice but this was found while writing other tests Co-authored-by: Bruno Da Silva <Bruno-DaSilva@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The original code relied on implicit null-termination which is not guaranteed.
Aligns forward declarations with definitions to silence MSVC warnings.
SpringApp.cpp included <X11/Xlib.h> for every non-Windows platform, but macOS has no X11 headers by default, breaking the native build. The only user of it, XInitThreads(), is already excluded on __APPLE__ at its call site, so guard the include the same way.
glad_glx.c includes X11/X.h to provide the GLX windowing-system bindings,
but macOS has no X11/GLX. The Glad CMakeLists added glad_glx.c for every
UNIX-and-not-MinGW platform, so building any GL-enabled target (e.g.
engine-legacy) on macOS failed:
fatal error: X11/X.h: No such file or directory
macOS resolves GL entry points without GLX (the engine's glxHandler is
already #ifdef'd out on __APPLE__), so exclude glad_glx.c on Apple and
build only the core glad.c there.
…eyond-all-reason#2919) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These three headers use std:: algorithms but rely on <algorithm> being pulled in transitively. libstdc++ does so; libc++ (clang/macOS) does not, so they can fail to compile under libc++ depending on the version. Symbols that require the include: - rts/System/Matrix44f.h -> std::copy - rts/System/SpringHashMap.hpp -> std::fill_n - rts/System/SpringHashSet.hpp -> std::fill_n Adding the include is "include what you use" correctness and is a no-op on toolchains that already provide it transitively. No functional change. Cherry-picked from ExaDev/RecoilEngine (0ed29d5, 0b4bd10, 3adabd0). AI assistance: changes identified and applied with Claude (Anthropic); verified by a human (compiled under clang/libc++, no regression).
DemoTool compiles engine FileSystem sources (FileHandler, FileSystem) that include nowide/fstream.hpp and fmt/printf.h, but its target_link_libraries omitted the nowide and fmt targets. The engine builds obtain those include directories transitively through the nowide::nowide and fmt::fmt INTERFACE targets; DemoTool linked neither, so the build failed when the headers were not on a default search path (observed building the demotool target on macOS). Link nowide::nowide and fmt::fmt so their INTERFACE include directories propagate to the demotool target. Co-authored-by: Robert Burnham <burnhamrobertp@gmail.com>
* avoid parenthesised aggregate init and name the type explicitly (not supported everywhere) * avoid narrowing conversions.
* adds `Spring.TraceRayBetweenPositions(xA, yA, zA, xB, yB, zB, type)`
* adds `Spring.TraceRayInDirection(x, y, z, dx, dy, dz, length, type)`
* type is a string, "unit", "feature", or "both"
* both return an array of `{distance, objID, objType}` sorted by increasing distance
* these no longer did anything and just polluted the interface. * also removed example gadgetry using them from basecontent.
> warning: space between quotes and suffix is deprecated in C++23
Fixes beyond-all-reason#3177. Seems a missed side effect of beyond-all-reason#1509.
…for unittype based draw order (beyond-all-reason#3204) * Added configbool "UnitIconsSorted" (default disabled) to allow for unittype based draw order * sortUnitIcons -> sortUnitIconsByDepth removed `sortUnitIcons`, Games that define no `drawOrder` values skip sorting entirely added `UnitIconsSortedByDepth` config bool (default false). When enabled, overlapping icons are additionally ordered back-to-front by view depth within equal `drawOrder`; this makes overlap stacking change as units and the camera move, hence optional. * changed comment, applied static_assert "icon sort records cannot index all units" * moved changelog to: doc\pr-changelogs\3204.md
* Fix local demo waiting for replay players --------- Co-authored-by: sprunk <spr.ng@o2.pl>
* Add mod rule to allow game to override map gravity value.
* ProjectileDrawer: reuse alpha particle geometry across the water passes
When water is visible the main view draws alpha particles twice per
frame: a below-water pass, then an above-water pass after the water
surface. Both passes contain the same particles, viewed from the same
camera at the same interpolation time; only the clip-plane uniform
differs. Each pass nevertheless refiltered, re-sorted, re-ran every
particle's Draw() to regenerate its quads, and re-uploaded all of it.
Fill and upload the geometry once in the below-water pass, capture the
pending index range before submitting (new TypedRenderBuffer::
GetPendingElemsRange), and have the above-water pass of the same draw
frame re-issue that range through the new DrawElementsRange, which
draws an already-uploaded range without touching the consume/rewind
bookkeeping. The reflection and refraction passes that run in between
fill and consume their own ranges, so the saved range stays valid;
stream buffer chunks only swap at end of frame. The reuse pass still
fires DrawWorldPreParticles and flushes anything appended during it,
so nothing can leak into a later submit under a different shader.
The reuse can be disabled at runtime with the new config
ProjectileDrawReuseWaterPasses (default 1, safemode 0), which forces
the previous full-refill-per-pass behavior.
The above-water particle pass drops from ~1.16 ms to ~65 us mean at
~6k visible alpha particles (deterministic map-wide effect-spawner
benchmark at 3x battle-level intensity on a water map, measured with
Tracy). The resubmitted geometry is byte-identical to what a refill
would produce, and maps without visible water (single combined pass)
are unaffected.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ProjectileDrawer: faster sorting and threaded quad generation
Three further reductions of the alpha pass' CPU cost, measured with the
same deterministic effect-spawner benchmark as the previous commit
(~6k visible alpha particles, water map, Tracy):
1. Sort-key snapshot. The sorting predicates dereferenced two
CProjectile pointers per comparison (drawOrder + sortDist), so
sorting a few thousand particles was mostly cache misses (~200 us
per fill). DrawAlpha now snapshots {drawOrder, sortDist, proj} into
a contiguous array while filtering and sorts that instead. Ordering
semantics are unchanged (drawOrder asc, distance desc, pointer
tiebreak).
2. Threaded quad generation, config ProjectileDrawThreadedFill
(default 1, safemode 0). The per-particle Draw() loops split the
draw list into contiguous chunks; each chunk generates its quads
into a per-chunk scratch buffer through a thread_local redirect of
the buffer returned by CExpGenSpawnable::GetPrimaryRenderBuffer,
and the scratch buffers are merged into the primary buffer in chunk
order. The submitted geometry is therefore byte-identical to a
serial fill, including back-to-front order. Each chunk opens a
ProjectileDrawer::DrawAlpha(MTChunk) Tracy zone so the distribution
is visible on the worker tracks; the ordered merge is zoned as
DrawAlpha(MTMerge).
All Draw() overrides reachable from the alpha pass were audited for
shared-state writes: none call RNG, write statics, or hit lazily
initialized caches (CColorMap::GetColor and the pre-resolved
AtlasedTexture pointers are pure reads); they only write their own
members and the buffer. The two exceptions carry the new
CProjectile::mtDrawSafe = false and are drawn serially at their
exact position in the sorted order: CTracerProjectile issues
immediate GL calls on its own VA_TYPE_TC buffer inside Draw(), and
ShieldSegmentProjectile mutates the shared per-shield
ShieldSegmentCollection latch and can fire the DrawShield Lua
callin. Segments shorter than 96 particles per chunk stay serial,
so small fills (reflection pass, quiet frames) skip the for_mt
dispatch overhead. Scratch buffers are only grown on the main
thread since RenderBuffer registration is not thread-safe.
On sync safety (particles that deal damage or spawn other
particles): damage dealing and particle spawning live exclusively
in synced simulation paths. Every guRNG call and every
projMemPool.alloc chained spawn (CWreckProjectile smoke,
CExpGenSpawner, FireBallProjectile sparks) sits in a ctor, Init()
or Update(); gsRNG is not referenced by any particle code; Draw()
bodies write only render-side members that sim code never reads.
The draw path therefore cannot reach synced state, and neither
reordering, deduplicating nor threading it can diverge the
simulation.
3. ProjectileReflectionMinRadius (default 0 = off): optionally skip
non-model alpha particles below a given draw radius in the water
reflection pass, decided in UpdateDrawFlags before the reflection
camera InView test. Small particles are barely visible in a wavy
reflection, while the reflection pass pays the full fill/sort/
quad-generation cost for them.
Also hoists the loop-invariant camera direction reads out of
CSimpleParticleSystem's non-directional draw loop.
With threading on, DrawAlpha(DS) drops ~390 us -> ~130 us and the
below-water particle pass ~1.15 ms -> ~660 us. Benchmark frame
average: 5.82 ms -> 5.22 ms; combined with the previous commit,
7.51 ms -> 5.22 ms (-30% frame time, 133 -> 192 fps) at 3x
battle-level effect load.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ProjectileDrawer: radix-sort the alpha particles
The back-to-front ordering used std::sort with a comparison predicate;
at battle-level counts (~20k sortable alpha particles) this cost
~184 us mean and up to ~470 us per fill, with the input-dependent
variance inherent to comparison sorting.
drawOrder and view distance are now packed into a single 64-bit key at
filter time (drawOrder as a biased int in the high half, distance
mapped to an order-preserving inverted integer in the low half; an
ascending sort yields drawOrder asc, distance desc as before), sorted
with a stable LSD radix sort: 8-bit digits, passes whose digit is
constant across all keys are skipped (with drawOrder unused the whole
high half is), and fills below 1024 elements use a plain
single-integer-compare std::sort, which wins at that size. Cost is
linear and input-independent, so the worst-case spikes disappear:
measured 31 us mean / ~75 us max (down from 184/470), with the key
packing moving a fixed ~50 us into the filter pass - a net ~100 us
saved per fill.
Two deliberate semantic notes: exactly-coincident particles (equal
drawOrder and distance, e.g. multi-part effects spawned at the same
point) are now tiebroken by stable fill order instead of pointer
address, which is frame-coherent; and NaN distances can no longer feed
a comparison predicate that violates strict weak ordering (undefined
behavior for std::sort) - the radix path handles any bit pattern.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ProjectileDrawer: reuse geometry for refraction, balance fill chunks
Two refinements adopted from bruno-dasilva's draft beyond-all-reason#3037, which
independently arrived at the same overall design as this branch (pass
geometry reuse, threaded quad fill with the same two serial-exception
classes, radix sort):
- The water refraction pass views the same particles from the same
player camera as the main view, so it now re-submits the geometry
saved by the below-water pass with its own clip plane instead of
doing a filter/sort/fill of the underwater-flagged subset. Visually
equivalent: the clip plane discards everything above the surface.
Water maps now build alpha geometry twice per frame (main view +
mirrored reflection camera) instead of four times. Gated by the
existing ProjectileDrawReuseWaterPasses config.
- Fill chunks are oversubscribed 4x relative to the worker count.
Per-particle draw cost varies wildly (a smoke trail emits dozens of
quads, a small flash one), and with one chunk per worker a single
heavy chunk gated the whole dispatch. MTChunk mean dropped from
~68 us to ~26 us and benchmark 1%-low fps improved 111 -> 125.
Benchmark frame average 5.18 ms -> 4.98 ms (193 -> 201 fps); total
against the pre-series baseline 7.51 ms -> 4.98 ms (-34% frame time)
at 3x battle-level effect load on a water map.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* System: extract the radix sort into a reusable utility
Move the stable LSD radix sort out of ProjectileDrawer into
System/RadixSort.h, templated on element type and key projection, as
suggested by review on beyond-all-reason#3037 for the equivalent code there. No
functional change: ProjectileDrawer sorts by the packed 64-bit
particle key exactly as before, and the caller-owned scratch buffer
keeps its capacity across frames.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ProjectileDrawer: thread the shadow-transparent particle fill
DrawShadowTransparent runs whenever shadows are enabled at all (any
shadowConfig > 0 sets SHADOWGEN_BIT_PROJ), and filled shadow-camera
billboards for every shadow-casting particle serially on the render
thread. Route it through the same FillParticleGeometry helper as the
alpha pass: the multiplicative shadow blend is order-independent, so
the list needs no sorting and chunk merge order does not matter; the
mtDrawSafe exceptions are handled by the shared helper as usual.
Gated by the existing ProjectileDrawThreadedFill config; the fill is
Tracy-zoned as DrawShadowTransparent(Fill).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ProjectileDrawer: hoist per-frame invariants out of UpdateDrawFlags
The per-particle camera loop re-evaluated per-frame constants for
every projectile: IWater::GetWater()->CanDrawReflectionPass() (a
virtual call through a unique_ptr, once per particle per frame), the
projectile shadow-gen bit, the camera pointer lookups, and repeated
GetDrawRadius() calls. Hoist them ahead of the for_mt and unroll the
three-camera loop into straight-line per-camera blocks.
Flag results and sort distances are unchanged; the reflection
min-radius guard is re-expressed equivalently (hasModel || radius >=
min instead of skip-if !hasModel && radius < min).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ProjectileDrawer: declare the on/off toggles as CONFIG(bool)
ProjectileDrawThreadedFill and ProjectileDrawReuseWaterPasses are pure
on/off switches; declare them as bool configs (read via GetBool)
instead of int. Stored 1/0 values from existing configs parse the
same, so nothing user-facing changes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…all-reason#2797) So we successfully update with the f-1 value, not the f value; and OwnerMoved() can be called with the proper p(f) - p(f-1) vector
Via a dummy function so that people trying to add it in the future are likely to actually stumble upon it.
…2878) Obsoleted by resource packs. No logic change, internals only. Co-authored-by: TarnishedKnight <lostsquirrel43@gmail.com>
* Updated EnTT to v3.16.0
Affects the the Lua Platform value.
* Add KeyBindingsChanged callin * Emit KeyBindingsChanged from keybinding commands
* Register key names for the keys that had none * Correct the key listing in the ui-keys reference * Deprecate KEYSYMS in favour of Spring.GetKeyCode
…ond-all-reason#3132) * fix issue where ships could get incorrect pathmap blocking zones with underwater structures near coast lines. * rename isSubmersible to hasUnderwaterCollision
* Stop unbind from removing longer chains that share the last key * Match unbind keychains exactly instead of by fit() --------- Co-authored-by: TarnishedKnight <lostsquirrel43@gmail.com>
…ed) (beyond-all-reason#3191) * Nano Particles: add NanoParticleUpdate engine callin Batched, unsynced lifecycle events for nano particles, so deferred-lighting widgets can light them without polling. Nothing emits these yet; the standalone nano particle effect added in the following commit owns the batching and the sampling that decides which particles are reported. Events are passed as one flat numeric array of 13-entry records rather than a table per event, because a per-event table would dominate the cost of the callin at the rates involved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Nano Particles: add standalone nano particle effect Adds an optional nano particle effect that renders build spray as shader-generated 3D shapes with an additive halo, behind NanoParticlesGL4 (default off). Ported from BAR's gfx_nano_particles_gl4 gadget. The effect is not a projectile. Its particles have no projectile id, take no part in collision or quadfield work, are never handed to Lua as projectiles, and are not serialised; motion is analytic, so the shader reconstructs position from start/velocity/frame and the CPU only touches a particle when it homes or has to clear terrain. All of it lives in rts/Rendering/Env/NanoParticles: NanoParticleConfig every tunable, in one place NanoParticleDefs the PODs the other three share NanoParticleSystem the particle store, homing, ground clamp, LuaUI batching NanoParticleEmitter how much spray a builder produces, and reclaim bursts NanoParticleRenderer shaders, buffers, culling, draw Legacy nano spray is untouched. NanoProjectile, ProjectileDrawer and NanoPieceCache have no diff at all; when the effect is off, or no shader path is usable, emission falls through to CNanoProjectile exactly as before. The simulation cannot tell the difference either way: a work tick still polls QueryNanoPiece once and draws one synced RNG value, and everything the effect adds runs off the unsynced RNG, as nano spray already did. Beyond the shader look the effect also carries, each behind its own setting: * emission proportional to buildSpeed * buildPower rather than one particle per work tick, so a builder's spray tracks the work it is doing instead of its nano piece count, spread round-robin over its pieces * NanoParticlesHoming, particles following moving targets * NanoParticlesGroundClamp, particles routed over intervening terrain * NanoParticlesReclaimBurst, a burst when reclaiming a unit finishes, sized by metal cost and split across the builders that contributed * NanoParticlesUpdateLuaUI, the batched callin added in the previous commit The per-unit emission accumulators and the reclaim contributor tracking are owned by the emitter rather than by CBuilder/CUnit: they are unsynced presentation state, they must not be serialised, and no part of the sim needs to know they exist. No sim class gains a member. The renderer is heap-allocated behind a pointer, as the other GL-owning drawers are. A VBO's constructor calls VBO::IsSupported(), which latches the GLAD extension flags into function-local statics on its first call; constructing one before GLAD has loaded latches them all to false and silently turns every VBO in the process into a no-op. Particles show on the minimap as the legacy ones do, reusing the vertex arrays the world pass already filtered so it costs one walk and no visibility work, and filling the shared projectile minimap buffer rather than adding a draw of its own. The spawn gate is the effect's own rather than the legacy proportional one, which throttles from the first particle and so makes emission approach the budget asymptotically instead of scaling with NanoParticlesRate. Tunables are named and documented in NanoParticleConfig.h instead of being literals spread through the sources. The visual subset reaches both shader paths as uniforms, so the geometry and instanced renderers cannot drift apart. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Nano Particles: changelog for the standalone effect Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * added `Engine.FeatureSupport.nanoParticlesGL4` boolean, so games can detect that the engine has the standalone nano particle effect and retire their own Lua implementation of it. * moved changelog to: doc\pr-changelogs\3191.md * Nano Particles: added configbool: NanoParticlesTargetLostFade "Nano particles fade out and shrink when the unit they were aimed at is destroyed, cancelled, or finished, instead of flying on into nothing" (ported over this feature from the gadget as well) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n modifiers (beyond-all-reason#3336) * apply terrain speed mod to max speed for drag calculations
* Sim: avoid UB in float-to-short heading casts * Move the cast into a TAAngleToShort helper * Cast up front through FloatToHeading, assert the range
…ll-reason#3268) The early block check added in beyond-all-reason#2557 runs on a position snapped to 8 elmos, while the build itself uses Pos2BuildPos, which snaps to 16 and adds half a square for an odd footprint. For an odd footprint the check therefore evaluates a footprint 8 elmos away from the one that would be placed, and can drop a build order the build itself would accept. Pos2BuildPos is idempotent, so the later call at MoveInBuildRange is left alone rather than moved, keeping the change to the check itself.
…#3358) On Linux hosts, the game statistics stored in the replay/demo files were incorrectly written using big-endian instead of little-endian. This is due to the "__BYTE_ORDER" and "__BIG_ENDIAN" macros not being defined on Linux under some circumstances. This commit fixes this problem by including the required header <endian.h> just before the macros are used to detect host endianness.
…-all-reason#3328) * TraceRay: block rays that start below the terrain CGround::LineGroundCol returns a hit distance of 0 when the ray origin is underground, but TraceRay only accepted ground hits with a distance > 0 and CWeapon::HaveFreeLineOfFire applied the same filter to the result. A ray from an underground origin was therefore reported as unobstructed, so a weapon whose muzzle (or aim-from piece) sat inside a cliff passed the line-of-fire test, stopped, and could never fire (beyond-all-reason#3242), and Spring.GetUnitWeaponHaveFreeLineOfFire told game code the same (beyond-all-reason#3301). Accept 0 as a hit in both places: the ray is blocked at its origin. CCannon::HaveFreeLineOfFire had the same pattern with TrajectoryGroundCol, which also reports 0 for an origin below the terrain, but tests against GetApproximateHeight; on rough ground that can lie above a muzzle that is clear of the interpolated surface. Reject an origin below GetHeightReal explicitly instead, the test the fire-time check already applies to the muzzle, and keep ignoring the coarse 0 from the trajectory scan. The underground test of LineGroundCol itself compared the origin against the corner vertex of its heightmap square. Next to a steep cliff that vertex can be far above an origin that is well clear of the ground, which skipped the whole ground trace. Compare against the interpolated surface instead, and treat an origin exactly on the surface as above ground; LineGroundSquareCol still reports a hit at distance 0 when such a ray points into the ground. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Weapons: consistently reject underground line-of-fire sources Reject sources below real terrain height at the start of both the base weapon and cannon line-of-fire checks when ground avoidance is enabled. This matches the existing pre-fire muzzle rejection, including when the target is within explosion range, and covers the early-return cases. Preserve the base weapon AoE exception for surface sources and later ground hits, including zero-distance hits directed into terrain. Validation: Podman engine-headless build and git diff --check passed. In-game validation remains pending. AI assistance: OpenAI Codex prepared the changes and ran the build. * Weapons: explain differing zero-distance ground checks Document why the accurate ray trace accepts zero-distance ground hits while the approximate cannon trajectory scan ignores them. No behavior changes. Validation: git diff --check passed; reviewed comment-only diff. AI assistance: OpenAI Codex wrote the comments and checked the diff. * Weapons: clarify line-of-fire terrain comments --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* Fix cleanup after early game load failures * Clear game pointer after constructor failure
…ond-all-reason#3359) * Lua: AllowQuit callin, so a game can hold a window close request Closing the window (close button, Alt+F4) set globalQuit directly, with no way for a game to ask about unsaved work first. Add an unsynced control callin AllowQuit, asked on SDL_WINDOWEVENT_CLOSE and SDL_QUIT. Any handle returning false keeps the game running (logged); handles without the callin allow, so nothing changes for games that don't use it. Not asked while still loading, since events are pumped from the load thread there. Spring.Quit and /quitforce are unchanged and never ask. * Add changelog entry
… center of the screen (beyond-all-reason#3186)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automated upstream sync hit conflicts. Merge
master(now level with beyond-all-reason/RecoilEngine) intomainand resolve here.