Weapons: Fix muzzle below terrain reporting free line of fire - #3328
Open
eun-ice wants to merge 1 commit into
Open
Weapons: Fix muzzle below terrain reporting free line of fire#3328eun-ice wants to merge 1 commit into
eun-ice wants to merge 1 commit into
Conversation
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>
This was referenced Sep 5, 2026
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.
Units whose muzzle is clipping inside terrain stop moving and try to shoot, because
CWeapon::HaveFreeLineOfFireandCCannon::HaveFreeLineOfFirereturn the shot as unobstructed if it starts below terrain.Partly addresses #3242.
Likely addresses #3301 (see below).
Problem
CGround::LineGroundColreturns a hit distance of 0 when the ray origin is below the terrain.TraceRayonly accepted ground hits withgroundLength > 0.0f, andCWeapon::HaveFreeLineOfFireapplied the same> 0filter to the returned distance. A ray that starts underground was therefore reported as unobstructed.CCannon::HaveFreeLineOfFirehad the identical pattern withCGround::TrajectoryGroundCol, which also reports 0 for that case.Consequences:
Spring.GetUnitWeaponHaveFreeLineOfFiretells game code the same thing.Additionally the underground test in
LineGroundColcompared the origin against the corner vertex of its heightmap square rather than the interpolated surface. Next to a steep cliff that vertex can sit far above an origin that is well clear of the ground, and then the whole ground trace was skipped. That is the shape of #3301: a mech at the foot of a cliff reports a free line of fire to a unit on top of it as soon as it moves close to the cliff face.Spring.TraceRayGroundBetweenPositions, which @FLOZi swapped in, treats 0 as a hit and so looked correct in the same situation.Change
TraceRay: accept a ground distance of 0 as a hit (the ray is blocked at its origin). Only -1 means "no ground hit".CWeapon::HaveFreeLineOfFire: same for the returned distance.CMissileLauncheralready handled it.CCannon::HaveFreeLineOfFire: reject a source belowGetHeightRealexplicitly, the test the fire-time check already applies to the muzzle, and keep ignoring the 0 fromTrajectoryGroundCol. That scan usesGetApproximateHeight, which on rough ground lies above sources a couple of elmo clear of the interpolated surface; accepting its 0 blocked 48 above-ground probe points that are free today.CGround::LineGroundCol: the underground test compares against the interpolated height (InterpolateCornerHeight) and treats an origin exactly on the surface as above ground.LineGroundSquareColstill returns a hit at distance 0 when such a ray points into the ground, so the "unit position on the surface" case keeps working for rays pointing up and is blocked for rays pointing down.Callers that scan ground with
TraceRayareHaveFreeLineOfFire,BeamLaser,LightningCannon,Rifleand the AI callbacks. The weapons already refuse to fire when the real muzzle is belowGetHeightReal(TryTarget,preFire), so with the interpolated underground test they never reach the new "blocked at origin" branch at fire time.#3241 is stacked on this PR. Its explicit below-ground check for the predicted muzzle stays: it also covers weapons that skip ground checks (
avoidGround = false) and mirrors the fire-time test.Validation
Headless probe in the #3242 rock scenario (five Sheldons behind the rock on All That Glitters, one with its muzzle inside the cliff; gadget
dbg_lof_underground_probe.luain my workspace), same BAR checkout, before = engine without this commit:The probe creates a Flea (BeamLaser,
CWeapon::HaveFreeLineOfFire) and a Pawn (Cannon,CCannon::HaveFreeLineOfFire) and callsSpring.GetUnitWeaponHaveFreeLineOfFirewith explicit source positions towards the Fatboy.Spring.TraceRayGroundBetweenPositionsfrom the same underground sourcesFull rock reproducer (hold position and maneuver) on master + this commit: the cliff Sheldon's pre-aim test now fails (
tryTarget=false lofAim=false lofMuzzle=false), so it no longer stops believing it has a shot. Getting it to walk out of the corner is what #3241 does; without it master's CommandAI still leaves it there.