Skip to content

Conversation

@BillNyeTheIE
Copy link

@BillNyeTheIE BillNyeTheIE commented Jan 10, 2026

  • Fix TGP NRE
  • Fix robotic parts with turretID = 0 getting sent back to original position by AI
  • Massive RWR overhaul
  • Improved explosion modelling for both standard and shaped charge explosions, particularly with regards to armor, as well as very large explosions
  • More granular control over notch chaff effects
  • Improve uncagedLock behavior and new HMD
  • Various minor bug fixes
  • Update KKV to use BDCustomWarhead
  • Update AIM-9 to use uncagedLock as well as improve cfg documentation

BillNyeTheIE and others added 30 commits December 19, 2025 21:37
New "torqueLimiter" and "torqueMargin" fields in missiles to give greater user control
- Fix wrong part used in intermediate armor calculations
- Fix non-early continue of intermediate armor calcs for repeated parts
- Fix TimeToImpact for the shaped charge jet
- Fix ModuleTargetingCamera using the wrong TargetInfo
- Completely overhaul the RWR/MWS system
- Fix issues with ToggleGuardMode (namely turrets not getting de-activated and RWR not always being enabled)
- Remove redundant blast calculations (multiple calculations of the same logs and cube roots)
- Swap to using local Vector3s in one radar function
- Replace GetUpDirection with vessel.up
- Missing parentheses
- More efficient code for MWS by splitting it off into its own array
- Improve handling of passive missiles and handle the no-MWS detection but within visual ID range case
- Replace use of TargetSignatureData in RWR with specialized struct
- Use bitmasks instead of lists for antiradTargets
- Use circular queue based system for missile locks and launch warnings (as both have fixed durations)
- Add support for anti-rad missiles in PD code
- AI will detect SARH launches outside of MWS range
- Remove incorrectly repeated code
- Add comments to explain what's being done
Both orbit fields appear to be unused?
- Swap to using ValueTuple for GetMissilesAway() return type, with named fields for ease of maintenance
- Use ToggleGuardMode() in LoadedVesselSwitcher for toggling guard mode
- Locally cache results of more .transform calls
- Create function that makes the two ParseRadarLimits calls to simplify maintenance
- Add additional call to said function in GetInfo as even moving the check outside of the HighLogic.LoadedSceneIsFlight block in OnStart didn't work
- Swap notch direction from missile to the parent radar when defensive against SARH missile
- Since blastInfo.Damage gets modified, we have to use damageWithoutIntermediateParts here
- Check for missilesAway and queuedLaunches not being null before clearing them in MissileFire.OnDestroy
After fixing the armor bug, it was noted that current blast mechanics would significantly under-estimate blast penetration. Despite being able to, according to the equations we use, blow through the armor, the blast is basically completely stopped a couple of m past the armor. Assessing blast pressure at the part, and then scaling overall blast pressure by this fixes this issue.

It must be noted that as it is right now, big explosions are consistently overpenetrating, which is something to look into and re-assess in terms of the equations.
- Add a factor of 4 to blast resistance, which seems to approx. match expected values for armor pen
- Non-`uncagedLock` missiles should no longer be able to be queued on targets via IRST/radar
- Account for offset in the lookRay direction
Set up infrastructure for dynamic boresight scale (though all commented out as currently, it looks terrible).
- Add fuze details to missile GetInfo tab
- Fix "bugs" due to multiple parts named the same thing as the missile causing issues (still only prevents cases where only one of those parts is a missile), as well as cases were the part is located in a cfg file with multiple parts in it
Make `tntMass` non-persistent for missiles
- Check for when cursor stops being on menu
- IRST de-prioritized compared to radar for IR slaving
- Perform nullcheck for targetVessel in GMR -> laser
- Perform nullcheck for VRD in GMR -> laser
Replace Hardcoded bitshifts with ToBits for RWR/anti-rad for better maintainability, as per DocNapper's suggestion.
- Involve fixedDeltaTime in the equation to remove framerate dependency
- Use MissileReferenceTransform
#LOC_BDArmory_Settings_ArmorExplosivePenetrationResistanceMultiplier = Exp. Armor Pen. Mult.
#LOC_BDArmory_Settings_BuildingDamageMultiplier = Building Damage Multiplier
#LOC_BDArmory_Settings_ImplosiveDamageMultiplier = Implosive Damage Multiplier
#LOC_BDArmory_Settings_ExplosionImpulseMultiplier = Exp. Impulse Multiplier
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: sync this to the other localisation files once merged.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I was thinking about this, but perhaps it'd be better to sync these to the other localization files as just the English text, rather than what it was previously translated to, since otherwise translators might miss the fact that this was changed, and that the meaning is different.

