Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion BDArmory/Distribution/GameData/BDArmory/ChangeLog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@
- Warheads:
- Add "bulletDmgMult" field to BDCustomWarhead modules, allowing their damage to be tuned.
- Bombs:
- Add Symmetrical Firing option to bombs to enable simultaneously dropping a symmetry twin bomb at the same time the selected bomb is dropped.
- Add Symmetrical Firing option to bombs to enable simultaneously dropping a symmetry twin bomb at the same time the selected bomb is dropped.
- Lasers:
- Lasers now do less damage over distance based on atmospheric and water attenuation. LASER_ATM_GAMMA and LASER_WATER_GAMMA in the settings.cfg control this relationship. Drop-off is given by exp(-GAMMA * DISTANCE * NORMALIZED_ATM_DENSITY), NORMALIZED_ATM_DENSITY is 1 for water transmission. At sea level, lasers will do more damage before 2.5 km, and less damage past 2.5 km. Underwater, lasers will not work past ~10m. In vacuum, lasers will do more damage than previously and be unaffected by attenuation.
- New realism-based formula for microwave damage (lasers with conicAoE = true) based on Friis transmission equation with conical directivity gain based on tanAngle value. Microwave damage is unaffected by atmosphere, but drops off very fast with distance. Microwaves are extremely strong at short ranges.
- Apply tanAngle and distance attenuation to electro-lasers, heat rays, and HE pulse lasers.
- Add laser configuration options to ABL.cfg part file for reference.
- Missiles:
- Fix graphical glitch where Helmet Mounted Display/Sight appearing in WM modules despite there not being a cockpit with a HMD.
- Fix NREs that would occur when a CLOS missile has its guiding targeting pod destroyed.
Expand Down
16 changes: 13 additions & 3 deletions BDArmory/Distribution/GameData/BDArmory/Parts/ABL/ABL.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,24 @@ MODULE

maxEffectiveDistance = 5000
maxTargetingRange = 5000
maxDeviation = 0.0125
maxDeviation = 0.0125 // Affects pulse divergence for pulse lasers, otherwise is unused for lasers

ammoName = ElectricCharge
requestResourceAmount = 350

weaponType = laser
laserDamage = 1600
tanAngle = 0.0001 //controls how quickly damage scales down with distance
laserDamage = 1600 // Base laser or microwave damage. For lasers, final damage is exp(-gamma * distance * atmDensity / 1.225) / (1 + pi * tanAngle^2 * distance^2), where gamma is LASER_ATM_GAMMA for in-atmosphere or LASER_WATER_GAMMA for underwater
tanAngle = 0.0001 // Controls how quickly damage scales down with distance for lasers and microwaves, is converted to directivity for microwave weapons (Directivity = 1/(1 - cos(atan(tanAngle))))
// LaserGrowTime = -1 // Time laser to be fired to go from base to max damage
// DynamicBeamColor = false // Beam color changes longer laser fired, for growlasers
// beamScrollRate = 0.5f // Beam texture scroll rate, for plasma beams, etc
// beamScalar = 0.01f // X scaling for beam texture. lower is more stretched
// pulseLaser = false // Pulse vs beam
// HEpulses = false // Do the pulses have blast damage
// HeatRay = false // Laser heats parts instead of doing damage
// electroLaser = false // Drains EC from target/induces EMP effects
// conicAoE = false // Is a Microwave Emitter or similar that does a conical AoE instead of a linear beam, use microwave damage formula (laserDamage * Directivity * lamda / (4 * pi * distance)^2), lambda = 0.125m (2.4 GHz)
Copy link
Owner

Choose a reason for hiding this comment

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

I don't think this can be correct. The base laser damage is essentially the damage at point blank range, which should already take into account the gain (e.g., base damage ∝ Pt*Gt, where gain is G=ηD), so directivity shouldn't be an extra factor here, otherwise a non-divergent beam would transmit infinite power.
(I think the definition of gain here actually breaks down when the directivity is so high compared to the base beam width since the only way to avoid the gain going to infinity for a constant power input and constant radiation efficiency is if the beam width narrows to 0.)

However, without this factor, the formula at https://en.wikipedia.org/wiki/Friis_transmission_equation#Contemporary_formula becomes damage = base damage * Gr² * (λ/4πd)², and since the article also states that the condition d >> λ is required (and presumably Gr=1 since the target isn't a receiving antenna), then that factor is miniscule.

// beamFOV = 20 // FoV angle of the beam when conicAoE = true. Targets inside FOV are affected, damage is still controlled by tanAngle

isAPS = true
APSType = missile
Expand Down
2 changes: 2 additions & 0 deletions BDArmory/Settings/BDArmorySettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ public class BDArmorySettings
[BDAPersistentSettingsField] public static float ARMOR_MASS_MOD = 1f; //Armor mass multiplier
[BDAPersistentSettingsField] public static bool KERBAL_ERA = true;
[BDAPersistentSettingsField] public static float HMDCost = 2000f;
[BDAPersistentSettingsField] public static float LASER_ATM_GAMMA = 0.000341f; // Transmission factor for laser in atmosphere. Gives 250W on target (melts metal) at 512km from 1MW laser at 65kft altitude, based on https://en.wikipedia.org/wiki/Boeing_YAL-1#Intercept_sequence. Also gives ~0.425 scaling at 2.5km (prior scaling factor)
[BDAPersistentSettingsField] public static float LASER_WATER_GAMMA = 0.38f; // Transmission factor for laser in water. 15% at 5m, ~2% at 10m, ~0% at 100m
#endregion

#region FX
Expand Down
56 changes: 38 additions & 18 deletions BDArmory/Weapons/ModuleWeapon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,7 @@ public float GetEngageRange()
public float tanAngle = 0.0001f;
//Angle of divergeance/2. Theoretical minimum value calculated using θ = (1.22 L/RL)/2,
//where L is laser's wavelength and RL is the radius of the mirror (=gun).
private float microwaveDirectivity; // Directivity (gain) for microwave weapons, calculated from tanAngle variable

//audioclip paths
[KSPField]
Expand Down Expand Up @@ -2679,7 +2680,7 @@ private void LaserBeam(string vesselname)
if (!useRippleFire || !pulseLaser || fireState.Length == 1 || (useRippleFire && i == barrelIndex))
{
float damage = 0;
float initialDamage = laserDamage * 0.425f;
float initialDamage = laserDamage;
Transform tf = fireTransforms[i];
LineRenderer lr = laserRenderers[i];
Vector3 rayDirection = tf.forward;
Expand Down Expand Up @@ -2735,9 +2736,10 @@ private void LaserBeam(string vesselname)

KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards<KerbalEVA>();
Part p = eva ? eva.part : hit.collider.gameObject.GetComponentInParent<Part>();
float distance = hit.distance;
initialDamage = (laserDamage > 0) ? LaserDamage(laserDamage, tanAngle, distance, tf.position, hit.point) : laserDamage;
if (p && p.vessel && p.vessel != vessel)
{
float distance = hit.distance;
if (instagib)
{
p.AddInstagibDamage();
Expand All @@ -2754,7 +2756,7 @@ private void LaserBeam(string vesselname)
/////////////////////////////////////////////////
if (!VesselModuleRegistry.IgnoredVesselTypes.Contains(p.vessel.vesselType))
{
float EMPDamage = laserDamage * (pulseLaser ? 1 : TimeWarp.fixedDeltaTime) * (BDArmorySettings.DMG_MULTIPLIER / 100);
float EMPDamage = initialDamage * (pulseLaser ? 1 : TimeWarp.fixedDeltaTime) * (BDArmorySettings.DMG_MULTIPLIER / 100);
Part closestCommand = null;
float distToCommandSqr = float.PositiveInfinity; //lets find out which command part is closest to the hit
Vector3 commandDir = Vector3.zero;
Expand Down Expand Up @@ -2884,9 +2886,6 @@ private void LaserBeam(string vesselname)
HitpointTracker armor = p.GetComponent<HitpointTracker>();
if (laserDamage > 0)
{
var angularSpread = tanAngle * distance; //Scales down the damage based on the increased surface area of the area being hit by the laser. Think flashlight on a wall.
initialDamage = laserDamage / (1 + Mathf.PI * angularSpread * angularSpread) * 0.425f;

if (armor != null)// technically, lasers shouldn't do damage until armor gone, but that would require localized armor tracking instead of the monolithic model currently used
{
damage = (initialDamage * (pulseLaser ? 1 : TimeWarp.fixedDeltaTime)) * Mathf.Clamp((1 - (BDAMath.Sqrt(armor.Diffusivity * (armor.Density / 1000)) * armor.Armor) / initialDamage), 0.005f, 1); //old calc lacked a clamp, could potentially become negative damage
Expand Down Expand Up @@ -2985,13 +2984,11 @@ private void LaserBeam(string vesselname)
else
{
if (electroLaser || HeatRay) continue;
var angularSpread = tanAngle * hit.distance; //Scales down the damage based on the increased surface area of the area being hit by the laser. Think flashlight on a wall.
initialDamage = laserDamage / (1 + Mathf.PI * angularSpread * angularSpread) * 0.425f;
if (!BDArmorySettings.PAINTBALL_MODE) ProjectileUtils.CheckBuildingHit(hit, initialDamage, pulseLaser);
if (HEpulses)
{
ExplosionFx.CreateExplosion(hit.point,
(laserDamage / 10000),
(initialDamage / 10000),
explModelPath, explSoundPath, ExplosionSourceType.Bullet, 1, null, vessel.vesselName, null);
}
}
Expand All @@ -3011,7 +3008,7 @@ private void LaserBeam(string vesselname)
if (HEpulses)
{
ExplosionFx.CreateExplosion(tf.position + rayDirection * raycastDistance,
(laserDamage / 10000),
(initialDamage / 10000),
explModelPath, explSoundPath, ExplosionSourceType.Bullet, 1, null, vessel.vesselName, null);
}
}
Expand All @@ -3028,6 +3025,21 @@ private void LaserBeam(string vesselname)
}
}

// Adjusts laser damage based on tanAngle, distance, firing and hit points
private float LaserDamage(float laserDamage, float tanAngle, float distance, Vector3 firingPoint, Vector3 hitPoint)
{
bool underwater = FlightGlobals.currentMainBody.ocean && FlightGlobals.getAltitudeAtPos(firingPoint) < 0 || FlightGlobals.currentMainBody.ocean && FlightGlobals.getAltitudeAtPos(hitPoint) < 0;
float firingDensity = (float)FlightGlobals.getAtmDensity(FlightGlobals.getStaticPressure(firingPoint), FlightGlobals.getExternalTemperature(firingPoint));
float hitDensity = (float)FlightGlobals.getAtmDensity(FlightGlobals.getStaticPressure(hitPoint), FlightGlobals.getExternalTemperature(hitPoint));
float atmDensity = (firingDensity + hitDensity) / 2f; // Average densities instead of complicated integral along firing path

float gamma = underwater ? BDArmorySettings.LASER_WATER_GAMMA : BDArmorySettings.LASER_ATM_GAMMA;
float normDensity = underwater ? 1f : Mathf.Max(0f, atmDensity / 1.225f);
float transmission = Mathf.Exp(-gamma * distance * normDensity);
float angularSpread = tanAngle * distance; //Scales down the damage based on the increased surface area of the area being hit by the laser. Think flashlight on a wall.
return laserDamage * transmission / (1 + Mathf.PI * angularSpread * angularSpread);
Copy link
Owner

Choose a reason for hiding this comment

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

As I mentioned on discord, I'm not convinced that this is the right formula for the "flashlight on a wall" analogy.
I think the proper factor ought to be 1/(1+distance/baseWidth*tanAngle)². Apparently the 1/(1+π*angularSpread²) formula comes from commit 53f086d way back in 2015.

}

//Conic AoE 'beam' for AoE beam weapons - tractor beams, microwave EMP, heatrays, etc.
private void MicrowaveBeam(string vesselname)
{
Expand All @@ -3041,7 +3053,7 @@ private void MicrowaveBeam(string vesselname)
}
WeaponFX();
float damage = 0;
float initialDamage = laserDamage * 0.425f;
float initialDamage = laserDamage;
var beamLength = engageRangeMax / 1000;
var beamAngle = Mathf.Tan(beamFOV * Mathf.Deg2Rad) * beamLength; //assumes a default 1km long, 45deg angle cone model
//Also, TOD - add conic AoE ingo to the GetInfo weapon infocard
Expand All @@ -3066,19 +3078,18 @@ private void MicrowaveBeam(string vesselname)
if (VesselModuleRegistry.IgnoredVesselTypes.Contains(loadedvessels.Current.vesselType)) continue;

if (Vector3.Angle(loadedvessels.Current.CoM - fireTransforms[0].transform.position, fireTransforms[0].forward) > beamFOV / 2f) continue;
if (loadedvessels.Current.IsUnderwater()) continue; //would microwaves work underwater...?
if (loadedvessels.Current.IsUnderwater()) continue; // Microwaves don't work underwater
if (!friendlyFire) //don't affect friendly targets. Something something phased array dynamic beam shaping
{
var wms = VesselModuleRegistry.GetModule<MissileFire>(loadedvessels.Current);
if (wms == null || wms.Team == WeaponManager.Team) continue;
}
if (!loadedvessels.Current.Splashed && vessel.IsUnderwater()) //not sure if a water transition should serve as a barrier, but easily commented out
if (!loadedvessels.Current.Splashed && vessel.IsUnderwater()) //water transition is barrier
continue;
if (loadedvessels.Current == vessel) continue;
float distance = (loadedvessels.Current.CoM - fireTransforms[0].transform.position).magnitude;
if (distance > maxTargetingRange) continue;
var angularSpread = tanAngle * distance; //Scales down the damage based on the increased surface area of the area being hit by the laser. Think flashlight on a wall.
initialDamage = (laserDamage * 0.425f) / (1 + Mathf.PI * angularSpread * angularSpread);
initialDamage = MicrowaveDamage(laserDamage, microwaveDirectivity, distance);

if (electroLaser)
{
Expand Down Expand Up @@ -3178,7 +3189,7 @@ private void MicrowaveBeam(string vesselname)
if (HEpulses)
{
ExplosionFx.CreateExplosion(h.point,
(laserDamage / 10000),
(initialDamage / 10000),
explModelPath, explSoundPath, ExplosionSourceType.Bullet, 1, null, vessel.vesselName, null, Hitpart: hitPart);
}
if (HeatRay)
Expand Down Expand Up @@ -3250,6 +3261,15 @@ private void MicrowaveBeam(string vesselname)
}
}

// Adjusts microwwave damage based on tanAngle, distance, firing and directivity (gain)
private float MicrowaveDamage(float baseDamage, float directivity, float distance)
{
float wavelength = 0.124913524166667f; // 2.4 GHz wavelength, 299792458f / 2400000000
float sqrTerm = wavelength / (4 * Mathf.PI * Mathf.Max(distance, 1f));
float finalDamage = baseDamage * Mathf.Min(directivity * (sqrTerm * sqrTerm), 1000f); // Clamp max damage at very short ranges, https://en.wikipedia.org/wiki/Friis_transmission_equation
return finalDamage;
}

public void SetupLaserSpecifics()
{
//chargeSound = SoundUtils.GetAudioClip(chargeSoundPath);
Expand All @@ -3259,6 +3279,7 @@ public void SetupLaserSpecifics()
}
Color laserColor = GUIUtils.ParseColor255(projectileColor);
laserColor.a = laserColor.a / 2;
microwaveDirectivity = 2f / (1 - Mathf.Cos(Mathf.Atan(tanAngle))); // Directivity for microwaves, based on conical beam https://en.wikipedia.org/wiki/Directivity#Relation_to_beam_width
if (conicAoE)
{
var cone = GameDatabase.Instance.GetModel(laserModelPath);
Expand Down Expand Up @@ -6144,8 +6165,7 @@ IEnumerator KillIncomingProjectile(PooledBullet shell, PooledRocket rocket)
if (delayTime < 0)
{
delayTime = rocket != null ? 0.5f : (shell.bulletMass * (1 - Mathf.Clamp(shell.tntMass / shell.bulletMass, 0f, 0.95f) / 2)); //for shells, laser delay time is based on shell mass/HEratio. The heavier the shell, the more mass to burn through. Don't expect to stop sabots via laser APS
var angularSpread = tanAngle * targetDistance;
delayTime /= ((laserDamage / (1 + Mathf.PI * angularSpread * angularSpread) * 0.425f) / 100);
delayTime /= LaserDamage(laserDamage, tanAngle, targetDistance, part.rb.position, targetPosition) / 100f;
if (delayTime < TimeWarp.fixedDeltaTime) delayTime = 0;
}
yield return new WaitForSeconds(delayTime);
Expand Down