- Ensure TGP actually gets placed on the target
- Remove degree symbols from missile debug as they were cluttering more than helping
- Do not affect targeting data when not locked on
- Set INS targeting data
- Ensure _lockFailTimer is reset
- Swap over everything to use lockFailTimer
- Remove drag mode change inconsistencies
- Fix NRE in GMR -> heat caused by vesselTransform.up call due to targetVessel no longer existing
- Fix NRE in GMR -> dumbfire while DEBUG_MISSILES is on caused by targetVessel no longer existing
debugGuidanceTarget was not getting update for non-existent radar targets leading to confusion with regards to the debug output
GuardMode() was not including custom turrets when performing the pre-GMR launch authorization check. Also moved the turret checks in front of the GetLaunchAuthorization check as those are faster
- Account for some edge cases
- Corrected vesselPos use instead of vesselTransformPos
- Replaced missed hardcoded bitshift
- Corrected PN and APN target calcs
- Downgraded GetVesselRadarCrossSection() infinite loop warnings to simply log entries
- Remove all SlaveTurrets() calls for custom turrets, given that they have aim commands regardless
- Replace these calls with the same slavedGuard mechanic as regular missile turrets
- Create targetData for INS missiles where it's needed
- Do not set TargetAcquired = false in UpdateTerminalGuidance, let the guidance types do that
{
vesselRadarData.SlaveTurrets();
//vesselRadarData.SlaveTurrets();
for (int i = 0; i < ml.customTurret.Count; i++)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot of duplicate code here setting ml.customTurret[i].slavedGuard to true or false. I'd suggest putting it into a single function, e.g.,

public void SlaveCustomTurrets(bool slaved)
{
    for (int i = 0; i < customTurret.Count; i++)
    {
        if (customTurret[i] == null) continue;
        if (customTurret[i].vessel != vessel) continue;
        customTurret[i].slavedGuard = slaved;
    }
}

in MissileBase.cs and just use ml.SlaveCustomTurrets(true); here to make this easier to maintain in the future.

Similarly, the while loop for aiming the slaved turrets (both custom and MissileTurrets) could likewise be moved into common coroutines for ease of maintenance, which would reduce these blocks to something like

ml.SlaveCustomTurrets(true);
yield return ml.AimCustomTurretToTarget(targetVessel, Mathf.Max(targetScanInterval / 2f, 2));
ml.SlaveCustomTurrets(false);

The slaving calls could even be moved inside the coroutine if those are always required, making these all into one-liners where MissileBase.cs (and MissileTurret.cs for regular turrets) contains something like

public IEnumerator AimCustomTurretToTarget(Vessel target, float timeout)
{
    SlaveCustomTurrets(true);
    var wait = new WaitForFixedUpdate();
    float angle = 999;
    float turretStartTime = Time.time;
    while (Time.time - turretStartTime < timeout && targetVessel && angle > 5)//customTurret[0].fireFOV
    {
        for (int i = 0; i < customTurret.Count; i++)
        {
            if (customTurret[i] == null) continue;
            if (customTurret[i].vessel != vessel) continue;
            angle = VectorUtils.Angle(MissileReferenceTransform.forward, customTurret[i].slavedTargetPosition - MissileReferenceTransform.position);
            customTurret[i].slavedTargetPosition = MissileGuidance.GetAirToAirFireSolution(this, target.CoM, target.Velocity(),
                customTurretLoft, customTurretLoftFac);
            customTurret[i].AimToTarget(customTurret[i].slavedTargetPosition);
        }
        yield return wait;
        //Debug.Log($"[PD Missile Debug - {vessel.GetName()}] bringing radarMsl custom turret to bear, angle {angle:F2}...");
    }
    SlaveCustomTurrets(false);
}

and a similar function for taking in TargetSignatureData instead of Vessel.

I see some of the turret aiming code may be doing other stuff that would make this more complicated, but I think it should generally be possible to condense most of these large blocks of code to common functions that are easier to maintain.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrrrm I'm trying to implement this but it'll need some kind of method to tell GMR it has completed before GMR can go about trying to fire the missile. I was thinking of using a boolean field that it can write to which I can then read from GMR, but it's getting a bit convoluted in terms of predicate inputs for the different conditionals and if we ever want to allow simultaneous launches a single field or even an array of fields is gonna be a bit messy to deal with. I think it might be wiser to just keep the while loops on the outside.

I have packaged up the turret aiming code for all missiles into a single function in MissileBase though (with an override in MissileLauncher to account for the additional turret types they can be mounted on), so it is still more maintainable, with only a single loop required.

- Use vessel smallest dimension to set crossrange error and largest dimension to set range error
- Use proper, frame-corrected positions
- Also fix the issue of dead targets resulting in hanging mid-air TGP locks
- Re-organize the function so it looks a bit cleaner
- Return early if possible
- Deprecate StabilizeNextFrame since that's no longer required (and even counterproductive)
- Remove CoM == Vector3.zero check (why was this a thing?)
Various calculations have both from and to vectors that are Vector3ds, this saves some precision loss from the casts
- RotateTowards has some precision issues with small angles, so replace those instances with VectorUtils.Angle and Slerp
- Also replaced RotateTowards in the turret lofting code
- Removed unnecessary comment now that the issue is fixed
- Correct RotateTowards using `maxMagnitudeDelta` despite the design of the function being different
- Pre-aim laser turrets prior to performing laser targeting, to provide a better lock opportunity
- Fix missing factor of 0.001 in ExplosionFX
- Rename "explosionDamage" to "explosionProjectile" in AddBallisticDamage to more clearly delineate its intended purpose as a flag for fragmentation, spall, etc. from explosions
- Apply said flag to shrapnel damage
This version of RotateTowards hasn't produced any noticeable improvements upon Vector3.RotateTowards. However, it should still be noted that the case where the angle between two vectors is less than maxRadiansDelta, and where precision is critical, one should skip RotateTowards and simply use the second vector, particularly in cases where the vector magnitude is irrelevant.
- Consolidate all slavedGuard toggles into a single function
surfaceDetected = false;
// If we hit nothing but air, then only stay locked if we're at the gimbal limit
// or we're in the process of slewing to the target
groundStabilized = groundStabilized && (gimbalLimitReached || slewingToPosition);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be written more succinctly as

groundStabilized &= gimbalLimitReached || slewingToPosition;

return (float)(Math.Acos(num2) * 57.295779513082325);
}

/// <summary>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These could be combined to the following without losing any performance:

MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Angle(Vector3d from, Vector3d to)
{
    double num = from.sqrMagnitude * to.sqrMagnitude;
    if (num < 1e-30)
    {
        return 0f;
    }

    double num2 = BDAMath.Clamp(Vector3d.Dot(from, to) / Math.Sqrt(num), -1.0, 1.0);
    return (float)(Math.Acos(num2) * 57.295779513082325);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Angle(Vector3 from, Vector3 to) => Angle((Vector3d)from, (Vector3d)to);
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Angle(Vector3 from, Vector3d to) => Angle((Vector3d)from, to);
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Angle(Vector3d from, Vector3 to) => Angle(from, (Vector3d)to);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mentioned this in my commit, but I was thinking, given that Vector3 is automatically (as far as I'm aware) typecast to Vector3d when passed into a Vector3d function, would these lambda expressions even be needed? Does it change how the typecast functions?

- Consolidate a bunch of oft-repeated code into functions
- Don't let PTPR go on forever
- Collapse different Angle() methods to one using lambda expressions (actually now that I think about it, it might be fine just to only have a Angle(Vector3d, Vector3d) function, as Vector3 gets automatically typecast into Vector3d as required...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants