From e37a5e3bf10c8fc74572d65955e9056f0fd91493 Mon Sep 17 00:00:00 2001 From: TheKurgan Date: Wed, 22 Apr 2020 15:12:08 -0400 Subject: [PATCH 1/8] Adjustment of the weight of the Saturn AL-31 engine, and a slight change to the thrust. --- .../BDArmory/Parts/SaturnAL31/SaturnAL31.cfg | 17 ++++++++--------- .../BDArmory/Parts/SaturnAL31/SaturnAL31_TS.cfg | 9 +++++++++ 2 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31_TS.cfg diff --git a/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31.cfg b/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31.cfg index 3818e5154..5c1160ac3 100644 --- a/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31.cfg +++ b/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31.cfg @@ -9,7 +9,7 @@ } rescaleFactor = 1 node_stack_top = 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 - CoMOffset = 0.0, 1.5, 0.0 + //CoMOffset = 0.0, 1.5, 0.0 TechRequired = supersonicFlight entryCost = 9000 cost = 2000 @@ -20,7 +20,7 @@ manufacturer = KTech description = A high performance jet engine with a variable geometry thrust vectoring nozzle and an afterburner for extra thrust. Based on the highly popular J-404 engine, KTech engineers saw the potential of (highly) modifying the commercial variant into a formidable powerplant for military use. After seeing the potential of the engine, the BDAc group immediately licensed it for their new MkIII test drone. attachRules = 1,0,1,0,0 - mass = 0.65 + mass = 1.05 skinInternalConductionMult = 4.0 emissiveConstant = 0.8 // engine nozzles are good at radiating. dragModelType = default @@ -47,7 +47,7 @@ exhaustDamage = True ignitionThreshold = 0.1 minThrust = 0 - maxThrust = 94 + maxThrust = 92 heatProduction = 15 useEngineResponseTime = True engineAccelerationSpeed = 0.5 @@ -154,13 +154,12 @@ machHeatMult = 20.0 velCurve - { + { key = 0 1.25 0 0 - key = 0.25 1.7 5.605438 4.559909 - key = 0.4 2 0.8646202 0.9608269 - key = 1.85 2.7 -0.01492752 -0.1344079 - key = 2.25 2.5 -1.147821 -1.864674 - key = 2.5 0 -26.83361 0 + key = 0.25 1.7 2 2 + key = 1.8 2.8 0 0 + key = 2.5 2.25 -1.75 -1.75 + key = 4 0 0 0 } atmCurve diff --git a/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31_TS.cfg b/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31_TS.cfg new file mode 100644 index 000000000..f4914e6a7 --- /dev/null +++ b/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31_TS.cfg @@ -0,0 +1,9 @@ +@PART[SaturnAL31] +{ + #@TWEAKSCALEBEHAVIOR[Engine]/MODULE[TweakScale] { } + %MODULE[TweakScale] + { + type = stack + defaultScale = 1.25 + } +} \ No newline at end of file From 8c5764cfc4066f8b8dad262ea5bbc3503aa05f4a Mon Sep 17 00:00:00 2001 From: TheKurgan Date: Sat, 25 Apr 2020 15:53:28 -0400 Subject: [PATCH 2/8] Small adjustment to Satrun engine specs --- .../GameData/BDArmory/Parts/SaturnAL31/SaturnAL31.cfg | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31.cfg b/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31.cfg index 5c1160ac3..2797fdb85 100644 --- a/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31.cfg +++ b/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31.cfg @@ -37,6 +37,8 @@ secondaryEngineID = Wet carryOverThrottle = True autoSwitchAvailable = False + primaryEngineModeDisplayName = Dry + secondaryEngineModeDisplayName = Wet } MODULE @@ -78,7 +80,7 @@ } atmosphereCurve { - key = 0 11000 0 0 + key = 0 9600 0 0 } // Jet params atmChangeFlow = True @@ -143,7 +145,7 @@ } atmosphereCurve { - key = 0 6000 0 0 + key = 0 5200 0 0 } // Jet params atmChangeFlow = True @@ -208,7 +210,7 @@ name = ModuleGimbal gimbalTransformName = Gimbal gimbalRange = 10 - gimbalResponseSpeed = 8 + gimbalResponseSpeed = 9 useGimbalResponseSpeed = true } From a8c4433d0b341c4061b6e16992825321b75bdf05 Mon Sep 17 00:00:00 2001 From: TheKurgan Date: Sat, 25 Apr 2020 16:01:22 -0400 Subject: [PATCH 3/8] Fixed Tweakscale patch error --- .../GameData/BDArmory/Parts/SaturnAL31/SaturnAL31_TS.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31_TS.cfg b/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31_TS.cfg index f4914e6a7..3774a2c5b 100644 --- a/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31_TS.cfg +++ b/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31_TS.cfg @@ -1,4 +1,4 @@ -@PART[SaturnAL31] +@PART[SaturnAL31]:NEEDS[TweakScale] { #@TWEAKSCALEBEHAVIOR[Engine]/MODULE[TweakScale] { } %MODULE[TweakScale] From b2acdcf35b70cd4760a1b36fa1619a731b642e06 Mon Sep 17 00:00:00 2001 From: TheKurgan Date: Sat, 25 Apr 2020 18:38:04 -0400 Subject: [PATCH 4/8] Modification to the .50 cal rounds to make them actually usable --- .../BDArmory/BulletDefs/BD_Bullets.cfg | 15 ++- .../Parts/browninganm2/browninganm2.cfg | 95 +++++++++---------- 2 files changed, 52 insertions(+), 58 deletions(-) diff --git a/BDArmory/Distribution/GameData/BDArmory/BulletDefs/BD_Bullets.cfg b/BDArmory/Distribution/GameData/BDArmory/BulletDefs/BD_Bullets.cfg index 39556ecaf..d6e240102 100644 --- a/BDArmory/Distribution/GameData/BDArmory/BulletDefs/BD_Bullets.cfg +++ b/BDArmory/Distribution/GameData/BDArmory/BulletDefs/BD_Bullets.cfg @@ -94,19 +94,18 @@ BULLET BULLET { + // RaufossMk211 name = 12.7mmBullet caliber = 12.7 - bulletVelocity = 890 - bulletMass = .16 - //HE Bullet Values - explosive = False - tntMass = 0 + bulletVelocity = 915 + bulletMass = .15 + explosive = True + tntMass = .01 blastPower = 0 blastHeat = 0 - blastRadius = 0 - apBulletMod = 0 + blastRadius = 0.01 + apBulletMod = 1 bulletDragTypeName = AnalyticEstimate - } BULLET diff --git a/BDArmory/Distribution/GameData/BDArmory/Parts/browninganm2/browninganm2.cfg b/BDArmory/Distribution/GameData/BDArmory/Parts/browninganm2/browninganm2.cfg index 308163c80..9c6a921e9 100644 --- a/BDArmory/Distribution/GameData/BDArmory/Parts/browninganm2/browninganm2.cfg +++ b/BDArmory/Distribution/GameData/BDArmory/Parts/browninganm2/browninganm2.cfg @@ -26,7 +26,7 @@ category = none bdacategory = Guns subcategory = 0 bulkheadProfiles = srf -title = Browning .50cal AN/M2 +title = Browning Heavy Machine Gun (AN/M3) manufacturer = Bahamuto Dynamics description = An old fixed .50 cal machine gun 50cal ammo // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision @@ -44,54 +44,49 @@ maxTemp = 3600 stagingIcon = SOLID_BOOSTER MODULE -{ - name = ModuleWeapon - shortName = Browning AN/M2 - - fireTransformName = fireTransform - - hasDeployAnim = false - hasFireAnimation = false - - roundsPerMinute = 750 - maxDeviation = 0.60 - maxEffectiveDistance = 2500 - maxTargetingRange = 5000 - - bulletDmgMult = 1.3 - bulletType = 12.7mmBullet - ammoName = 50CalAmmo - requestResourceAmount = 1 - shellScale = 0.463 - - hasRecoil = true - onlyFireInRange = false - bulletDrop = true - - weaponType = ballistic - - projectileColor = 255, 50, 0, 160 //RGBA 0-255 - startColor = 255, 105, 25, 120 - fadeColor = true - - tracerStartWidth = 0.18 - tracerEndWidth = 0.16 - tracerLength = 0 - tracerDeltaFactor = 2.75 - tracerInterval = 3 - nonTracerWidth = 0.035 - - oneShotWorldParticles = true - - maxHeat = 3600 - heatPerShot = 120 - heatLoss = 820 - - autoProxyTrackRange = 1200 - - fireSoundPath = BDArmory/Parts/browninganm2/Sounds/fire - overheatSoundPath = BDArmory/Parts/50CalTurret/sounds/turretOverheat - oneShotSound = true -} + { + name = ModuleWeapon + shortName = Browning AN/M3 + + fireTransformName = fireTransform + + hasDeployAnim = false + hasFireAnimation = false + + roundsPerMinute = 1150 + maxDeviation = 0.22 + maxEffectiveDistance = 2800 + maxTargetingRange = 4000 + + weaponType = ballistic + bulletType = 12.7mmBullet + ammoName = 50CalAmmo + + requestResourceAmount = 1 + shellScale = 0.463 + + hasRecoil = true + onlyFireInRange = true + bulletDrop = true + + projectileColor = 255, 50, 0, 160 //RGBA 0-255 + startColor = 255, 105, 25, 120 + fadeColor = true + tracerStartWidth = 0.18 + tracerEndWidth = 0.16 + tracerLength = 0 + tracerDeltaFactor = 2.75 + tracerInterval = 30 + nonTracerWidth = 0.035 + autoProxyTrackRange = 1200 + + fireSoundPath = BDArmory/Parts/browninganm2/Sounds/fire + overheatSoundPath = BDArmory/Parts/50CalTurret/sounds/turretOverheat + oneShotSound = true + + maxHeat = 3600 + heatPerShot = 115 + heatLoss = 825 + } } From 723a661b2ef4cb2218aa94cb87ae08d3a3e6348b Mon Sep 17 00:00:00 2001 From: TheKurgan Date: Wed, 20 May 2020 20:30:01 -0400 Subject: [PATCH 5/8] Fixed CoMOffset on Saturn engine --- .../GameData/BDArmory/Parts/SaturnAL31/SaturnAL31.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31.cfg b/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31.cfg index 2797fdb85..e0855afcb 100644 --- a/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31.cfg +++ b/BDArmory/Distribution/GameData/BDArmory/Parts/SaturnAL31/SaturnAL31.cfg @@ -9,7 +9,7 @@ } rescaleFactor = 1 node_stack_top = 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 - //CoMOffset = 0.0, 1.5, 0.0 + CoMOffset = 0.0, 1.2, 0.0 TechRequired = supersonicFlight entryCost = 9000 cost = 2000 From d44395697632e8f769a4b8a066ff86095387af30 Mon Sep 17 00:00:00 2001 From: Kurgun Date: Tue, 26 May 2020 17:59:44 -0400 Subject: [PATCH 6/8] Fix localization regarding name change of Browning AN/M2 to An/M3 --- .../GameData/BDArmory/Localization/localization-en-us.cfg | 2 +- .../GameData/BDArmory/Localization/localization-es-es.cfg | 2 +- .../GameData/BDArmory/Localization/localization-zh-cn.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BDArmory/Distribution/GameData/BDArmory/Localization/localization-en-us.cfg b/BDArmory/Distribution/GameData/BDArmory/Localization/localization-en-us.cfg index 87b5d964d..676a3f49e 100644 --- a/BDArmory/Distribution/GameData/BDArmory/Localization/localization-en-us.cfg +++ b/BDArmory/Distribution/GameData/BDArmory/Localization/localization-en-us.cfg @@ -114,7 +114,7 @@ Localization #loc_BDArmory_part_bdammGuidanceModule_description = A missile guidance computer. Manually tune steering settings to craft's unique flight characteristics. Select a guidance mode. Select a target then enable guidance. Activate engines and stages manually. (EXPERIMENTAL) //Browning .50cal AN/M2 - #loc_BDArmory_part_bahaBrowningAnm2_title = Browning .50cal AN/M2 + #loc_BDArmory_part_bahaBrowningAnm2_title = Browning .50cal AN/M3 //An old fixed .50 cal machine gun 50cal ammo #loc_BDArmory_part_bahaBrowningAnm2_description = An old fixed .50 cal machine gun 50cal ammo diff --git a/BDArmory/Distribution/GameData/BDArmory/Localization/localization-es-es.cfg b/BDArmory/Distribution/GameData/BDArmory/Localization/localization-es-es.cfg index ebc6af91a..1a14b7576 100644 --- a/BDArmory/Distribution/GameData/BDArmory/Localization/localization-es-es.cfg +++ b/BDArmory/Distribution/GameData/BDArmory/Localization/localization-es-es.cfg @@ -114,7 +114,7 @@ Localization #loc_BDArmory_part_bdammGuidanceModule_description = Una computadora de guía de misiles. Ajuste manualmente las configuraciones de dirección a las características de vuelo únicas de la nave. Seleccione un modo de guía. Seleccione un objetivo y luego habilite la guía. Activar motores y etapas manualmente. (EXPERIMENTAL) //Browning .50cal AN/M2 - #loc_BDArmory_part_bahaBrowningAnm2_title = Browning calibre .50 AN/M2 + #loc_BDArmory_part_bahaBrowningAnm2_title = Browning calibre .50 AN/M3 //An old fixed .50 cal machine gun 50cal ammo #loc_BDArmory_part_bahaBrowningAnm2_description = Una vieja ametralladora fija calibre .50 diff --git a/BDArmory/Distribution/GameData/BDArmory/Localization/localization-zh-cn.cfg b/BDArmory/Distribution/GameData/BDArmory/Localization/localization-zh-cn.cfg index 8c9d23f5d..f13d3ddac 100644 --- a/BDArmory/Distribution/GameData/BDArmory/Localization/localization-zh-cn.cfg +++ b/BDArmory/Distribution/GameData/BDArmory/Localization/localization-zh-cn.cfg @@ -114,7 +114,7 @@ #loc_BDArmory_part_bdammGuidanceModule_description = 一台导弹制导计算机。请根据你的导弹实际性能调节各项飞行参数,设置制导模式,锁定目标启用制导,手动控制启动引擎和分级顺序即可。(实验功能) //Browning .50cal AN/M2 - #loc_BDArmory_part_bahaBrowningAnm2_title = 勃朗宁.50口径AN/M2机枪 + #loc_BDArmory_part_bahaBrowningAnm2_title = 勃朗宁.50口径AN/M3机枪 //An old fixed .50 cal machine gun 50cal ammo #loc_BDArmory_part_bahaBrowningAnm2_description = 旧式.50口径固定机枪。 From 2d121c47bb375a1487b7292db6a390ef094f96e4 Mon Sep 17 00:00:00 2001 From: SuicidalInsanity Date: Thu, 6 Aug 2020 23:01:56 -0700 Subject: [PATCH 7/8] 1.3.4.SI changes See changelog for details. ModuleWeapon rewrite MissileFire rocket compatibility Paintball Mode Part damage consequence --- BDArmory.Core/BDArmorySettings.cs | 12 +- BDArmory.Core/Extension/DamageFX.cs | 44 - BDArmory.Core/Extension/PartExtensions.cs | 160 +- BDArmory.Core/Module/HitpointTracker.cs | 85 +- BDArmory/Bullets/PooledBullet.cs | 83 +- BDArmory/FX/BulletHitFX.cs | 632 +-- BDArmory/FX/DecalEmitterScript.cs | 119 +- BDArmory/Modules/BDModularGuidance.cs | 5 +- BDArmory/Modules/BDModulePilotAI.cs | 743 +-- BDArmory/Modules/BDModuleSurfaceAI.cs | 15 +- BDArmory/Modules/MissileBase.cs | 2 +- BDArmory/Modules/MissileFire.cs | 783 ++- BDArmory/Modules/ModuleDrainFuel.cs | 55 + BDArmory/Modules/ModuleTargetingCamera.cs | 7 +- BDArmory/Modules/ModuleWeapon.cs | 5401 +++++++++++---------- BDArmory/Modules/ModuleWingCommander.cs | 6 +- BDArmory/Modules/RadarWarningReceiver.cs | 8 +- BDArmory/Modules/RocketLauncher.cs | 1400 ++---- BDArmory/Radar/VesselRadarData.cs | 8 +- BDArmory/UI/BDArmorySetup.cs | 113 +- BDArmory/UI/BDTeamSelector.cs | 6 +- BDArmory/UI/LoadedVesselSwitcher.cs | 6 +- Changelog 1.3.4-SI.txt | 100 + 23 files changed, 4934 insertions(+), 4859 deletions(-) delete mode 100644 BDArmory.Core/Extension/DamageFX.cs create mode 100644 BDArmory/Modules/ModuleDrainFuel.cs create mode 100644 Changelog 1.3.4-SI.txt diff --git a/BDArmory.Core/BDArmorySettings.cs b/BDArmory.Core/BDArmorySettings.cs index d04db0b7c..502c3e885 100644 --- a/BDArmory.Core/BDArmorySettings.cs +++ b/BDArmory.Core/BDArmorySettings.cs @@ -5,7 +5,9 @@ public class BDArmorySettings public static string settingsConfigURL = "GameData/BDArmory/settings.cfg"; [BDAPersistantSettingsField] public static bool INSTAKILL = false; - [BDAPersistantSettingsField] public static bool BULLET_HITS = true; + [BDAPersistantSettingsField] public static bool PAINTBALL = false; + [BDAPersistantSettingsField] public static bool BATTLEDAMAGE = true; + [BDAPersistantSettingsField] public static bool BULLET_HITS = true; [BDAPersistantSettingsField] public static bool BULLET_DECALS = true; [BDAPersistantSettingsField] public static int MAX_NUM_BULLET_DECALS = 200; [BDAPersistantSettingsField] public static bool SHOW_AMMO_GAUGES = false; @@ -32,7 +34,8 @@ public class BDArmorySettings [BDAPersistantSettingsField] public static float TARGET_WINDOW_SCALE_MIN = 0.50f; [BDAPersistantSettingsField] public static float TARGET_WINDOW_SCALE = 1f; [BDAPersistantSettingsField] public static float TARGET_WINDOW_SCALE_MAX = 2f; - [BDAPersistantSettingsField] public static float BDARMORY_UI_VOLUME = 0.35f; + [BDAPersistantSettingsField] public static bool STRICT_WINDOW_BOUNDARIES = false; + [BDAPersistantSettingsField] public static float BDARMORY_UI_VOLUME = 0.35f; [BDAPersistantSettingsField] public static float BDARMORY_WEAPONS_VOLUME = 0.32f; [BDAPersistantSettingsField] public static float MAX_GUARD_VISUAL_RANGE = 40000f; [BDAPersistantSettingsField] public static float MAX_ACTIVE_RADAR_RANGE = 40000f; //NOTE: used ONLY for display range of radar windows! Actual radar range provided by part configs! @@ -52,8 +55,9 @@ public class BDArmorySettings [BDAPersistantSettingsField] public static float EXP_DMG_MOD_BALLISTIC; [BDAPersistantSettingsField] public static float EXP_DMG_MOD_MISSILE; [BDAPersistantSettingsField] public static float EXP_IMP_MOD; - [BDAPersistantSettingsField] public static bool FIRE_FX_IN_FLIGHT = false; - [BDAPersistantSettingsField] public static int MAX_FIRES_PER_VESSEL = 10; //controls fx for penetration only for landed or splashed + [BDAPersistantSettingsField] public static bool FIRE_FX_IN_FLIGHT = false; + [BDAPersistantSettingsField] public static bool SELFSEALING_TANKS = false; + [BDAPersistantSettingsField] public static int MAX_FIRES_PER_VESSEL = 10; //controls fx for penetration only for landed or splashed [BDAPersistantSettingsField] public static float FIRELIFETIME_IN_SECONDS = 90f; //controls fx for penetration only for landed or splashed [BDAPersistantSettingsField] public static bool PERFORMANCE_LOGGING = false; [BDAPersistantSettingsField] public static bool AUTOCATEGORIZE_PARTS = true; diff --git a/BDArmory.Core/Extension/DamageFX.cs b/BDArmory.Core/Extension/DamageFX.cs deleted file mode 100644 index ea7112864..000000000 --- a/BDArmory.Core/Extension/DamageFX.cs +++ /dev/null @@ -1,44 +0,0 @@ -using UnityEngine; - -namespace BDArmory.Core.Extension -{ - public class DamageFX : MonoBehaviour - { - public static bool engineDamaged = false; - - public void Start() - { - } - - public void FixedUpdate() - { - if (engineDamaged) - { - float probability = Utils.BDAMath.RangedProbability(new[] { 50f, 25f, 20f, 2f }); - if (probability >= 3) - { - ModuleEngines engine = gameObject.GetComponent(); - engine.flameout = true; - engine.heatProduction *= 1.05f; - engine.maxThrust *= 0.825f; - } - } - } - - public static void SetEngineDamage(Part part) - { - ModuleEngines engine; - engine = part.GetComponent(); - engine.flameout = true; - engine.heatProduction *= 1.0125f; - engine.maxThrust *= 0.825f; - } - - public static void SetWingDamage(Part part) - { - ModuleLiftingSurface wing; - wing = part.GetComponent(); - wing.deflectionLiftCoeff *= 0.825f; - } - } -} diff --git a/BDArmory.Core/Extension/PartExtensions.cs b/BDArmory.Core/Extension/PartExtensions.cs index 7096724c3..ae34502aa 100644 --- a/BDArmory.Core/Extension/PartExtensions.cs +++ b/BDArmory.Core/Extension/PartExtensions.cs @@ -132,8 +132,10 @@ public static void ApplyHitPoints(Part p, float damage_, float caliber, float ma Debug.Log("[BDArmory]: mass: " + mass + " caliber: " + caliber + " multiplier: " + multiplier + " velocity: " + impactVelocity + " penetrationfactor: " + penetrationfactor); Debug.Log("[BDArmory]: Ballistic Hitpoints Applied : " + Math.Round(damage_, 2)); } - - //CheckDamageFX(p); + if (BDArmorySettings.BATTLEDAMAGE) + { + CheckDamageFX(p, caliber); + } } /// @@ -149,7 +151,10 @@ public static void ApplyHitPoints(Part p, float damage) if (BDArmorySettings.DRAW_DEBUG_LABELS) Debug.Log("[BDArmory]: Explosive Hitpoints Applied to " + p.name + ": " + Math.Round(damage, 2)); - //CheckDamageFX(p); + if (BDArmorySettings.BATTLEDAMAGE) + { + CheckDamageFX(p, 50); + } } /// @@ -294,11 +299,6 @@ public static Vector3 GetSize(this Part part) { var size = part.GetComponentInChildren().mesh.bounds.size; - if (part.name.Contains("B9.Aero.Wing.Procedural")) - { - size = size * 0.1f; - } - float scaleMultiplier = 1f; if (part.Modules.Contains("TweakScale")) { @@ -310,13 +310,22 @@ public static Vector3 GetSize(this Part part) return size * scaleMultiplier; } - public static bool IsAero(this Part part) - { - return part.Modules.Contains("ModuleControlSurface") || - part.Modules.Contains("ModuleLiftingSurface"); - } - - public static string GetExplodeMode(this Part part) + public static bool IsAero(this Part part) + { + return part.Modules.Contains("ModuleLiftingSurface") || + part.Modules.Contains("FARWingAerodynamicModel"); + } + public static bool IsCtrlSrf(this Part part) + { + return part.Modules.Contains("ModuleControlSurface") || + part.Modules.Contains("FARControllableSurface"); + } + public static bool IsProcpart(this Part part) + { + return part.Modules.Contains("ProceduralPart"); + } + + public static string GetExplodeMode(this Part part) { return Dependencies.Get().GetExplodeMode_svc(part); } @@ -409,21 +418,114 @@ public static float DamageReduction(float armor, float damage, bool isMissile, f return damage; } - public static void CheckDamageFX(Part part) - { - if (part.GetComponent() != null && part.GetDamagePercentatge() <= 0.35f) - { - part.gameObject.AddOrGetComponent(); - DamageFX.engineDamaged = true; - } - - if (part.GetComponent() != null && part.GetDamagePercentatge() <= 0.35f) + public static void CheckDamageFX(Part part, float caliber) + { + //what can get damaged? engines, wings, SAS, cockpits (past a certain dmg%, kill kerbals?), weapons(would be far easier to just have these have low hp), radars + + if (part.GetComponent() != null && part.GetDamagePercentatge() < 0.95f) + { + ModuleEngines engine; + engine = part.GetComponent(); + if (part.GetDamagePercentatge() >= 0.50f) + { + if (engine.thrustPercentage > 0) + { + //engine.maxThrust -= ((engine.maxThrust * 0.125f) / 100); // doesn't seem to adjust thrust; investigate + engine.thrustPercentage -= ((engine.maxThrust * 0.125f) / 100); + Mathf.Clamp(engine.thrustPercentage, 0, 1); + } + } + if (part.GetDamagePercentatge() < 0.50f) + { + if (engine.EngineIgnited) + { + engine.PlayFlameoutFX(true); + engine.Shutdown(); + engine.allowRestart = false; + } + } + } + if (part.GetComponent() != null && part.GetDamagePercentatge() < 0.95f) + { + ModuleEnginesFX engine; + engine = part.GetComponent(); + if (part.GetDamagePercentatge() >= 0.50f) + { + if (engine.thrustPercentage > 0) + { + //engine.maxThrust -= ((engine.maxThrust * 0.125f) / 100); // doesn't seem to adjust thrust; investigate + engine.thrustPercentage -= ((engine.maxThrust * 0.125f) / 100); + Mathf.Clamp(engine.thrustPercentage, 0, 1); + } + } + if (part.GetDamagePercentatge() < 0.50f) + { + if (engine.EngineIgnited) + { + engine.PlayFlameoutFX(true); + engine.Shutdown(); + engine.allowRestart = false; + } + } + } + if (part.GetComponent() != null && part.GetDamagePercentatge() > 0.125f) + { + ModuleLiftingSurface wing; + wing = part.GetComponent(); + if (wing.deflectionLiftCoeff > (caliber * caliber / 40000)) + { + wing.deflectionLiftCoeff -= (caliber * caliber / 40000); + } + } + if (part.GetComponent() != null && part.GetDamagePercentatge() > 0.125f) + { + ModuleControlSurface aileron; + aileron = part.GetComponent(); + aileron.deflectionLiftCoeff -= (caliber * caliber / 40000); + if (part.GetDamagePercentatge() < 0.75f) + { + if (aileron.ctrlSurfaceRange >= 0.5) + { + aileron.ctrlSurfaceRange -= 0.5f; + } + } + } + if (part.GetComponent() != null && part.GetDamagePercentatge() < 0.75f) { - //part.gameObject.AddOrGetComponent(); - } - } - - public static Vector3 GetBoundsSize(Part part) + ModuleReactionWheel SAS; + SAS = part.GetComponent(); + if (SAS.PitchTorque > 1) + { + SAS.PitchTorque -= (1 - part.GetDamagePercentatge()); + } + if (SAS.YawTorque > 1) + { + SAS.YawTorque -= (1 - part.GetDamagePercentatge()); + } + if (SAS.RollTorque > 1) + { + SAS.RollTorque -= (1 - part.GetDamagePercentatge()); + } + } + if (part.protoModuleCrew.Count > 0 && part.GetDamagePercentatge() < 0.50f) + { + ProtoCrewMember crewMember = part.protoModuleCrew.FirstOrDefault(x => x != null); + if (crewMember != null) + { + crewMember.UnregisterExperienceTraits(part); + crewMember.Die(); + part.RemoveCrewmember(crewMember); + //Vessel.CrewWasModified(part.vessel); + Debug.Log(crewMember.name + " was killed by damage to cabin!"); + if (HighLogic.CurrentGame.Parameters.Difficulty.MissingCrewsRespawn) + { + crewMember.StartRespawnPeriod(); + } + //ScreenMessages.PostScreenMessage(crewMember.name + " killed by damage to " + part.vessel.name + part.partName + ".", 5.0f, ScreenMessageStyle.UPPER_LEFT); + } + } + } + public static Vector3 GetBoundsSize(Part part) { return PartGeometryUtil.MergeBounds(part.GetRendererBounds(), part.transform).size; } diff --git a/BDArmory.Core/Module/HitpointTracker.cs b/BDArmory.Core/Module/HitpointTracker.cs index 10a0b7162..2387c58a1 100644 --- a/BDArmory.Core/Module/HitpointTracker.cs +++ b/BDArmory.Core/Module/HitpointTracker.cs @@ -40,7 +40,7 @@ public class HitpointTracker : PartModule private float previousHitpoints; private bool _updateHitpoints = false; private bool _forceUpdateHitpointsUI = false; - private const int HpRounding = 100; + private const int HpRounding = 25; public override void OnLoad(ConfigNode node) { @@ -150,39 +150,56 @@ private void RefreshHitPoints() } } - #region Hitpoints Functions - - public float CalculateTotalHitpoints() - { - float hitpoints; - - if (!part.IsMissile()) - { - var averageSize = part.GetAverageBoundSize(); - var sphereRadius = averageSize * 0.5f; - var sphereSurface = 4 * Mathf.PI * sphereRadius * sphereRadius; - var structuralVolume = sphereSurface * 0.1f; - - var density = (part.mass * 1000f) / structuralVolume; - density = Mathf.Clamp(density, 1000, 10000); - //Debug.Log("[BDArmory]: Hitpoint Calc" + part.name + " | structuralVolume : " + structuralVolume); - //Debug.Log("[BDArmory]: Hitpoint Calc"+part.name+" | Density : " + density); - - var structuralMass = density * structuralVolume; - //Debug.Log("[BDArmory]: Hitpoint Calc" + part.name + " | structuralMass : " + structuralMass); - //3. final calculations - hitpoints = structuralMass * hitpointMultiplier * 0.33f; - - if (hitpoints > 10 * part.mass * 1000f || hitpoints < 0.1f * part.mass * 1000f) - { - Debug.Log($"[BDArmory]: HitpointTracker::Clamping hitpoints for part {part.name}"); - hitpoints = hitpointMultiplier * part.mass * 333f; - } - - hitpoints = Mathf.Round(hitpoints / HpRounding) * HpRounding; - if (hitpoints <= 0) hitpoints = HpRounding; - } - else + #region Hitpoints Functions + + public float CalculateTotalHitpoints() + { + float hitpoints; + + if (!part.IsMissile()) + { + var averageSize = part.GetAverageBoundSize(); + var sphereRadius = averageSize * 0.5f; + var sphereSurface = 4 * Mathf.PI * sphereRadius * sphereRadius; + var structuralVolume = sphereSurface * 0.1f; + + var density = (part.mass * 1000f) / structuralVolume; + density = Mathf.Clamp(density, 1000, 10000); + //Debug.Log("[BDArmory]: Hitpoint Calc" + part.name + " | structuralVolume : " + structuralVolume); + //Debug.Log("[BDArmory]: Hitpoint Calc"+part.name+" | Density : " + density); + + var structuralMass = density * structuralVolume; + //Debug.Log("[BDArmory]: Hitpoint Calc" + part.name + " | structuralMass : " + structuralMass); + //3. final calculations + hitpoints = structuralMass * hitpointMultiplier * 0.33f; + + if (hitpoints > 10 * part.mass * 1000f || hitpoints < 0.1f * part.mass * 1000f) + { + Debug.Log($"[BDArmory]: HitpointTracker::Clamping hitpoints for part {part.name}"); + hitpoints = hitpointMultiplier * part.mass * 333f; + } + + if (part.name.Contains("B9.Aero.Wing.Procedural")) // the above works for basic cylindrical-esque parts, but not wings, esp. not proc wings + { + hitpoints = (part.mass * 1000f) * 3.5f; // since wings are basically a 2d object, lets have mass be our scalar - afterall, 2x the mass will ~= 2x the surfce area + } + if (part.IsAero()) + { + hitpoints = (part.mass * 1000f) * 7f; // stock wings are half the mass of proc wings, at least in FAR. Will need to check stock aero wing masses. + } + if (part.IsCtrlSrf()) + { + hitpoints = 100f + (part.mass * 1000f) * 5f; // Crtl surfaces will have actuators of some flavor, are going to be more vulnerable to damage. +100 for guaranteed min health + } + if (part.IsProcpart()) + { + hitpoints = (part.mass * 1000f) * 10f; // a 10x mult gives them about the same HP as similar size stock tanks and cones + } + hitpoints = Mathf.Round(hitpoints / HpRounding) * HpRounding; + if (hitpoints <= 0) hitpoints = HpRounding; + if (hitpoints < 100) hitpoints = 100; + } + else { hitpoints = 5; Armor = 2; diff --git a/BDArmory/Bullets/PooledBullet.cs b/BDArmory/Bullets/PooledBullet.cs index 33e1da145..0582e4639 100644 --- a/BDArmory/Bullets/PooledBullet.cs +++ b/BDArmory/Bullets/PooledBullet.cs @@ -337,8 +337,8 @@ void FixedUpdate() float penetrationFactor = CalculateArmorPenetration(hitPart, anglemultiplier, hit); - if (penetrationFactor >= 2) - { + if (penetrationFactor >= 2 || BDArmorySettings.PAINTBALL) + { //its not going to bounce if it goes right through hasRichocheted = false; } @@ -348,8 +348,8 @@ void FixedUpdate() hasRichocheted = true; } - if (penetrationFactor > 1 && !hasRichocheted) //fully penetrated continue ballistic damage - { + if (penetrationFactor > 1 && !hasRichocheted && !BDArmorySettings.PAINTBALL) //fully penetrated continue ballistic damage + { hasPenetrated = true; ApplyDamage(hitPart, hit, 1, penetrationFactor); penTicker += 1; @@ -369,8 +369,8 @@ void FixedUpdate() KillBullet(); } } - else if (!hasRichocheted) // explosive bullets that get stopped by armor will explode - { + else if (!hasRichocheted && !BDArmorySettings.PAINTBALL) // explosive bullets that get stopped by armor will explode + { //New method if (hitPart.rb != null) @@ -393,15 +393,21 @@ void FixedUpdate() hasDetonated = true; KillBullet(); } - - ///////////////////////////////////////////////////////////////////////////////// - // penetrated after a few ticks - ///////////////////////////////////////////////////////////////////////////////// - - //penetrating explosive - //richochets - - if ((penTicker >= 2 && explosive) || (hasRichocheted && explosive)) + else if (BDArmorySettings.PAINTBALL) + { + hasPenetrated = false; + ApplyDamage(hitPart, hit, 1, penetrationFactor); + hasDetonated = true; + KillBullet(); + } + ///////////////////////////////////////////////////////////////////////////////// + // penetrated after a few ticks + ///////////////////////////////////////////////////////////////////////////////// + + //penetrating explosive + //richochets + + if ((penTicker >= 2 && explosive) || (hasRichocheted && explosive)) { //detonate ExplosiveDetonation(hitPart, hit, ray, airDetonation); @@ -505,14 +511,21 @@ private void ApplyDamage(Part hitPart, RaycastHit hit, float multiplier, float p BulletHitFX.CreateBulletHit(hitPart, hit.point, hit, hit.normal, hasRichocheted, caliber, penetrationfactor); } - - if (explosive) - { + if (BDArmorySettings.INSTAKILL) + { + hitPart.AddBallisticDamage(100000f, 500f, multiplier, 0.1f, 1000000, impactVelocity); + } + if (BDArmorySettings.PAINTBALL) + { + hitPart.AddBallisticDamage(0.001f, 5f, multiplier, 0.1f, 0.1f, impactVelocity); + } + if (explosive && !BDArmorySettings.PAINTBALL) + { hitPart.AddBallisticDamage(bulletMass - tntMass, caliber, multiplier, penetrationfactor, bulletDmgMult, impactVelocity); } - else - { + else if (!BDArmorySettings.PAINTBALL) + { hitPart.AddBallisticDamage(bulletMass, caliber, multiplier, penetrationfactor, bulletDmgMult, impactVelocity); } @@ -598,12 +611,19 @@ private float CalculateArmorPenetration(Part hitPart, float anglemultiplier, Ray private float CalculatePenetration() { float penetration = 0; - if (caliber > 10) //use the "krupp" penetration formula for anything larger than HMGs - { - penetration = (float)(16f * impactVelocity * Math.Sqrt(bulletMass / 1000) / Math.Sqrt(caliber)); - } - - return penetration; + if (apBulletMod <= 0) // sanity check to avoid divide by zero, since stock Bulletdef has apBulletMod = 0 by default + { + apBulletMod = 1; + } + if (caliber > 5 && !BDArmorySettings.PAINTBALL) //use the "krupp" penetration formula for anything larger than HMGs. Dropping min caliber to 5 so .30 cal rounds can do damage. + { + penetration = (float)((16f * impactVelocity * Math.Sqrt(bulletMass / 1000) / Math.Sqrt(caliber)) * apBulletMod); + } + else + { + penetration = 0.1f; + } + return penetration; } private static float CalculateThickness(Part hitPart, float anglemultiplier) @@ -671,8 +691,8 @@ private bool CheckBuildingHit(RaycastHit hit) } catch (Exception) { } - if (building != null && building.IsIntact) - { + if (building != null && building.IsIntact && !BDArmorySettings.PAINTBALL) + { float damageToBuilding = ((0.5f * (bulletMass * Mathf.Pow(currentVelocity.magnitude, 2))) * (BDArmorySettings.DMG_MULTIPLIER / 100) * bulletDmgMult * 1e-4f); @@ -862,11 +882,10 @@ public void CreateExplosion(Part part) } resources.Dispose(); - - explodeScale /= 100; + ExplosionFx.CreateExplosion(part.transform.position, explodeScale, explModelPath, explSoundPath, false, 20); + explodeScale /= 100; part.explosionPotential = explodeScale; - - PartExploderSystem.AddPartToExplode(part); + PartExploderSystem.AddPartToExplode(part); } private float GetExplosivePower() diff --git a/BDArmory/FX/BulletHitFX.cs b/BDArmory/FX/BulletHitFX.cs index 8f34f7c4b..9111a2acd 100644 --- a/BDArmory/FX/BulletHitFX.cs +++ b/BDArmory/FX/BulletHitFX.cs @@ -2,306 +2,342 @@ using BDArmory.Core; using BDArmory.Core.Extension; using BDArmory.Misc; +using BDArmory.Modules; using UniLinq; using UnityEngine; namespace BDArmory.FX { - public class BulletHitFX : MonoBehaviour - { - KSPParticleEmitter[] pEmitters; - AudioSource audioSource; - AudioClip hitSound; - public Vector3 normal; - float startTime; - public bool ricochet; - public float caliber; - - public GameObject bulletHoleDecalPrefab; - public static ObjectPool decalPool_small; - public static ObjectPool decalPool_large; - public static Dictionary> PartsOnFire = new Dictionary>(); - public static Queue HitsLoaded = new Queue(); - - public static int MaxFiresPerVessel = 3; - public static float FireLifeTimeInSeconds = 5f; - - private bool disabled = false; - - public static void SetupShellPool() - { - GameObject templateShell_large; - templateShell_large = - Instantiate(GameDatabase.Instance.GetModel("BDArmory/Models/bulletDecal/BulletDecal2")); - templateShell_large.SetActive(false); - if (decalPool_large == null) - decalPool_large = ObjectPool.CreateObjectPool(templateShell_large, BDArmorySettings.MAX_NUM_BULLET_DECALS, true, true); - - GameObject templateShell_small; - templateShell_small = - Instantiate(GameDatabase.Instance.GetModel("BDArmory/Models/bulletDecal/BulletDecal1")); - templateShell_small.SetActive(false); - if (decalPool_small == null) - decalPool_small = ObjectPool.CreateObjectPool(templateShell_small, BDArmorySettings.MAX_NUM_BULLET_DECALS, true, true); - } - - public static void SpawnDecal(RaycastHit hit, Part hitPart, float caliber, float penetrationfactor) - { - if (!BDArmorySettings.BULLET_DECALS) return; - ObjectPool decalPool_; - - if (caliber >= 90f) - { - decalPool_ = decalPool_large; - } - else - { - decalPool_ = decalPool_small; - } - - //front hit - GameObject decalFront = decalPool_.GetPooledObject(); - if (decalFront != null && hitPart != null) - { - decalFront.transform.SetParent(hitPart.transform); - decalFront.transform.position = hit.point + new Vector3(0.25f, 0f, 0f); - decalFront.transform.rotation = Quaternion.FromToRotation(Vector3.forward, hit.normal); - decalFront.SetActive(true); - } - //back hole if fully penetrated - if (penetrationfactor >= 1) - { - GameObject decalBack = decalPool_.GetPooledObject(); - if (decalBack != null && hitPart != null) - { - decalBack.transform.SetParent(hitPart.transform); - decalBack.transform.position = hit.point + new Vector3(-0.25f, 0f, 0f); - decalBack.transform.rotation = Quaternion.FromToRotation(Vector3.forward, hit.normal); - decalBack.SetActive(true); - } - - if (CanFlamesBeAttached(hitPart)) - { - AttachFlames(hit, hitPart, caliber); - } - } - } - - private static bool CanFlamesBeAttached(Part hitPart) - { - if (!BDArmorySettings.FIRE_FX_IN_FLIGHT && !hitPart.vessel.LandedOrSplashed || !hitPart.HasFuel()) - return false; - - if (hitPart.vessel.LandedOrSplashed) - { - MaxFiresPerVessel = BDArmorySettings.MAX_FIRES_PER_VESSEL; - FireLifeTimeInSeconds = BDArmorySettings.FIRELIFETIME_IN_SECONDS; - } - - if (PartsOnFire.ContainsKey(hitPart.vessel) && PartsOnFire[hitPart.vessel].Count >= MaxFiresPerVessel) - { - var firesOnVessel = PartsOnFire[hitPart.vessel]; - - firesOnVessel.Where(x => (Time.time - x) > FireLifeTimeInSeconds).Select(x => firesOnVessel.Remove(x)); - return false; - } - - if (!PartsOnFire.ContainsKey(hitPart.vessel)) - { - List firesList = new List { Time.time }; - - PartsOnFire.Add(hitPart.vessel, firesList); - } - else - { - PartsOnFire[hitPart.vessel].Add(Time.time); - } - - return true; - } - - void Start() - { - HitsLoaded.Enqueue(this); - if (decalPool_large == null || decalPool_small == null) - SetupShellPool(); - - startTime = Time.time; - pEmitters = gameObject.GetComponentsInChildren(); - - IEnumerator pe = pEmitters.AsEnumerable().GetEnumerator(); - while (pe.MoveNext()) - { - if (pe.Current == null) continue; - EffectBehaviour.AddParticleEmitter(pe.Current); - } - - pe.Dispose(); - - audioSource = gameObject.AddComponent(); - audioSource.minDistance = 1; - audioSource.maxDistance = 50; - audioSource.spatialBlend = 1; - audioSource.volume = BDArmorySettings.BDARMORY_WEAPONS_VOLUME; - - int random = Random.Range(1, 3); - - if (ricochet) - { - if (caliber <= 30) - { - string path = "BDArmory/Sounds/ricochet" + random; - hitSound = GameDatabase.Instance.GetAudioClip(path); - } - else - { - string path = "BDArmory/Sounds/Artillery_Shot"; - hitSound = GameDatabase.Instance.GetAudioClip(path); - } - } - else - { - if (caliber <= 30) - { - string path = "BDArmory/Sounds/bulletHit" + random; - hitSound = GameDatabase.Instance.GetAudioClip(path); - } - else - { - string path = "BDArmory/Sounds/Artillery_Shot"; - hitSound = GameDatabase.Instance.GetAudioClip(path); - } - } - - audioSource.PlayOneShot(hitSound); - } - - void Update() - { - using (new PerformanceLogger("BulletHitFX.Update")) - { - if (!disabled && Time.time - startTime > 0.03f) - { - IEnumerator pe = pEmitters.AsEnumerable().GetEnumerator(); - while (pe.MoveNext()) - { - if (pe.Current == null) continue; - pe.Current.emit = false; - } - pe.Dispose(); - disabled = true; - } - if (Time.time - startTime > 0.3f) - { - HitsLoaded.Dequeue(); - Destroy(gameObject); - } - } - } - - public static void CreateBulletHit(Part hitPart, Vector3 position, RaycastHit hit, Vector3 normalDirection, - bool ricochet, float caliber, float penetrationfactor) - { - if (HitsLoaded.Count > 5) return; - - if (decalPool_large == null || decalPool_small == null) - SetupShellPool(); - - GameObject go; - - if (caliber <= 30) - { - go = GameDatabase.Instance.GetModel("BDArmory/Models/bulletHit/bulletHit"); - } - else - { - go = GameDatabase.Instance.GetModel("BDArmory/FX/PenFX"); - } - - if ((hitPart != null) && caliber != 0 && !hitPart.IgnoreDecal()) - { - SpawnDecal(hit, hitPart, caliber, penetrationfactor); //No bullet decals for laser or ricochet - } - - GameObject newExplosion = - (GameObject)Instantiate(go, position, Quaternion.LookRotation(normalDirection)); - newExplosion.SetActive(true); - newExplosion.AddComponent(); - newExplosion.GetComponent().ricochet = ricochet; - newExplosion.GetComponent().caliber = caliber; - IEnumerator pe = newExplosion.GetComponentsInChildren().Cast().GetEnumerator(); - while (pe.MoveNext()) - { - if (pe.Current == null) continue; - pe.Current.emit = true; - - if (pe.Current.gameObject.name == "sparks") - { - pe.Current.force = (4.49f * FlightGlobals.getGeeForceAtPosition(position)); - } - else if (pe.Current.gameObject.name == "smoke") - { - pe.Current.force = (1.49f * FlightGlobals.getGeeForceAtPosition(position)); - } - } - pe.Dispose(); - } - - public static void AttachFlames(RaycastHit hit, Part hitPart, float caliber) - { - var modelUrl = "BDArmory/FX/FlameEffect2/model"; - - var flameObject = - (GameObject) - Instantiate( - GameDatabase.Instance.GetModel(modelUrl), - hit.point + new Vector3(0.25f, 0f, 0f), - Quaternion.identity); - - flameObject.SetActive(true); - flameObject.transform.SetParent(hitPart.transform); - flameObject.AddComponent(); - - if (hitPart.vessel.LandedOrSplashed && hitPart.GetFireFX() && caliber >= 100f) - { - DecalEmitterScript.shrinkRateFlame = 0.25f; - DecalEmitterScript.shrinkRateSmoke = 0.125f; - } - - foreach (var pe in flameObject.GetComponentsInChildren()) - { - if (!pe.useWorldSpace) continue; - var gpe = pe.gameObject.AddComponent(); - gpe.Emit = true; - } - } - - public static void AttachFlames(Vector3 contactPoint, Part hitPart) - { - if (!CanFlamesBeAttached(hitPart)) return; - - var modelUrl = "BDArmory/FX/FlameEffect2/model"; - - var flameObject = - (GameObject) - Instantiate( - GameDatabase.Instance.GetModel(modelUrl), - contactPoint, - Quaternion.identity); - - flameObject.SetActive(true); - flameObject.transform.SetParent(hitPart.transform); - flameObject.AddComponent(); - - DecalEmitterScript.shrinkRateFlame = 0.125f; - DecalEmitterScript.shrinkRateSmoke = 0.125f; - - foreach (var pe in flameObject.GetComponentsInChildren()) - { - if (!pe.useWorldSpace) continue; - var gpe = pe.gameObject.AddComponent(); - gpe.Emit = true; - } - } - } -} + public class BulletHitFX : MonoBehaviour + { + KSPParticleEmitter[] pEmitters; + AudioSource audioSource; + AudioClip hitSound; + public Vector3 normal; + float startTime; + public bool ricochet; + public float caliber; + + public GameObject bulletHoleDecalPrefab; + public static ObjectPool decalPool_small; + public static ObjectPool decalPool_large; + public static ObjectPool decalPool_paint1; + public static ObjectPool decalPool_paint2; + public static ObjectPool decalPool_paint3; + public static Dictionary> PartsOnFire = new Dictionary>(); + public static Queue HitsLoaded = new Queue(); + + public static int MaxFiresPerVessel = 3; + public static float FireLifeTimeInSeconds = 5f; + + private bool disabled = false; + + public static void SetupShellPool() + { + GameObject templateShell_large; + templateShell_large = + Instantiate(GameDatabase.Instance.GetModel("BDArmory/Models/bulletDecal/BulletDecal2")); + templateShell_large.SetActive(false); + if (decalPool_large == null) + decalPool_large = ObjectPool.CreateObjectPool(templateShell_large, BDArmorySettings.MAX_NUM_BULLET_DECALS, true, true); + + GameObject templateShell_small; + templateShell_small = + Instantiate(GameDatabase.Instance.GetModel("BDArmory/Models/bulletDecal/BulletDecal1")); + templateShell_small.SetActive(false); + if (decalPool_small == null) + decalPool_small = ObjectPool.CreateObjectPool(templateShell_small, BDArmorySettings.MAX_NUM_BULLET_DECALS, true, true); + + GameObject templateShell_paint1; + templateShell_paint1 = + Instantiate(GameDatabase.Instance.GetModel("BDArmory/Models/bulletDecal/BulletDecal3")); + templateShell_paint1.SetActive(false); + if (decalPool_paint1 == null) + decalPool_paint1 = ObjectPool.CreateObjectPool(templateShell_paint1, BDArmorySettings.MAX_NUM_BULLET_DECALS, true, true); + + GameObject templateShell_paint2; + templateShell_paint2 = + Instantiate(GameDatabase.Instance.GetModel("BDArmory/Models/bulletDecal/BulletDecal4")); + templateShell_paint2.SetActive(false); + if (decalPool_paint2 == null) + decalPool_paint2 = ObjectPool.CreateObjectPool(templateShell_paint2, BDArmorySettings.MAX_NUM_BULLET_DECALS, true, true); + + GameObject templateShell_paint3; + templateShell_paint3 = + Instantiate(GameDatabase.Instance.GetModel("BDArmory/Models/bulletDecal/BulletDecal5")); + templateShell_paint3.SetActive(false); + if (decalPool_paint3 == null) + decalPool_paint3 = ObjectPool.CreateObjectPool(templateShell_paint3, BDArmorySettings.MAX_NUM_BULLET_DECALS, true, true); + } + + public static void SpawnDecal(RaycastHit hit, Part hitPart, float caliber, float penetrationfactor) + { + if (!BDArmorySettings.BULLET_DECALS) return; + ObjectPool decalPool_; + if (!BDArmorySettings.PAINTBALL) + { + if (caliber >= 90f) + { + decalPool_ = decalPool_large; + } + else + { + decalPool_ = decalPool_small; + } + } + else + { + int i; + i = Random.Range(1, 4); + if (i < 1.66) + { + decalPool_ = decalPool_paint1; + } + else if (i > 2.33) + { + decalPool_ = decalPool_paint2; + } + else + { + decalPool_ = decalPool_paint3; + } + } + //front hit + GameObject decalFront = decalPool_.GetPooledObject(); + if (decalFront != null && hitPart != null) + { + decalFront.transform.SetParent(hitPart.transform); + decalFront.transform.position = hit.point + new Vector3(0.25f, 0f, 0f); + decalFront.transform.rotation = Quaternion.FromToRotation(Vector3.forward, hit.normal); + decalFront.SetActive(true); + + if (CanFuelDrain(hitPart)) + { + AttachLeak(hit, hitPart, caliber); + } + } + //back hole if fully penetrated + if (penetrationfactor >= 1) + { + GameObject decalBack = decalPool_.GetPooledObject(); + if (decalBack != null && hitPart != null) + { + decalBack.transform.SetParent(hitPart.transform); + decalBack.transform.position = hit.point + new Vector3(-0.25f, 0f, 0f); + decalBack.transform.rotation = Quaternion.FromToRotation(Vector3.forward, hit.normal); + decalBack.SetActive(true); + + if (CanFuelDrain(hitPart)) + { + AttachLeak(hit, hitPart, caliber); + } + } + } + } + + private static bool CanFuelDrain(Part hitPart) + { + if (BDArmorySettings.SELFSEALING_TANKS || BDArmorySettings.PAINTBALL || !hitPart.HasFuel()) + return false; + + if (!hitPart.Resources.Contains("LiquidFuel")) + return false; + /* + if (PartsOnFire.ContainsKey(hitPart.vessel) && PartsOnFire[hitPart.vessel].Count >= MaxFiresPerVessel) + { + var firesOnVessel = PartsOnFire[hitPart.vessel]; + + firesOnVessel.Where(x => (Time.time - x) > FireLifeTimeInSeconds).Select(x => firesOnVessel.Remove(x)); + return false; + } + + if (!PartsOnFire.ContainsKey(hitPart.vessel)) + { + List firesList = new List { Time.time }; + + PartsOnFire.Add(hitPart.vessel, firesList); + } + else + { + PartsOnFire[hitPart.vessel].Add(Time.time); + } + */ + return true; + } + + void Start() + { + HitsLoaded.Enqueue(this); + if (decalPool_large == null || decalPool_small == null) + SetupShellPool(); + + if (BDArmorySettings.PAINTBALL && decalPool_paint1 == null) + SetupShellPool(); + + startTime = Time.time; + pEmitters = gameObject.GetComponentsInChildren(); + + IEnumerator pe = pEmitters.AsEnumerable().GetEnumerator(); + while (pe.MoveNext()) + { + if (pe.Current == null) continue; + EffectBehaviour.AddParticleEmitter(pe.Current); + } + + pe.Dispose(); + + audioSource = gameObject.AddComponent(); + audioSource.minDistance = 1; + audioSource.maxDistance = 50; + audioSource.spatialBlend = 1; + audioSource.volume = BDArmorySettings.BDARMORY_WEAPONS_VOLUME; + + int random = Random.Range(1, 3); + + if (ricochet) + { + if (caliber <= 30) + { + string path = "BDArmory/Sounds/ricochet" + random; + hitSound = GameDatabase.Instance.GetAudioClip(path); + } + else + { + string path = "BDArmory/Sounds/Artillery_Shot"; + hitSound = GameDatabase.Instance.GetAudioClip(path); + } + } + else + { + if (caliber <= 30) + { + string path = "BDArmory/Sounds/bulletHit" + random; + hitSound = GameDatabase.Instance.GetAudioClip(path); + } + else + { + string path = "BDArmory/Sounds/Artillery_Shot"; + hitSound = GameDatabase.Instance.GetAudioClip(path); + } + } + + audioSource.PlayOneShot(hitSound); + } + + void Update() + { + using (new PerformanceLogger("BulletHitFX.Update")) + { + if (!disabled && Time.time - startTime > 0.03f) + { + IEnumerator pe = pEmitters.AsEnumerable().GetEnumerator(); + while (pe.MoveNext()) + { + if (pe.Current == null) continue; + pe.Current.emit = false; + } + pe.Dispose(); + disabled = true; + } + if (Time.time - startTime > 0.3f) + { + HitsLoaded.Dequeue(); + Destroy(gameObject); + } + } + } + + public static void CreateBulletHit(Part hitPart, Vector3 position, RaycastHit hit, Vector3 normalDirection, + bool ricochet, float caliber, float penetrationfactor) + { + if (HitsLoaded.Count > 5) return; + + if (decalPool_large == null || decalPool_small == null) + SetupShellPool(); + + if (BDArmorySettings.PAINTBALL && decalPool_paint1 == null) + SetupShellPool(); + + GameObject go; + + if (BDArmorySettings.PAINTBALL) + { + go = GameDatabase.Instance.GetModel("BDArmory/Models/bulletHit/bulletHit"); + } + + if (caliber <= 30) + { + go = GameDatabase.Instance.GetModel("BDArmory/Models/bulletHit/bulletHit"); + } + else + { + go = GameDatabase.Instance.GetModel("BDArmory/FX/PenFX"); + } + + if ((hitPart != null) && caliber != 0 && !hitPart.IgnoreDecal()) + { + SpawnDecal(hit, hitPart, caliber, penetrationfactor); //No bullet decals for laser or ricochet + } + + GameObject newExplosion = + (GameObject)Instantiate(go, position, Quaternion.LookRotation(normalDirection)); + newExplosion.SetActive(true); + newExplosion.AddComponent(); + newExplosion.GetComponent().ricochet = ricochet; + newExplosion.GetComponent().caliber = caliber; + IEnumerator pe = newExplosion.GetComponentsInChildren().Cast().GetEnumerator(); + while (pe.MoveNext()) + { + if (pe.Current == null) continue; + pe.Current.emit = true; + + if (pe.Current.gameObject.name == "sparks") + { + pe.Current.force = (4.49f * FlightGlobals.getGeeForceAtPosition(position)); + } + else if (pe.Current.gameObject.name == "smoke") + { + pe.Current.force = (1.49f * FlightGlobals.getGeeForceAtPosition(position)); + } + } + pe.Dispose(); + } + + public static void AttachLeak(RaycastHit hit, Part hitPart, float caliber) + { + var modelUrl = "BDArmory/FX/FuelLeakFX/model"; + + var flameObject = + (GameObject) + Instantiate( + GameDatabase.Instance.GetModel(modelUrl), + hit.point + new Vector3(0.25f, 0f, 0f), + Quaternion.identity); + + flameObject.SetActive(true); + flameObject.transform.SetParent(hitPart.transform); + flameObject.AddComponent(); + + //DecalEmitterScript.lifeTime = 20; + + var leak = hitPart.FindModuleImplementing(); + if (leak != null) + { + leak.drainRate += ((caliber / 100)); + } + else + { + hitPart.AddModule("ModuleDrainFuel"); + leak.drainRate = ((caliber / 100)); + leak.drainDuration = 20; + } + Debug.Log("[BDArmory]: BulletHit attaching fuel leak, drainrate: " + leak.drainRate); + foreach (var pe in flameObject.GetComponentsInChildren()) + { + if (!pe.useWorldSpace) continue; + var gpe = pe.gameObject.AddComponent(); + gpe.Emit = true; + } + } + } +} \ No newline at end of file diff --git a/BDArmory/FX/DecalEmitterScript.cs b/BDArmory/FX/DecalEmitterScript.cs index 5bd134105..44cdae8a7 100644 --- a/BDArmory/FX/DecalEmitterScript.cs +++ b/BDArmory/FX/DecalEmitterScript.cs @@ -2,68 +2,59 @@ namespace BDArmory.FX { - public class DecalEmitterScript : MonoBehaviour - { - //public static float _maxCombineDistance = 0.6f; - - public static float shrinkRateFlame = 2.0f; - - public static float shrinkRateSmoke = 2.25f; - - private GameObject _destroyer; - - private float _destroyTimerStart; - - private float _highestEnergy; - - public void Start() - { - foreach (var pe in gameObject.GetComponentsInChildren()) - { - var color = pe.material.color; - color.a = color.a / 2; - pe.material.SetColor("_TintColor", color); - pe.force = -FlightGlobals.getGeeForceAtPosition(transform.position) / 3; - if (!(pe.maxEnergy > _highestEnergy)) continue; - _destroyer = pe.gameObject; - _highestEnergy = pe.maxEnergy; - EffectBehaviour.AddParticleEmitter(pe); - } - } - - public void FixedUpdate() - { - if (_destroyTimerStart != 0 && Time.time - _destroyTimerStart > _highestEnergy) - { - Destroy(gameObject); - } - - foreach (var pe in gameObject.GetComponentsInChildren()) - { - var shrinkRate = pe.gameObject.name.Contains("smoke") ? shrinkRateSmoke : shrinkRateFlame; - pe.maxSize = Mathf.MoveTowards(pe.maxSize, 0, shrinkRate * Time.fixedDeltaTime); - pe.minSize = Mathf.MoveTowards(pe.minSize, 0, shrinkRate * Time.fixedDeltaTime); - - if (pe.maxSize < 0.1f && pe.gameObject == _destroyer && _destroyTimerStart == 0) - { - _destroyTimerStart = Time.time; - } - - var lightComponent = pe.gameObject.GetComponent(); - - if (lightComponent != null) - { - lightComponent.intensity = Random.Range(0f, pe.maxSize / 6); - } - } - } - - private void OnDestroy() - { - foreach (var pe in gameObject.GetComponentsInChildren()) - { - EffectBehaviour.RemoveParticleEmitter(pe); - } - } - } + public class DecalEmitterScript : MonoBehaviour + { + //public static float _maxCombineDistance = 0.6f; + + public float lifeTime = 20; + + private GameObject _destroyer; + + private float _destroyTimerStart; + + private float _highestEnergy; + + public void Start() + { + foreach (var pe in gameObject.GetComponentsInChildren()) + { + pe.force = FlightGlobals.getGeeForceAtPosition(transform.position); + if (!(pe.maxEnergy > _highestEnergy)) continue; + _destroyer = pe.gameObject; + _highestEnergy = pe.maxEnergy; + EffectBehaviour.AddParticleEmitter(pe); + } + } + + public void FixedUpdate() + { + if (_destroyTimerStart != 0 && Time.time - _destroyTimerStart > _highestEnergy) + { + Destroy(gameObject); + } + + foreach (var pe in gameObject.GetComponentsInChildren()) + { + //var shrinkRate = pe.gameObject.name.Contains("smoke") ? shrinkRateSmoke : shrinkRateFlame; + //pe.maxSize = Mathf.MoveTowards(pe.maxSize, 0, shrinkRate * Time.fixedDeltaTime); + //pe.minSize = Mathf.MoveTowards(pe.minSize, 0, shrinkRate * Time.fixedDeltaTime); + + lifeTime -= Time.fixedDeltaTime; + + if (lifeTime < 0 && pe.gameObject == _destroyer && _destroyTimerStart == 0) + { + _destroyTimerStart = Time.time; + } + } + } + + private void OnDestroy() + { + foreach (var pe in gameObject.GetComponentsInChildren()) + { + EffectBehaviour.RemoveParticleEmitter(pe); + } + + } + } } diff --git a/BDArmory/Modules/BDModularGuidance.cs b/BDArmory/Modules/BDModularGuidance.cs index d9c0cd22c..0e0dad7d4 100644 --- a/BDArmory/Modules/BDModularGuidance.cs +++ b/BDArmory/Modules/BDModularGuidance.cs @@ -1265,7 +1265,10 @@ public void GUIWindow(int windowID) GUILayout.EndVertical(); GUI.DragWindow(); - BDGUIUtils.RepositionWindow(ref guiWindowRect); + if (BDArmorySettings.STRICT_WINDOW_BOUNDARIES) + { + BDGUIUtils.RepositionWindow(ref guiWindowRect); + } } private static void InitializeStyles() diff --git a/BDArmory/Modules/BDModulePilotAI.cs b/BDArmory/Modules/BDModulePilotAI.cs index c624a6a18..b4f628dc8 100644 --- a/BDArmory/Modules/BDModulePilotAI.cs +++ b/BDArmory/Modules/BDModulePilotAI.cs @@ -14,247 +14,279 @@ namespace BDArmory.Modules { - public class BDModulePilotAI : BDGenericAIBase, IBDAIControl - { - public enum SteerModes { NormalFlight, Aiming } - - SteerModes steerMode = SteerModes.NormalFlight; - - bool belowMinAltitude; - bool extending; - - bool requestedExtend; - Vector3 requestedExtendTpos; - - public bool IsExtending - { - get { return extending || requestedExtend; } - } - - public void RequestExtend(Vector3 tPosition) - { - requestedExtend = true; - requestedExtendTpos = tPosition; - } - - public override bool CanEngage() - { - return !vessel.LandedOrSplashed; - } - - GameObject vobj; - - Transform velocityTransform - { - get - { - if (!vobj) - { - vobj = new GameObject("velObject"); - vobj.transform.position = vessel.ReferenceTransform.position; - vobj.transform.parent = vessel.ReferenceTransform; - } - - return vobj.transform; - } - } - - Vector3 upDirection = Vector3.up; - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_DefaultAltitude"),//Default Alt. - UI_FloatRange(minValue = 500f, maxValue = 15000f, stepIncrement = 25f, scene = UI_Scene.All)] - public float defaultAltitude = 1500; - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_MinAltitude"),//Min Altitude - UI_FloatRange(minValue = 150f, maxValue = 6000, stepIncrement = 50f, scene = UI_Scene.All)] - public float minAltitude = 500f; - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_SteerFactor"),//Steer Factor - UI_FloatRange(minValue = 0.1f, maxValue = 20f, stepIncrement = .1f, scene = UI_Scene.All)] - public float steerMult = 6; - //make a combat steer mult and idle steer mult - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_SteerKi"),//Steer Ki - UI_FloatRange(minValue = 0.01f, maxValue = 1f, stepIncrement = 0.01f, scene = UI_Scene.All)] - public float steerKiAdjust = 0.05f; - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_StagesNumber"),//Steer Limiter - UI_FloatRange(minValue = .1f, maxValue = 1f, stepIncrement = .05f, scene = UI_Scene.All)] - public float maxSteer = 1; - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_SteerDamping"),//Steer Damping - UI_FloatRange(minValue = 1f, maxValue = 8f, stepIncrement = 0.5f, scene = UI_Scene.All)] - public float steerDamping = 3; - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_MaxSpeed"),//Max Speed - UI_FloatRange(minValue = 20f, maxValue = 800f, stepIncrement = 1.0f, scene = UI_Scene.All)] - public float maxSpeed = 325; - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_TakeOffSpeed"),//TakeOff Speed - UI_FloatRange(minValue = 10f, maxValue = 200f, stepIncrement = 1.0f, scene = UI_Scene.All)] - public float takeOffSpeed = 70; - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_MinSpeed"),//MinCombatSpeed - UI_FloatRange(minValue = 20f, maxValue = 200, stepIncrement = 1.0f, scene = UI_Scene.All)] - public float minSpeed = 60f; - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_IdleSpeed"),//Idle Speed - UI_FloatRange(minValue = 10f, maxValue = 200f, stepIncrement = 1.0f, scene = UI_Scene.All)] - public float idleSpeed = 120f; - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_maxAllowedGForce"),//Max G - UI_FloatRange(minValue = 2f, maxValue = 45f, stepIncrement = 0.25f, scene = UI_Scene.All)] - public float maxAllowedGForce = 10; - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_maxAllowedAoA"),//Max AoA - UI_FloatRange(minValue = 0f, maxValue = 85f, stepIncrement = 2.5f, scene = UI_Scene.All)] - public float maxAllowedAoA = 35; - float maxAllowedCosAoA; - float lastAllowedAoA; - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_Orbit", advancedTweakable = true),//Orbit - UI_Toggle(enabledText = "#LOC_BDArmory_Orbit_enabledText", disabledText = "#LOC_BDArmory_Orbit_disabledText", scene = UI_Scene.All),]//Starboard (CW)--Port (CCW) - public bool ClockwiseOrbit = true; - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_UnclampTuning", advancedTweakable = true),//Unclamp tuning - UI_Toggle(enabledText = "#LOC_BDArmory_UnclampTuning_enabledText", disabledText = "#LOC_BDArmory_UnclampTuning_disabledText", scene = UI_Scene.All),]//Unclamped--Clamped - public bool UpToEleven = false; - bool toEleven = false; - - Dictionary altMaxValues = new Dictionary - { - { nameof(defaultAltitude), 100000f }, - { nameof(minAltitude), 30000f }, - { nameof(steerMult), 200f }, - { nameof(steerKiAdjust), 20f }, - { nameof(steerDamping), 100f }, - { nameof(maxSpeed), 3000f }, - { nameof(takeOffSpeed), 2000f }, - { nameof(minSpeed), 2000f }, - { nameof(idleSpeed), 3000f }, - { nameof(maxAllowedGForce), 1000f }, - { nameof(maxAllowedAoA), 180f }, - }; - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_StandbyMode"),//Standby Mode - UI_Toggle(enabledText = "#LOC_BDArmory_On", disabledText = "#LOC_BDArmory_Off")]//On--Off - public bool standbyMode = false; - - //manueuverability and g loading data - float maxDynPresGRecorded; - - float maxPosG; - float cosAoAAtMaxPosG; - - float maxNegG; - float cosAoAAtMaxNegG; - - float[] gLoadMovingAvgArray = new float[32]; - float[] cosAoAMovingAvgArray = new float[32]; - int movingAvgIndex; - - float gLoadMovingAvg; - float cosAoAMovingAvg; - - float gaoASlopePerDynPres; //used to limit control input at very high dynamic pressures to avoid structural failure - float gOffsetPerDynPres; - - float posPitchDynPresLimitIntegrator = 1; - float negPitchDynPresLimitIntegrator = -1; - - float lastCosAoA; - float lastPitchInput; - - //Controller Integral - float pitchIntegral; - float yawIntegral; - - //instantaneous turn radius and possible acceleration from lift - //properties can be used so that other AI modules can read this for future maneuverability comparisons between craft - float turnRadius; - - public float TurnRadius - { - get { return turnRadius; } - private set { turnRadius = value; } - } - - float maxLiftAcceleration; - - public float MaxLiftAcceleration - { - get { return maxLiftAcceleration; } - private set { maxLiftAcceleration = value; } - } - - float turningTimer; - float evasiveTimer; - Vector3 lastTargetPosition; - - LineRenderer lr; - Vector3 flyingToPosition; - Vector3 rollTarget; - Vector3 angVelRollTarget; - - //speed controller - bool useAB = true; - bool useBrakes = true; - bool regainEnergy = false; - - //collision detection - int collisionDetectionTicker; - float collisionDetectionTimer; - Vector3 collisionAvoidDirection; - - //wing command - bool useRollHint; - private Vector3d debugFollowPosition; - - double commandSpeed; - Vector3d commandHeading; - - float finalMaxSteer = 1; - - #region RMB info in editor - - // Yes - public override string GetInfo() - { - StringBuilder sb = new StringBuilder(); - sb.AppendLine("Available settings:"); - sb.AppendLine($"- Default Alt. - altitude to fly at when cruising/idle"); - sb.AppendLine($"- Min Altitude - below this altitude AI will prioritize gaining altitude over combat"); - sb.AppendLine($"- Steer Factor - higher will make the AI apply more control input for the same desired rotation"); - sb.AppendLine($"- Steer Ki - higher will make the AI apply control trim faster"); - sb.AppendLine($"- Steer Limiter - limit AI from applying full control input"); - sb.AppendLine($"- Steer Damping - higher will make the AI apply more control input when it wants to stop rotation"); - sb.AppendLine($"- Max Speed - AI will not fly faster than this"); - sb.AppendLine($"- TakeOff Speed - speed at which to start pitching up when taking off"); - sb.AppendLine($"- MinCombat Speed - AI will prioritize regaining speed over combat below this"); - sb.AppendLine($"- Idle Speed - Cruising speed when not in combat"); - sb.AppendLine($"- Max G - AI will try not to perform maneuvers at higher G than this"); - sb.AppendLine($"- Max AoA - AI will try not to exceed this angle of attack"); - if (GameSettings.ADVANCED_TWEAKABLES) - { - sb.AppendLine($"- Orbit - Which direction to orbit when idling over a location"); - sb.AppendLine($"- Unclamp tuning - Increases variable limits, no direct effect on behaviour"); - } - sb.AppendLine($"- Standby Mode - AI will not take off until an enemy is detected"); - - return sb.ToString(); - } - - #endregion RMB info in editor - - protected override void Start() - { - base.Start(); - - if (HighLogic.LoadedSceneIsFlight) - { - maxAllowedCosAoA = (float)Math.Cos(maxAllowedAoA * Math.PI / 180.0); - lastAllowedAoA = maxAllowedAoA; - } + public class BDModulePilotAI : BDGenericAIBase, IBDAIControl + { + public enum SteerModes { NormalFlight, Aiming } + + SteerModes steerMode = SteerModes.NormalFlight; + + bool belowMinAltitude; + bool extending; + + bool requestedExtend; + Vector3 requestedExtendTpos; + + public bool IsExtending + { + get { return extending || requestedExtend; } + } + + public void RequestExtend(Vector3 tPosition) + { + requestedExtend = true; + requestedExtendTpos = tPosition; + } + + public override bool CanEngage() + { + return !vessel.LandedOrSplashed; + } + + GameObject vobj; + + Transform velocityTransform + { + get + { + if (!vobj) + { + vobj = new GameObject("velObject"); + vobj.transform.position = vessel.ReferenceTransform.position; + vobj.transform.parent = vessel.ReferenceTransform; + } + + return vobj.transform; + } + } + + Vector3 upDirection = Vector3.up; + + [KSPField(isPersistant = true)] + public bool advancedSettings = false; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_DefaultAltitude"),//Default Alt. + UI_FloatRange(minValue = 500f, maxValue = 15000f, stepIncrement = 25f, scene = UI_Scene.All)] + public float defaultAltitude = 1500; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_MinAltitude"),//Min Altitude + UI_FloatRange(minValue = 150f, maxValue = 6000, stepIncrement = 50f, scene = UI_Scene.All)] + public float minAltitude = 500f; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_SteerFactor"),//Steer Factor + UI_FloatRange(minValue = 0.1f, maxValue = 20f, stepIncrement = .1f, scene = UI_Scene.All)] + public float steerMult = 6; + //make a combat steer mult and idle steer mult + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_SteerKi"),//Steer Ki + UI_FloatRange(minValue = 0.01f, maxValue = 1f, stepIncrement = 0.01f, scene = UI_Scene.All)] + public float steerKiAdjust = 0.05f; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_SteerLimiter"),//Steer Limiter + UI_FloatRange(minValue = .1f, maxValue = 1f, stepIncrement = .05f, scene = UI_Scene.All)] + public float maxSteer = 1; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_SASteerLimiter", advancedTweakable = true), //Speed Adjusted Steer Limiter +UI_FloatRange(minValue = .1f, maxValue = 2f, stepIncrement = .05f, scene = UI_Scene.All)] + public float maxSteerAtMaxSpeed = 1; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_LimiterSpeed", advancedTweakable = true), //Adjusted Limiter Speed + UI_FloatRange(minValue = 10f, maxValue = 500f, stepIncrement = 1.0f, scene = UI_Scene.All)] + public float cornerSpeed = 200f; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_SteerDamping"),//Steer Damping + UI_FloatRange(minValue = 1f, maxValue = 8f, stepIncrement = 0.5f, scene = UI_Scene.All)] + public float steerDamping = 3; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_MaxSpeed"),//Max Speed + UI_FloatRange(minValue = 20f, maxValue = 800f, stepIncrement = 1.0f, scene = UI_Scene.All)] + public float maxSpeed = 325; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_TakeOffSpeed"),//TakeOff Speed + UI_FloatRange(minValue = 10f, maxValue = 200f, stepIncrement = 1.0f, scene = UI_Scene.All)] + public float takeOffSpeed = 70; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_MinSpeed"),//MinCombatSpeed + UI_FloatRange(minValue = 20f, maxValue = 200, stepIncrement = 1.0f, scene = UI_Scene.All)] + public float minSpeed = 60f; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_IdleSpeed"),//Idle Speed + UI_FloatRange(minValue = 10f, maxValue = 200f, stepIncrement = 1.0f, scene = UI_Scene.All)] + public float idleSpeed = 120f; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = false, guiName = "#LOC_BDArmory_ExtendDist", advancedTweakable = true), //Extend Distance +UI_FloatRange(minValue = 100f, maxValue = 4000f, stepIncrement = 100.0f, scene = UI_Scene.All)] + public float ExtendDist = 2000; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_EvadeDist", advancedTweakable = true), //Missile Evade Dist + UI_FloatRange(minValue = 200f, maxValue = 8000, stepIncrement = 200.0f, scene = UI_Scene.All)] + public float maxBreakDist = 2000; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_maxAllowedGForce"),//Max G + UI_FloatRange(minValue = 2f, maxValue = 45f, stepIncrement = 0.25f, scene = UI_Scene.All)] + public float maxAllowedGForce = 10; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_maxAllowedAoA"),//Max AoA + UI_FloatRange(minValue = 0f, maxValue = 85f, stepIncrement = 2.5f, scene = UI_Scene.All)] + public float maxAllowedAoA = 35; + float maxAllowedCosAoA; + float lastAllowedAoA; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_AIPersonality", advancedTweakable = true), //AI Personality +UI_Toggle(enabledText = "#LOC_BDArmory_Reckless", disabledText = "#LOC_BDArmory_Standard", scene = UI_Scene.All),] //Reckless -- Standard + public bool Reckless = false; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_Orbit", advancedTweakable = true),//Orbit + UI_Toggle(enabledText = "#LOC_BDArmory_Orbit_enabledText", disabledText = "#LOC_BDArmory_Orbit_disabledText", scene = UI_Scene.All),]//Starboard (CW)--Port (CCW) + public bool ClockwiseOrbit = true; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_UnclampTuning", advancedTweakable = true),//Unclamp tuning + UI_Toggle(enabledText = "#LOC_BDArmory_UnclampTuning_enabledText", disabledText = "#LOC_BDArmory_UnclampTuning_disabledText", scene = UI_Scene.All),]//Unclamped--Clamped + public bool UpToEleven = false; + bool toEleven = false; + + Dictionary altMaxValues = new Dictionary + { + { nameof(defaultAltitude), 100000f }, + { nameof(minAltitude), 30000f }, + { nameof(steerMult), 200f }, + { nameof(steerKiAdjust), 20f }, + { nameof(steerDamping), 100f }, + { nameof(maxSpeed), 3000f }, + { nameof(takeOffSpeed), 2000f }, + { nameof(minSpeed), 2000f }, + { nameof(idleSpeed), 3000f }, + { nameof(maxAllowedGForce), 1000f }, + { nameof(maxAllowedAoA), 180f }, + }; + + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_StandbyMode"),//Standby Mode + UI_Toggle(enabledText = "#LOC_BDArmory_On", disabledText = "#LOC_BDArmory_Off")]//On--Off + public bool standbyMode = false; + + //manueuverability and g loading data + float maxDynPresGRecorded; + + float maxPosG; + float cosAoAAtMaxPosG; + + float maxNegG; + float cosAoAAtMaxNegG; + + float[] gLoadMovingAvgArray = new float[32]; + float[] cosAoAMovingAvgArray = new float[32]; + int movingAvgIndex; + + float gLoadMovingAvg; + float cosAoAMovingAvg; + + float gaoASlopePerDynPres; //used to limit control input at very high dynamic pressures to avoid structural failure + float gOffsetPerDynPres; + + float posPitchDynPresLimitIntegrator = 1; + float negPitchDynPresLimitIntegrator = -1; + + float lastCosAoA; + float lastPitchInput; + + //Controller Integral + float pitchIntegral; + float yawIntegral; + + //instantaneous turn radius and possible acceleration from lift + //properties can be used so that other AI modules can read this for future maneuverability comparisons between craft + float turnRadius; + + public float TurnRadius + { + get { return turnRadius; } + private set { turnRadius = value; } + } + + float maxLiftAcceleration; + + public float MaxLiftAcceleration + { + get { return maxLiftAcceleration; } + private set { maxLiftAcceleration = value; } + } + + float turningTimer; + float evasiveTimer; + Vector3 lastTargetPosition; + + LineRenderer lr; + Vector3 flyingToPosition; + Vector3 rollTarget; + Vector3 angVelRollTarget; + + //speed controller + bool useAB = true; + bool useBrakes = true; + bool regainEnergy = false; + + //collision detection + int collisionDetectionTicker; + float collisionDetectionTimer; + Vector3 collisionAvoidDirection; + + //wing command + bool useRollHint; + private Vector3d debugFollowPosition; + + double commandSpeed; + Vector3d commandHeading; + + float finalMaxSteer = 1; + + #region RMB info in editor + + // Yes + public override string GetInfo() + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine("Available settings:"); + sb.AppendLine($"- Default Alt. - altitude to fly at when cruising/idle"); + sb.AppendLine($"- Min Altitude - below this altitude AI will prioritize gaining altitude over combat"); + sb.AppendLine($"- Steer Factor - higher will make the AI apply more control input for the same desired rotation"); + sb.AppendLine($"- Steer Ki - higher will make the AI apply control trim faster"); + sb.AppendLine($"- Steer Limiter - limit AI from applying full control input"); + sb.AppendLine($"- Steer Damping - higher will make the AI apply more control input when it wants to stop rotation"); + sb.AppendLine($"- Max Speed - AI will not fly faster than this"); + sb.AppendLine($"- TakeOff Speed - speed at which to start pitching up when taking off"); + sb.AppendLine($"- MinCombat Speed - AI will prioritize regaining speed over combat below this"); + sb.AppendLine($"- Idle Speed - Cruising speed when not in combat"); + sb.AppendLine($"- Max G - AI will try not to perform maneuvers at higher G than this"); + sb.AppendLine($"- Max AoA - AI will try not to exceed this angle of attack"); + if (GameSettings.ADVANCED_TWEAKABLES) + { + sb.AppendLine($"- Speed-Adjusted Steer Limiter - limit AI from applying full control input past this speed"); + sb.AppendLine($"- Adjusted Limiter Speed - Speed AI will begin limiting control input"); + sb.AppendLine($"- Missile Evade Distance - Distance Ai will begin evading incoming missiles"); + sb.AppendLine($"- Extend Distance - Distance Ai will extend to regain energy"); + sb.AppendLine($"- Orbit - Which direction to orbit when idling over a location"); + sb.AppendLine($"- Unclamp tuning - Increases variable limits, no direct effect on behaviour"); + sb.AppendLine($"- AI Personality - Standard or reckless AI behavior"); + } + sb.AppendLine($"- Standby Mode - AI will not take off until an enemy is detected"); + + return sb.ToString(); + } + + #endregion RMB info in editor + + protected override void Start() + { + base.Start(); + + if (HighLogic.LoadedSceneIsFlight) + { + maxAllowedCosAoA = (float)Math.Cos(maxAllowedAoA * Math.PI / 180.0); + lastAllowedAoA = maxAllowedAoA; + if (GameSettings.ADVANCED_TWEAKABLES) + { + advancedSettings = true; + } + } } public override void ActivatePilot() @@ -455,8 +487,15 @@ void UpdateAI(FlightCtrlState s) Evasive(s); evasiveTimer += Time.fixedDeltaTime; turningTimer = 0; - - if (evasiveTimer > 3) + if (Reckless) + { + if (evasiveTimer > 1.55) + { + evasiveTimer = 0; + collisionDetectionTicker = 21; //check for collision again after exiting evasion routine + } + } + if (evasiveTimer > 3) { evasiveTimer = 0; collisionDetectionTicker = 21; //check for collision again after exiting evasion routine @@ -872,8 +911,10 @@ void FlyToPosition(FlightCtrlState s, Vector3 targetPosition) flyingToPosition = targetPosition; - //test poststall - float AoA = Vector3.Angle(vessel.ReferenceTransform.up, vessel.Velocity()); + float angleToTarget = Vector3.Angle(vesselTransform.up, targetPosition - vesselTransform.position); + + //test poststall + float AoA = Vector3.Angle(vessel.ReferenceTransform.up, vessel.Velocity()); if (AoA > 30f) { steerMode = SteerModes.Aiming; @@ -918,8 +959,17 @@ void FlyToPosition(FlightCtrlState s, Vector3 targetPosition) pitchError = VectorUtils.SignedAngle(Vector3.up, Vector3.ProjectOnPlane(targetDirection, Vector3.right), Vector3.back); yawError = VectorUtils.SignedAngle(Vector3.up, Vector3.ProjectOnPlane(targetDirectionYaw, Vector3.forward), Vector3.right); - //test - debugString.Append($"finalMaxSteer: {finalMaxSteer}"); + if (vessel.srfSpeed > cornerSpeed & maxSteerAtMaxSpeed < 1) + { + finalMaxSteer *= 1 - (((float)vessel.srfSpeed - cornerSpeed) / (maxSpeed - cornerSpeed) * (1f - maxSteerAtMaxSpeed)); // linear approximation to set max control input when above corner speed + if (finalMaxSteer < 0.1f) + { + finalMaxSteer = 0.1f; // added just in case to ensure some input is retained no matter what happens + } + } + + //test + debugString.Append($"finalMaxSteer: {finalMaxSteer}"); debugString.Append(Environment.NewLine); //roll @@ -931,14 +981,14 @@ void FlyToPosition(FlightCtrlState s, Vector3 targetPosition) } rollTarget = (targetPosition + (rollUp * upDirection)) - vesselTransform.position; - //test - if (steerMode == SteerModes.Aiming && !belowMinAltitude) - { - angVelRollTarget = -140 * vesselTransform.TransformVector(Quaternion.AngleAxis(90f, Vector3.up) * localTargetAngVel); - rollTarget += angVelRollTarget; - } + //test + if (steerMode == SteerModes.Aiming && !belowMinAltitude && angleToTarget > 20) //if target more or less infront, use yaw to correct lateral aim error, else roll+pitch + { + angVelRollTarget = -140 * vesselTransform.TransformVector(Quaternion.AngleAxis(90f, Vector3.up) * localTargetAngVel); + rollTarget += angVelRollTarget; + } - if (command == PilotCommands.Follow && useRollHint) + if (command == PilotCommands.Follow && useRollHint) { rollTarget = -commandLeader.vessel.ReferenceTransform.forward; } @@ -976,7 +1026,12 @@ void FlyToPosition(FlightCtrlState s, Vector3 targetPosition) float steerPitch = (0.015f * steerMult * pitchError) - (steerDamping * -localAngVel.x * (1 + steerKiAdjust)); float steerYaw = (0.005f * steerMult * yawError) - (steerDamping * 0.2f * -localAngVel.z * (1 + steerKiAdjust)); - pitchIntegral += pitchError; + if (steerMode == SteerModes.Aiming) + { + steerYaw = (0.015f * steerMult * yawError) - (steerDamping * -localAngVel.z * (1 + steerKiAdjust)); // so Ai can use yaw for fine aiming + } + + pitchIntegral += pitchError; yawIntegral += yawError; steerPitch *= dynamicAdjustment; @@ -1005,19 +1060,25 @@ void FlyExtend(FlightCtrlState s, Vector3 tPosition) extending = false; } - float extendDistance = Mathf.Clamp(weaponManager.guardRange - 1800, 2500, 4000); - - if (weaponManager.CurrentMissile && weaponManager.CurrentMissile.GetWeaponClass() == WeaponClasses.Bomb) - { - extendDistance = 4500; - } - - if (targetVessel != null && !targetVessel.LandedOrSplashed) //this is just asking for trouble at 800m - { - extendDistance = 1600; - } - - Vector3 srfVector = Vector3.ProjectOnPlane(vessel.transform.position - tPosition, upDirection); + float extendDistance; + if (advancedSettings) + { + extendDistance = ExtendDist; + } + else + { + extendDistance = Mathf.Clamp(weaponManager.guardRange - 1800, 2500, 4000); + } + if ((targetVessel != null && !targetVessel.LandedOrSplashed) && !advancedSettings) //this is just asking for trouble at 800m + { + extendDistance = 1600; + } + if (weaponManager.CurrentMissile && weaponManager.CurrentMissile.GetWeaponClass() == WeaponClasses.Bomb) + { + extendDistance = 4500; + } + + Vector3 srfVector = Vector3.ProjectOnPlane(vessel.transform.position - tPosition, upDirection); float srfDist = srfVector.magnitude; if (srfDist < extendDistance) { @@ -1125,8 +1186,8 @@ void Evasive(FlightCtrlState s) AdjustThrottle(targetSpeed, false, useAB); } - if ((weaponManager.isChaffing || weaponManager.isFlaring) && (weaponManager.incomingMissileDistance > 2000)) - { + if (((weaponManager.isChaffing || weaponManager.isFlaring) && (weaponManager.incomingMissileDistance > 2000)) || ((weaponManager.isChaffing || weaponManager.isFlaring) && advancedSettings && weaponManager.incomingMissileDistance > maxBreakDist)) + { debugString.Append($"Breaking from missile threat!"); debugString.Append(Environment.NewLine); @@ -1146,55 +1207,99 @@ void Evasive(FlightCtrlState s) if (threatDirectionFactor > 0.9f) //within 28 degrees in front { - breakTarget += Vector3.Cross(threatRelativePosition.normalized, Mathf.Sign(Mathf.Sin((float)vessel.missionTime / 2)) * vessel.upAxis); - debugString.Append($" from directly ahead!"); - } - else if (threatDirectionFactor < -0.9) //within ~28 degrees behind - { - float threatDistance = threatRelativePosition.magnitude; - if (threatDistance > 400) - { - breakTarget = vesselTransform.position + vesselTransform.up * 1500 - 500 * vessel.upAxis; - breakTarget += Mathf.Sin((float)vessel.missionTime / 2) * vesselTransform.right * 1000 - Mathf.Cos((float)vessel.missionTime / 2) * vesselTransform.forward * 1000; - if (threatDistance > 800) - debugString.Append($" from behind afar; engaging barrel roll"); - else - { - debugString.Append($" from behind moderate distance; engaging aggressvie barrel roll and braking"); - steerMode = SteerModes.Aiming; - AdjustThrottle(minSpeed, true, false); - } - } - else - { - breakTarget = threatRelativePosition; - if (evasiveTimer < 1.5f) - breakTarget += Mathf.Sin((float)vessel.missionTime * 2) * vesselTransform.right * 500; - else - breakTarget += -Math.Sign(Mathf.Sin((float)vessel.missionTime * 2)) * vesselTransform.right * 150; - - debugString.Append($" from directly behind and close; breaking hard"); - steerMode = SteerModes.Aiming; - } - } - else - { - float threatDistance = threatRelativePosition.magnitude; - if (threatDistance < 400) - { - breakTarget += Mathf.Sin((float)vessel.missionTime * 2) * vesselTransform.right * 100; - - steerMode = SteerModes.Aiming; - } - else - { - breakTarget = vesselTransform.position + vesselTransform.up * 1500; - breakTarget += Mathf.Sin((float)vessel.missionTime / 2) * vesselTransform.right * 1000 - Mathf.Cos((float)vessel.missionTime / 2) * vesselTransform.forward * 1000; - debugString.Append($" from far side; engaging barrel roll"); - } - } - - float threatAltitudeDiff = Vector3.Dot(threatRelativePosition, vessel.upAxis); + if (threatDirectionFactor > 0.9f) //within 28 degrees in front + { + if (!Reckless) + { + breakTarget += Vector3.Cross(threatRelativePosition.normalized, Mathf.Sign(Mathf.Sin((float)vessel.missionTime / 2)) * vessel.upAxis); + debugString.Append($" from directly ahead!"); + } + else + debugString.Append($" by recklessly ignoring it!"); + } + } + else if (threatDirectionFactor < -0.9) //within ~28 degrees behind + { + float threatDistance = threatRelativePosition.magnitude; + if (threatDistance > 400) + { + if (!Reckless) + { + breakTarget = vesselTransform.position + vesselTransform.up * 1500 - 500 * vessel.upAxis; + breakTarget += Mathf.Sin((float)vessel.missionTime / 2) * vesselTransform.right * 1000 - Mathf.Cos((float)vessel.missionTime / 2) * vesselTransform.forward * 1000; + if (threatDistance > 800) + debugString.Append($" from behind afar; engaging barrel roll"); + else + { + debugString.Append($" from behind moderate distance; engaging aggressvie barrel roll and braking"); + steerMode = SteerModes.Aiming; + AdjustThrottle(minSpeed, true, false); + } + } + if (Reckless) + { + if (threatDistance < 800) + debugString.Append($" from behind; recklessly believes barrel roll enough"); + breakTarget = vesselTransform.position + vesselTransform.up * 1500 - 500 * vessel.upAxis; + breakTarget += Mathf.Sin((float)vessel.missionTime / 2) * vesselTransform.right * 1000 - Mathf.Cos((float)vessel.missionTime / 2) * vesselTransform.forward * 1000; + } + } + else + { + if (!Reckless) + { + breakTarget = threatRelativePosition; + if (evasiveTimer < 1.5f) + breakTarget += Mathf.Sin((float)vessel.missionTime * 2) * vesselTransform.right * 500; + else + breakTarget += -Math.Sign(Mathf.Sin((float)vessel.missionTime * 2)) * vesselTransform.right * 150; + + debugString.Append($" from directly behind and close; breaking hard"); + steerMode = SteerModes.Aiming; + } + if (Reckless) + { + AdjustThrottle(minSpeed - (minSpeed / 4), true, false); + useBrakes = true; + steerMode = SteerModes.Aiming; + debugString.Append($" from 6 O'clock close, breaking hard"); + } + } + } + else + { + float threatDistance = threatRelativePosition.magnitude; + if (threatDistance < 400) + { + if (!Reckless) + { + breakTarget += Mathf.Sin((float)vessel.missionTime * 2) * vesselTransform.right * 100; + steerMode = SteerModes.Aiming; + debugString.Append($" from close flanker, breaking away"); + } + else + { + breakTarget = vesselTransform.position + vesselTransform.up * 1500; + breakTarget += Mathf.Sin((float)vessel.missionTime / 2) * vesselTransform.right * 1000 - Mathf.Cos((float)vessel.missionTime / 2) * vesselTransform.forward * 1000; + debugString.Append($" from side; engaging barrel roll"); + } + } + else + { + if (!Reckless) + { + breakTarget = vesselTransform.position + vesselTransform.up * 1500; + breakTarget += Mathf.Sin((float)vessel.missionTime / 2) * vesselTransform.right * 1000 - Mathf.Cos((float)vessel.missionTime / 2) * vesselTransform.forward * 1000; + debugString.Append($" from far side; engaging barrel roll"); + } + else + { + debugString.Append($" from far side by recklessly ignoring it"); + } + } + } + + float threatAltitudeDiff = Vector3.Dot(threatRelativePosition, vessel.upAxis); if (threatAltitudeDiff > 500) breakTarget += threatAltitudeDiff * vessel.upAxis; //if it's trying to spike us from below, don't go crazy trying to dive below it else diff --git a/BDArmory/Modules/BDModuleSurfaceAI.cs b/BDArmory/Modules/BDModuleSurfaceAI.cs index f766293df..8b56b4814 100644 --- a/BDArmory/Modules/BDModuleSurfaceAI.cs +++ b/BDArmory/Modules/BDModuleSurfaceAI.cs @@ -392,24 +392,15 @@ void PilotLogic() { case WeaponClasses.Gun: case WeaponClasses.DefenseLaser: - var gun = (ModuleWeapon)weaponManager.selectedWeapon; + case WeaponClasses.Rocket: + var gun = (ModuleWeapon)weaponManager.selectedWeapon; if ((gun.yawRange == 0 || gun.maxPitch == gun.minPitch) && gun.FiringSolutionVector != null) { aimingMode = true; if (Vector3.Angle((Vector3)gun.FiringSolutionVector, vessel.transform.up) < 20) targetDirection = (Vector3)gun.FiringSolutionVector; } - break; - - case WeaponClasses.Rocket: - var rocket = (RocketLauncher)weaponManager.selectedWeapon; - if (rocket.yawRange == 0 || rocket.maxPitch == rocket.minPitch) - { - aimingMode = true; - if (Vector3.Angle((Vector3)rocket.FiringSolutionVector, vessel.transform.up) < 20) - targetDirection = (Vector3)rocket.FiringSolutionVector; - } - break; + break; } } targetVelocity = Mathf.Clamp(targetVelocity, PoweredSteering ? CruiseSpeed / 5 : 0, MaxSpeed); // maintain a bit of speed if using powered steering diff --git a/BDArmory/Modules/MissileBase.cs b/BDArmory/Modules/MissileBase.cs index 0160d4098..415960a2f 100644 --- a/BDArmory/Modules/MissileBase.cs +++ b/BDArmory/Modules/MissileBase.cs @@ -1042,7 +1042,7 @@ protected void CollisionEnter(Collision col) { ContactPoint contact = col.contacts[0]; Vector3 pos = contact.point; - BulletHitFX.AttachFlames(pos, col.collider.gameObject.GetComponentInParent()); + //BulletHitFX.AttachFlames(pos, col.collider.gameObject.GetComponentInParent()); } if (HasExploded || !HasFired) return; diff --git a/BDArmory/Modules/MissileFire.cs b/BDArmory/Modules/MissileFire.cs index 3bb1d0265..2c2ceb58e 100644 --- a/BDArmory/Modules/MissileFire.cs +++ b/BDArmory/Modules/MissileFire.cs @@ -122,97 +122,83 @@ public bool rippleFire } } - public void ToggleRippleFire() - { - if (selectedWeapon != null) - { - RippleOption ro; - if (rippleDictionary.ContainsKey(selectedWeapon.GetShortName())) - { - ro = rippleDictionary[selectedWeapon.GetShortName()]; - } - else - { - ro = new RippleOption(false, 650); //default to true ripple fire for guns, otherwise, false - if (selectedWeapon.GetWeaponClass() == WeaponClasses.Gun) - { - ro.rippleFire = currentGun.useRippleFire; - } - rippleDictionary.Add(selectedWeapon.GetShortName(), ro); - } + public void ToggleRippleFire() + { + if (selectedWeapon != null) + { + RippleOption ro; + if (rippleDictionary.ContainsKey(selectedWeapon.GetShortName())) + { + ro = rippleDictionary[selectedWeapon.GetShortName()]; + } + else + { + ro = new RippleOption(false, 650); //default to true ripple fire for guns, otherwise, false + if (selectedWeapon.GetWeaponClass() == WeaponClasses.Gun || selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket) + { + ro.rippleFire = currentGun.useRippleFire; + } + rippleDictionary.Add(selectedWeapon.GetShortName(), ro); + } - ro.rippleFire = !ro.rippleFire; + ro.rippleFire = !ro.rippleFire; - if (selectedWeapon.GetWeaponClass() == WeaponClasses.Gun) - { - List.Enumerator w = vessel.FindPartModulesImplementing().GetEnumerator(); - while (w.MoveNext()) - { - if (w.Current == null) continue; - if (w.Current.GetShortName() == selectedWeapon.GetShortName()) - w.Current.useRippleFire = ro.rippleFire; - } - w.Dispose(); - } - } - } + if (selectedWeapon.GetWeaponClass() == WeaponClasses.Gun || selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket) + { + List.Enumerator w = vessel.FindPartModulesImplementing().GetEnumerator(); + while (w.MoveNext()) + { + if (w.Current == null) continue; + if (w.Current.GetShortName() == selectedWeapon.GetShortName()) + w.Current.useRippleFire = ro.rippleFire; + } + w.Dispose(); + } + } + } - public void AGToggleRipple(KSPActionParam param) + public void AGToggleRipple(KSPActionParam param) { ToggleRippleFire(); } - void ParseRippleOptions() - { - rippleDictionary = new Dictionary(); - //Debug.Log("[BDArmory]: Parsing ripple options"); - if (!string.IsNullOrEmpty(rippleData)) - { - //Debug.Log("[BDArmory]: Ripple data: " + rippleData); - try - { - IEnumerator weapon = rippleData.Split(new char[] { ';' }).AsEnumerable().GetEnumerator(); ; - while (weapon.MoveNext()) - { - if (weapon.Current == string.Empty) continue; - - string[] options = weapon.Current.Split(new char[] { ',' }); - string wpnName = options[0]; - bool rf = bool.Parse(options[1]); - float rpm = float.Parse(options[2]); - RippleOption ro = new RippleOption(rf, rpm); - rippleDictionary.Add(wpnName, ro); - } - weapon.Dispose(); - } - catch (Exception) - { - //Debug.Log("[BDArmory]: Ripple data was invalid."); - rippleData = string.Empty; - } - } - else - { - //Debug.Log("[BDArmory]: Ripple data is empty."); - } - - if (vessel) - { - List.Enumerator rl = vessel.FindPartModulesImplementing().GetEnumerator(); - while (rl.MoveNext()) - { - if (rl.Current == null) continue; - if (!rippleDictionary.ContainsKey(rl.Current.GetShortName())) - { - rippleDictionary.Add(rl.Current.GetShortName(), new RippleOption(false, 650f)); - } - } - rl.Dispose(); - } - hasLoadedRippleData = true; - } - - void SaveRippleOptions(ConfigNode node) + void ParseRippleOptions() + { + rippleDictionary = new Dictionary(); + //Debug.Log("[BDArmory]: Parsing ripple options"); + if (!string.IsNullOrEmpty(rippleData)) + { + //Debug.Log("[BDArmory]: Ripple data: " + rippleData); + try + { + IEnumerator weapon = rippleData.Split(new char[] { ';' }).AsEnumerable().GetEnumerator(); ; + while (weapon.MoveNext()) + { + if (weapon.Current == string.Empty) continue; + + string[] options = weapon.Current.Split(new char[] { ',' }); + string wpnName = options[0]; + bool rf = bool.Parse(options[1]); + float rpm = float.Parse(options[2]); + RippleOption ro = new RippleOption(rf, rpm); + rippleDictionary.Add(wpnName, ro); + } + weapon.Dispose(); + } + catch (Exception) + { + //Debug.Log("[BDArmory]: Ripple data was invalid."); + rippleData = string.Empty; + } + } + else + { + //Debug.Log("[BDArmory]: Ripple data is empty."); + } + hasLoadedRippleData = true; + } + + void SaveRippleOptions(ConfigNode node) { if (rippleDictionary != null) { @@ -241,9 +227,6 @@ void SaveRippleOptions(ConfigNode node) private List loadedVessels = new List(); float targetListTimer; - //rocket aimer handling - RocketLauncher currentRocket; - //sounds AudioSource audioSource; public AudioSource warningAudioSource; @@ -328,22 +311,22 @@ public RadarWarningReceiver rwr //current weapon ref public MissileBase CurrentMissile; - public ModuleWeapon currentGun - { - get - { - if (selectedWeapon != null && selectedWeapon.GetWeaponClass() == WeaponClasses.Gun) - { - return selectedWeapon.GetPart().FindModuleImplementing(); - } - else - { - return null; - } - } - } + public ModuleWeapon currentGun + { + get + { + if (selectedWeapon != null && (selectedWeapon.GetWeaponClass() == WeaponClasses.Gun || selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket)) + { + return selectedWeapon.GetPart().FindModuleImplementing(); + } + else + { + return null; + } + } + } - public bool underAttack; + public bool underAttack; public bool underFire; Coroutine ufRoutine; @@ -351,7 +334,6 @@ public ModuleWeapon currentGun public Vessel incomingThreatVessel; bool guardFiringMissile; - bool disabledRocketAimers; bool antiRadTargetAcquired; Vector3 antiRadiationTarget; bool laserPointDetected; @@ -456,7 +438,7 @@ public void AGJettisonWeapon(KSPActionParam param) } else if (selectedWeapon != null && selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket) { - List.Enumerator rocket = vessel.FindPartModulesImplementing().GetEnumerator(); + List.Enumerator rocket = vessel.FindPartModulesImplementing().GetEnumerator(); while (rocket.MoveNext()) { if (rocket.Current == null) continue; @@ -607,9 +589,10 @@ public void AGFire(KSPActionParam param) [KSPAction("Fire Guns (Hold)")] public void AGFireGunsHold(KSPActionParam param) { - if (weaponIndex <= 0 || (selectedWeapon.GetWeaponClass() != WeaponClasses.Gun && - selectedWeapon.GetWeaponClass() != WeaponClasses.DefenseLaser)) return; - List.Enumerator weap = vessel.FindPartModulesImplementing().GetEnumerator(); + if (weaponIndex <= 0 || (selectedWeapon.GetWeaponClass() != WeaponClasses.Gun && + selectedWeapon.GetWeaponClass() != WeaponClasses.Rocket && + selectedWeapon.GetWeaponClass() != WeaponClasses.DefenseLaser)) return; + List.Enumerator weap = vessel.FindPartModulesImplementing().GetEnumerator(); while (weap.MoveNext()) { if (weap.Current == null) continue; @@ -623,9 +606,10 @@ public void AGFireGunsHold(KSPActionParam param) [KSPAction("Fire Guns (Toggle)")] public void AGFireGunsToggle(KSPActionParam param) { - if (weaponIndex <= 0 || (selectedWeapon.GetWeaponClass() != WeaponClasses.Gun && - selectedWeapon.GetWeaponClass() != WeaponClasses.DefenseLaser)) return; - List.Enumerator weap = vessel.FindPartModulesImplementing().GetEnumerator(); + if (weaponIndex <= 0 || (selectedWeapon.GetWeaponClass() != WeaponClasses.Gun && + selectedWeapon.GetWeaponClass() != WeaponClasses.Rocket && + selectedWeapon.GetWeaponClass() != WeaponClasses.DefenseLaser)) return; + List.Enumerator weap = vessel.FindPartModulesImplementing().GetEnumerator(); while (weap.MoveNext()) { if (weap.Current == null) continue; @@ -833,8 +817,6 @@ public override void OnUpdate() if (weaponArray.Length > 0 && selectedWeapon != weaponArray[weaponIndex]) selectedWeapon = weaponArray[weaponIndex]; - //finding next rocket to shoot (for aimer) - //FindNextRocket(); //targeting if (weaponIndex > 0 && @@ -867,8 +849,7 @@ public override void OnUpdate() //firing missiles and rockets=== if (!guardMode && selectedWeapon != null && - (selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket - || selectedWeapon.GetWeaponClass() == WeaponClasses.Missile + (selectedWeapon.GetWeaponClass() == WeaponClasses.Missile || selectedWeapon.GetWeaponClass() == WeaponClasses.Bomb || selectedWeapon.GetWeaponClass() == WeaponClasses.SLW )) @@ -891,10 +872,10 @@ public override void OnUpdate() } } } - else if (!guardMode && - selectedWeapon != null && - (selectedWeapon.GetWeaponClass() == WeaponClasses.Gun && currentGun.roundsPerMinute < 1500)) - { + else if (!guardMode && + selectedWeapon != null && + ((selectedWeapon.GetWeaponClass() == WeaponClasses.Gun || selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket) && currentGun.roundsPerMinute < 1500)) + { canRipple = true; } else @@ -1499,7 +1480,8 @@ IEnumerator GuardBombRoutine() { if (guardTarget && tgp.groundStabilized && (tgp.groundTargetPosition - guardTarget.transform.position).sqrMagnitude < CurrentMissile.GetBlastRadius() * CurrentMissile.GetBlastRadius()) { - radius = 500; + tgp.CoMLock = true; // make the designator continue to paint target + radius = 500; designatedGPSInfo = new GPSTargetInfo(tgp.bodyRelativeGTP, "Guard Target"); bombStartTime = Time.time; } @@ -1998,20 +1980,7 @@ void FireMissile() { FireCurrentMissile(true); } - else if (selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket) - { - if (!currentRocket || currentRocket.part.name != selectedWeapon.GetPart().name) - { - FindNextRocket(null); - } - - if (currentRocket) - { - currentRocket.FireRocket(); - FindNextRocket(currentRocket); - } - } - + UpdateList(); } @@ -2074,8 +2043,9 @@ public void UpdateList() weap.Dispose(); //dont add empty rocket pods - if (weapon.Current.GetWeaponClass() == WeaponClasses.Rocket && - weapon.Current.GetPart().FindModuleImplementing().GetRocketResource().amount < 1 + if (weapon.Current.GetWeaponClass() == WeaponClasses.Rocket && + (weapon.Current.GetPart().FindModuleImplementing().rocketPod && !weapon.Current.GetPart().FindModuleImplementing().externalAmmo) && + weapon.Current.GetPart().FindModuleImplementing().GetRocketResource().amount < 1 && !BDArmorySettings.INFINITE_AMMO) { continue; @@ -2212,10 +2182,10 @@ private void UpdateSelectedWeaponState() bombPart = null; } - //gun ripple stuff - if (selectedWeapon != null && selectedWeapon.GetWeaponClass() == WeaponClasses.Gun && - currentGun.roundsPerMinute < 1500) - { + //gun ripple stuff + if (selectedWeapon != null && (selectedWeapon.GetWeaponClass() == WeaponClasses.Gun || selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket) && + currentGun.roundsPerMinute < 1500) + { float counter = 0; // Used to get a count of the ripple weapons. a float version of rippleGunCount. gunRippleIndex = 0; // This value will be incremented as we set the ripple weapons @@ -2313,12 +2283,8 @@ private void UpdateSelectedWeaponState() weapon.Dispose(); } - //rocket - FindNextRocket(null); - ToggleTurret(); SetMissileTurrets(); - SetRocketTurrets(); SetRotaryRails(); } @@ -2502,129 +2468,6 @@ void SetMissileTurrets() mt.Dispose(); } - void SetRocketTurrets() - { - RocketLauncher currentTurret = null; - if (selectedWeapon != null && selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket) - { - RocketLauncher srl = selectedWeapon.GetPart().FindModuleImplementing(); - if (srl && srl.turret) - { - currentTurret = srl; - } - } - - List.Enumerator rl = vessel.FindPartModulesImplementing().GetEnumerator(); - while (rl.MoveNext()) - { - if (rl.Current == null) continue; - rl.Current.weaponManager = this; - if (rl.Current.turret) - { - if (currentTurret && rl.Current.part.name == currentTurret.part.name) - { - rl.Current.EnableTurret(); - } - else - { - rl.Current.DisableTurret(); - } - } - } - rl.Dispose(); - } - - void FindNextRocket(RocketLauncher lastFired) - { - if (weaponIndex > 0 && selectedWeapon?.GetWeaponClass() == WeaponClasses.Rocket) - { - disabledRocketAimers = false; - - //first check sym of last fired - if (lastFired && lastFired.part.name == selectedWeapon.GetPart().name) - { - List.Enumerator pSym = lastFired.part.symmetryCounterparts.GetEnumerator(); - while (pSym.MoveNext()) - { - if (pSym.Current == null) continue; - bool hasRocket = false; - RocketLauncher rl = pSym.Current.FindModuleImplementing(); - IEnumerator r = rl.part.Resources.GetEnumerator(); - while (r.MoveNext()) - { - if (r.Current == null) continue; - if ((r.Current.resourceName != rl.rocketType || !(r.Current.amount > 0)) - && !BDArmorySettings.INFINITE_AMMO) continue; - hasRocket = true; - break; - } - r.Dispose(); - - if (!hasRocket) continue; - if (currentRocket) currentRocket.drawAimer = false; - - rl.drawAimer = true; - currentRocket = rl; - selectedWeapon = currentRocket; - return; - } - } - - if (!lastFired && currentRocket && currentRocket.part.name == selectedWeapon.GetPart().name) - { - currentRocket.drawAimer = true; - selectedWeapon = currentRocket; - return; - } - - //then check for other rocket - bool foundRocket = false; - List.Enumerator orl = vessel.FindPartModulesImplementing().GetEnumerator(); - while (orl.MoveNext()) - { - if (orl.Current == null) continue; - if (!foundRocket && orl.Current.part.partInfo.title == selectedWeapon.GetPart().partInfo.title) - { - bool hasRocket = false; - IEnumerator r = orl.Current.part.Resources.GetEnumerator(); - while (r.MoveNext()) - { - if (r.Current == null) continue; - if (r.Current.amount > 0 || BDArmorySettings.INFINITE_AMMO) hasRocket = true; - else orl.Current.drawAimer = false; - } - r.Dispose(); - - if (!hasRocket) continue; - if (currentRocket != null) currentRocket.drawAimer = false; - orl.Current.drawAimer = true; - currentRocket = orl.Current; - selectedWeapon = currentRocket; - //return; - foundRocket = true; - } - else - { - orl.Current.drawAimer = false; - } - } - orl.Dispose(); - } - //not using a rocket, disable reticles. - else if (!disabledRocketAimers) - { - List.Enumerator rl = vessel.FindPartModulesImplementing().GetEnumerator(); - while (rl.MoveNext()) - { - if (rl.Current == null) continue; - rl.Current.drawAimer = false; - currentRocket = null; - } - rl.Dispose(); - disabledRocketAimers = true; - } - } - public void CycleWeapon(bool forward) { if (forward) weaponIndex++; @@ -3189,6 +3032,7 @@ bool SmartPickWeapon_EngagementEnvelope(TargetInfo target) float targetLaserDamage = 0; float targetYield = 0; float targetRocketPower = 0; + float targetRocketAccel = 0; if (target.isMissile) { @@ -3270,7 +3114,7 @@ bool SmartPickWeapon_EngagementEnvelope(TargetInfo target) // 1. AA missiles, if range > gunRange // 1. Lasers // 2. Guns - // + // 3. Rockets List.Enumerator item = weaponTypesAir.GetEnumerator(); while (item.MoveNext()) { @@ -3321,8 +3165,29 @@ bool SmartPickWeapon_EngagementEnvelope(TargetInfo target) targetWeapon = item.Current; targetWeaponRPM = candidateRPM; } + if (candidateClass == WeaponClasses.Rocket) + { + //for AA, favor higher accel and proxifuze + float canidateRocketAccel = (((ModuleWeapon)item.Current).thrust/ ((ModuleWeapon)item.Current).rocketMass); + float candidateRPM = ((ModuleWeapon)item.Current).roundsPerMinute; + bool canidatePFuzed = ((ModuleWeapon)item.Current).proximityDetonation; - if (candidateClass != WeaponClasses.Missile) continue; + if ((targetWeapon != null) && (targetRocketAccel > canidateRocketAccel)) + { + candidateRPM *= 1.5f; //weight towards faster rockets + } + if (targetWeapon != null && canidatePFuzed) + { + candidateRPM *= 1.5f; // weight selection towards flak ammo + } + if ((targetWeapon != null) && (targetWeaponRPM > candidateRPM)) + continue; + + targetWeapon = item.Current; + targetWeaponRPM = candidateRPM; + targetRocketAccel = canidateRocketAccel; + } + if (candidateClass != WeaponClasses.Missile) continue; MissileLauncher mlauncher = item.Current as MissileLauncher; float candidateTDPS = 0f; @@ -3343,8 +3208,8 @@ bool SmartPickWeapon_EngagementEnvelope(TargetInfo target) } else if (distance > gunRange) { - if (targetWeapon.GetWeaponClass() == WeaponClasses.Gun || targetWeaponTDPS > candidateTDPS) - continue; //dont replace guns or better missiles + if (targetWeapon.GetWeaponClass() == WeaponClasses.Gun || targetWeapon.GetWeaponClass() == WeaponClasses.Rocket || targetWeapon.GetWeaponClass() == WeaponClasses.DefenseLaser || targetWeaponTDPS > candidateTDPS) + continue; //dont replace missiles w/ guns targetWeapon = item.Current; targetWeaponTDPS = candidateTDPS; @@ -3376,7 +3241,7 @@ bool SmartPickWeapon_EngagementEnvelope(TargetInfo target) // - guided missiles // - by blast strength float canidateYield = ((MissileBase)item.Current).GetBlastRadius(); - double srfSpeed = currentTarget.Vessel.horizontalSrfSpeed; + double srfSpeed = currentTarget.Vessel.srf_velocity.magnitude; bool canidateAGM = false; bool canidateAntiRad = false; @@ -3445,14 +3310,25 @@ bool SmartPickWeapon_EngagementEnvelope(TargetInfo target) if (droptime > 0) //make sure it's an airdropped torpedo if flying { - if (!vessel.LandedOrSplashed) - { - if (targetYield > canidateYield) continue; - targetYield = canidateYield; - targetWeapon = item.Current; - if (distance > gunRange) - break; - } + if (!vessel.LandedOrSplashed) + { + if (targetYield > canidateYield) continue; + targetYield = canidateYield; + targetWeapon = item.Current; + if (distance > gunRange) + break; + } + } + else + { + if (vessel.LandedOrSplashed) + { + if (targetYield > canidateYield) continue; + targetYield = canidateYield; + targetWeapon = item.Current; + if (distance > canidateYield) //if outside blastradius, fire torp + break; + } } } @@ -3460,34 +3336,53 @@ bool SmartPickWeapon_EngagementEnvelope(TargetInfo target) { // only useful if we are flying float canidateYield = ((MissileBase)item.Current).GetBlastRadius(); + double srfSpeed = currentTarget.Vessel.srf_velocity.magnitude; if (!vessel.LandedOrSplashed) { // Priority Sequence: - // - guided (JDAM) + // if Moving target - Cluster, Standard, JDAM + // if static target - JDAM, standard, cluster // - by blast strength - // - find way to implement cluster bomb selection priority? - if (((MissileBase)item.Current).GuidanceMode == MissileBase.GuidanceModes.AGMBallistic) + // + if (((MissileBase)item.Current).GuidanceMode == MissileBase.GuidanceModes.AGMBallistic || ((MissileBase)item.Current).TargetingMode == MissileBase.TargetingModes.Gps) { - if (targetYield > canidateYield) continue; //prioritize biggest Boom + if (targetWeapon != null && targetYield > canidateYield) continue; + if (srfSpeed > 1) + { + canidateYield *= 0.2f;// GPS munitions aren't going to hit moving things, so weight selection against them + } + else + { + canidateYield *= 1.5f; // weight selection towards JDAMs if static target + } targetYield = canidateYield; targetWeapon = item.Current; - if (targetWeapon != null && distance > canidateYield) // don't drop bombs when within blast radius - break; //Prioritize guided bombs } if (((MissileBase)item.Current).GuidanceMode == MissileBase.GuidanceModes.None) { - if (targetYield > canidateYield) continue; - targetYield = canidateYield; - targetWeapon = item.Current; - if (targetWeapon != null && distance > canidateYield) - break; //then standard + if (item.Current.GetPart().FindModuleImplementing() != null) + { + if (targetWeapon != null && targetYield > (canidateYield * 20)) continue; + if (srfSpeed > 1) + { + canidateYield *= 20f; // weight selection towards cluster munitions agaisnt moving targets + } + targetYield = canidateYield; + targetWeapon = item.Current; + } + else + { + if (targetWeapon != null && targetYield > canidateYield) continue; + targetYield = canidateYield; + targetWeapon = item.Current; + } } - } + } } - if (candidateClass == WeaponClasses.Rocket) + if (candidateClass == WeaponClasses.Rocket) { - float canidateRocketPower = ((RocketLauncher)item.Current).blastForce; + float canidateRocketPower = ((ModuleWeapon)item.Current).blastForce; if ((targetWeapon != null) && (targetWeapon.GetWeaponClass() == WeaponClasses.Bomb)) continue; // dont replace bombs @@ -3498,7 +3393,7 @@ bool SmartPickWeapon_EngagementEnvelope(TargetInfo target) targetRocketPower = canidateRocketPower; } - if ((candidateClass != WeaponClasses.Gun)) continue; + if ((candidateClass != WeaponClasses.Gun)) continue; // Flying: prefer bombs/rockets/missiles if (!vessel.LandedOrSplashed) if (targetWeapon != null) @@ -3591,10 +3486,34 @@ bool CheckEngagementEnvelope(IBDWeapon weaponCandidate, float distanceToTarget) switch (weaponCandidate.GetWeaponClass()) { - case WeaponClasses.DefenseLaser: - // TODO: is laser treated like a gun? + case WeaponClasses.DefenseLaser: + { + ModuleWeapon laser = (ModuleWeapon)weaponCandidate; + + // check yaw range of turret + ModuleTurret turret = laser.turret; + float gimbalTolerance = vessel.LandedOrSplashed ? 0 : 15; + if (turret != null) + if (!TargetInTurretRange(turret, gimbalTolerance)) + return false; + + // check overheat + if (laser.isOverheated) + return false; - case WeaponClasses.Gun: + // check ammo + if (CheckAmmo(laser)) + { + if (BDArmorySettings.DRAW_DEBUG_LABELS) + { + Debug.Log("[BDArmory] : " + vessel.vesselName + " - Firing possible with " + weaponCandidate.GetShortName()); + } + return true; + } + break; + } + + case WeaponClasses.Gun: { ModuleWeapon gun = (ModuleWeapon)weaponCandidate; @@ -3606,7 +3525,7 @@ bool CheckEngagementEnvelope(IBDWeapon weaponCandidate, float distanceToTarget) return false; // check overheat - if (gun.isOverheated) + if (gun.isOverheated || gun.isReloading) return false; // check ammo @@ -3652,19 +3571,30 @@ bool CheckEngagementEnvelope(IBDWeapon weaponCandidate, float distanceToTarget) return true; // TODO: bomb always allowed? break; - case WeaponClasses.Rocket: - { - RocketLauncher rocketlauncher = (RocketLauncher)weaponCandidate; - // check yaw range of turret - var turret = rocketlauncher.turret; - float gimbalTolerance = vessel.LandedOrSplashed ? 0 : 15; - if (turret != null) - if (TargetInTurretRange(turret, gimbalTolerance)) - return true; - break; - } + case WeaponClasses.Rocket: + { + ModuleWeapon rocket = (ModuleWeapon)weaponCandidate; - case WeaponClasses.SLW: + // check yaw range of turret + ModuleTurret turret = rocket.turret; + float gimbalTolerance = vessel.LandedOrSplashed ? 0 : 15; + if (turret != null) + if (!TargetInTurretRange(turret, gimbalTolerance)) + return false; + + // check ammo + if (CheckAmmo(rocket)) + { + if (BDArmorySettings.DRAW_DEBUG_LABELS) + { + Debug.Log("[BDArmory] : " + vessel.vesselName + " - Firing possible with " + weaponCandidate.GetShortName()); + } + return true; + } + break; + } + + case WeaponClasses.SLW: { // Enable sonar, or radar, if no sonar is found. if (((MissileBase)weaponCandidate).TargetingMode == MissileBase.TargetingModes.Radar) @@ -3907,11 +3837,11 @@ void GuardMode() UpdateGuardViewScan(); - //setting turrets to guard mode - if (selectedWeapon != null && selectedWeapon.GetWeaponClass() == WeaponClasses.Gun) - { - //make this not have to go every frame - List.Enumerator weapon = vessel.FindPartModulesImplementing().GetEnumerator(); + //setting turrets to guard mode + if (selectedWeapon != null && (selectedWeapon.GetWeaponClass() == WeaponClasses.Gun || selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket || selectedWeapon.GetWeaponClass() == WeaponClasses.DefenseLaser)) + { + //make this not have to go every frame + List.Enumerator weapon = vessel.FindPartModulesImplementing().GetEnumerator(); while (weapon.MoveNext()) { if (weapon.Current == null) continue; @@ -3926,9 +3856,9 @@ void GuardMode() weapon.Dispose(); } - if (!guardTarget && selectedWeapon != null && selectedWeapon.GetWeaponClass() == WeaponClasses.Gun) - { - List.Enumerator weapon = vessel.FindPartModulesImplementing().GetEnumerator(); + if (!guardTarget && selectedWeapon != null && (selectedWeapon.GetWeaponClass() == WeaponClasses.Gun || selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket || selectedWeapon.GetWeaponClass() == WeaponClasses.DefenseLaser)) + { + List.Enumerator weapon = vessel.FindPartModulesImplementing().GetEnumerator(); while (weapon.MoveNext()) { if (weapon.Current == null) continue; @@ -4018,10 +3948,10 @@ void GuardMode() StartCoroutine(GuardBombRoutine()); } } - else if (selectedWeapon.GetWeaponClass() == WeaponClasses.Gun || - selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket || - selectedWeapon.GetWeaponClass() == WeaponClasses.DefenseLaser) - { + else if (selectedWeapon.GetWeaponClass() == WeaponClasses.Gun || + selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket || + selectedWeapon.GetWeaponClass() == WeaponClasses.DefenseLaser) + { StartCoroutine(GuardTurretRoutine()); } } @@ -4155,47 +4085,25 @@ public void ForceScan() targetScanTimer = -100; } - void StartGuardTurretFiring() - { - if (!guardTarget) return; - if (selectedWeapon == null) return; - - if (selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket) - { - List.Enumerator weapon = vessel.FindPartModulesImplementing().GetEnumerator(); - while (weapon.MoveNext()) - { - if (weapon.Current == null) continue; - if (weapon.Current.GetShortName() != selectedWeaponString) continue; - if (BDArmorySettings.DRAW_DEBUG_LABELS) - { - Debug.Log("[BDArmory]: Setting rocket to auto fire"); - } - weapon.Current.legacyGuardTarget = guardTarget; - weapon.Current.autoFireStartTime = Time.time; - //weapon.Current.autoFireDuration = targetScanInterval / 2; - weapon.Current.autoFireDuration = (fireBurstLength < 0.5) ? targetScanInterval / 2 : fireBurstLength; - weapon.Current.autoRippleRate = rippleFire ? rippleRPM : 0; - } - weapon.Dispose(); - } - else - { - List.Enumerator weapon = vessel.FindPartModulesImplementing().GetEnumerator(); - while (weapon.MoveNext()) - { - if (weapon.Current == null) continue; - if (weapon.Current.GetShortName() != selectedWeapon.GetShortName()) continue; - weapon.Current.visualTargetVessel = guardTarget; - weapon.Current.autoFireTimer = Time.time; - //weapon.Current.autoFireLength = 3 * targetScanInterval / 4; - weapon.Current.autoFireLength = (fireBurstLength < 0.5) ? targetScanInterval / 2 : fireBurstLength; - } - weapon.Dispose(); - } - } - - public void SetOverrideTarget(TargetInfo target) + void StartGuardTurretFiring() + { + if (!guardTarget) return; + if (selectedWeapon == null) return; + + List.Enumerator weapon = vessel.FindPartModulesImplementing().GetEnumerator(); + while (weapon.MoveNext()) + { + if (weapon.Current == null) continue; + if (weapon.Current.GetShortName() != selectedWeapon.GetShortName()) continue; + weapon.Current.visualTargetVessel = guardTarget; + weapon.Current.autoFireTimer = Time.time; + //weapon.Current.autoFireLength = 3 * targetScanInterval / 4; + weapon.Current.autoFireLength = (fireBurstLength < 0.5) ? targetScanInterval / 2 : fireBurstLength; + } + weapon.Dispose(); + } + + public void SetOverrideTarget(TargetInfo target) { overrideTarget = target; targetScanTimer = -100; @@ -4247,88 +4155,75 @@ bool AIMightDirectFire() return (AI == null || !AI.pilotEnabled || !AI.CanEngage() || !guardTarget || !AI.IsValidFixedWeaponTarget(guardTarget)); } - #endregion Guard - - #region Turret - - int CheckTurret(float distance) - { - if (weaponIndex == 0 || selectedWeapon == null || - !(selectedWeapon.GetWeaponClass() == WeaponClasses.Gun || - selectedWeapon.GetWeaponClass() == WeaponClasses.DefenseLaser || - selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket)) - { - return 2; - } - if (BDArmorySettings.DRAW_DEBUG_LABELS) - { - Debug.Log("[BDArmory]: Checking turrets"); - } - float finalDistance = distance; - //vessel.LandedOrSplashed ? distance : distance/2; //decrease distance requirement if airborne - - if (selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket) - { - List.Enumerator rl = vessel.FindPartModulesImplementing().GetEnumerator(); - while (rl.MoveNext()) - { - if (rl.Current == null) continue; - if (rl.Current.part.partInfo.title != selectedWeapon.GetPart().partInfo.title) continue; - float gimbalTolerance = vessel.LandedOrSplashed ? 0 : 15; - if (!(rl.Current.maxTargetingRange >= finalDistance) || - !TargetInTurretRange(rl.Current.turret, gimbalTolerance)) continue; - if (BDArmorySettings.DRAW_DEBUG_LABELS) - { - Debug.Log("[BDArmory]: " + selectedWeapon + " is valid!"); - } - return 1; - } - rl.Dispose(); - } - else - { - List.Enumerator weapon = vessel.FindPartModulesImplementing().GetEnumerator(); - while (weapon.MoveNext()) - { - if (weapon.Current == null) continue; - if (weapon.Current.GetShortName() != selectedWeapon.GetShortName()) continue; - float gimbalTolerance = vessel.LandedOrSplashed ? 0 : 15; - if (((AI != null && AI.pilotEnabled && AI.CanEngage()) || (TargetInTurretRange(weapon.Current.turret, gimbalTolerance))) && weapon.Current.maxEffectiveDistance >= finalDistance) - { - if (weapon.Current.isOverheated) - { - if (BDArmorySettings.DRAW_DEBUG_LABELS) - { - Debug.Log("[BDArmory]: " + selectedWeapon + " is overheated!"); - } - return -1; - } - if (CheckAmmo(weapon.Current) || BDArmorySettings.INFINITE_AMMO) - { - if (BDArmorySettings.DRAW_DEBUG_LABELS) - { - Debug.Log("[BDArmory]: " + selectedWeapon + " is valid!"); - } - return 1; - } - if (BDArmorySettings.DRAW_DEBUG_LABELS) - { - Debug.Log("[BDArmory]: " + selectedWeapon + " has no ammo."); - } - return -1; - } - if (BDArmorySettings.DRAW_DEBUG_LABELS) - { - Debug.Log("[BDArmory]: " + selectedWeapon + " cannot reach target (" + distance + " vs " + weapon.Current.maxEffectiveDistance + ", yawRange: " + weapon.Current.yawRange + "). Continuing."); - } - //else return 0; - } - weapon.Dispose(); - } - return 2; - } + #endregion Guard + + #region Turret + + int CheckTurret(float distance) + { + if (weaponIndex == 0 || selectedWeapon == null || + !(selectedWeapon.GetWeaponClass() == WeaponClasses.Gun || + selectedWeapon.GetWeaponClass() == WeaponClasses.DefenseLaser || + selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket)) + { + return 2; + } + if (BDArmorySettings.DRAW_DEBUG_LABELS) + { + Debug.Log("[BDArmory]: Checking turrets"); + } + float finalDistance = distance; + //vessel.LandedOrSplashed ? distance : distance/2; //decrease distance requirement if airborne + + List.Enumerator weapon = vessel.FindPartModulesImplementing().GetEnumerator(); + while (weapon.MoveNext()) + { + if (weapon.Current == null) continue; + if (weapon.Current.GetShortName() != selectedWeapon.GetShortName()) continue; + float gimbalTolerance = vessel.LandedOrSplashed ? 0 : 15; + if (((AI != null && AI.pilotEnabled && AI.CanEngage()) || (TargetInTurretRange(weapon.Current.turret, gimbalTolerance))) && weapon.Current.maxEffectiveDistance >= finalDistance) + { + if (weapon.Current.isOverheated || weapon.Current.isReloading) + { + if (BDArmorySettings.DRAW_DEBUG_LABELS) + { + Debug.Log("[BDArmory]: " + selectedWeapon + " is overheated!"); + } + return -1; + } + if (weapon.Current.hasGunner) + { + if (BDArmorySettings.DRAW_DEBUG_LABELS) + { + Debug.Log("[BDArmory]: " + selectedWeapon + " has no gunner!"); + } + return -1; + } + if (CheckAmmo(weapon.Current) || BDArmorySettings.INFINITE_AMMO) + { + if (BDArmorySettings.DRAW_DEBUG_LABELS) + { + Debug.Log("[BDArmory]: " + selectedWeapon + " is valid!"); + } + return 1; + } + if (BDArmorySettings.DRAW_DEBUG_LABELS) + { + Debug.Log("[BDArmory]: " + selectedWeapon + " has no ammo."); + } + return -1; + } + if (BDArmorySettings.DRAW_DEBUG_LABELS) + { + Debug.Log("[BDArmory]: " + selectedWeapon + " cannot reach target (" + distance + " vs " + weapon.Current.maxEffectiveDistance + ", yawRange: " + weapon.Current.yawRange + "). Continuing."); + } + //else return 0; + } + weapon.Dispose(); + return 2; + } - bool TargetInTurretRange(ModuleTurret turret, float tolerance) + bool TargetInTurretRange(ModuleTurret turret, float tolerance) { if (!turret) { diff --git a/BDArmory/Modules/ModuleDrainFuel.cs b/BDArmory/Modules/ModuleDrainFuel.cs new file mode 100644 index 000000000..164e16afd --- /dev/null +++ b/BDArmory/Modules/ModuleDrainFuel.cs @@ -0,0 +1,55 @@ +using System.Linq; +using UnityEngine; + +namespace BDArmory.Modules +{ + public class ModuleDrainFuel : PartModule + { + public float drainRate; + public float drainDuration = 20; + + public override void OnStart(StartState state) + { + if (HighLogic.LoadedSceneIsFlight) + { + part.force_activate(); + } + base.OnStart(state); + } + + public void Update() + { + if (HighLogic.LoadedSceneIsFlight) + { + drainDuration -= Time.fixedDeltaTime; + if (drainDuration > 0) + { + PartResource fuel = part.Resources.Where(pr => pr.resourceName == "LiquidFuel").FirstOrDefault(); + if (fuel != null) + { + if (fuel.amount >= 0) + { + part.RequestResource("LiquidFuel", drainRate * Time.fixedDeltaTime); + } + } + PartResource ox = part.Resources.Where(pr => pr.resourceName == "Oxidizer").FirstOrDefault(); + if (ox != null) + { + if (ox.amount >= 0) + { + part.RequestResource("Oxidizer", drainRate * Time.fixedDeltaTime); + } + } + } + else + { + foreach (var pe in part.GetComponentsInChildren()) + { + EffectBehaviour.RemoveParticleEmitter(pe); + } + part.RemoveModule(this); + } + } + } + } +} diff --git a/BDArmory/Modules/ModuleTargetingCamera.cs b/BDArmory/Modules/ModuleTargetingCamera.cs index 356494aa0..d3c213f08 100644 --- a/BDArmory/Modules/ModuleTargetingCamera.cs +++ b/BDArmory/Modules/ModuleTargetingCamera.cs @@ -833,8 +833,11 @@ void WindowTargetCam(int windowID) ResizeTargetWindow(); } } - //ResetZoomKeys(); - BDGUIUtils.RepositionWindow(ref BDArmorySetup.WindowRectTargetingCam); + //ResetZoomKeys(); + if (BDArmorySettings.STRICT_WINDOW_BOUNDARIES) + { + BDGUIUtils.RepositionWindow(ref BDArmorySetup.WindowRectTargetingCam); + } } internal static void UpdateTargetScale(float diff) diff --git a/BDArmory/Modules/ModuleWeapon.cs b/BDArmory/Modules/ModuleWeapon.cs index f234a8ef4..2c2d2747f 100644 --- a/BDArmory/Modules/ModuleWeapon.cs +++ b/BDArmory/Modules/ModuleWeapon.cs @@ -16,188 +16,201 @@ namespace BDArmory.Modules { - public class ModuleWeapon : EngageableWeapon, IBDWeapon - { - #region Declarations - - public static ObjectPool bulletPool; - public static ObjectPool shellPool; - - Coroutine startupRoutine; - Coroutine shutdownRoutine; - - bool finalFire; - - public int rippleIndex = 0; - public string OriginalShortName { get; private set; } - - // WeaponTypes.Cannon is deprecated. identical behavior is achieved with WeaponType.Ballistic and bulletInfo.explosive = true. - public enum WeaponTypes - { - Ballistic, - Cannon, - Laser - } - - public enum WeaponStates - { - Enabled, - Disabled, - PoweringUp, - PoweringDown - } - - public enum BulletDragTypes - { - None, - AnalyticEstimate, - NumericalIntegration - } - - public WeaponStates weaponState = WeaponStates.Disabled; - - //animations - private float fireAnimSpeed = 1; - //is set when setting up animation so it plays a full animation for each shot (animation speed depends on rate of fire) - - public float bulletBallisticCoefficient; - - public WeaponTypes eWeaponType; - - public float heat; - public bool isOverheated; - - private bool wasFiring; - //used for knowing when to stop looped audio clip (when you're not shooting, but you were) - - AudioClip reloadCompleteAudioClip; - AudioClip fireSound; - AudioClip overheatSound; - AudioClip chargeSound; - AudioSource audioSource; - AudioSource audioSource2; - AudioLowPassFilter lowpassFilter; - - private BDStagingAreaGauge gauge; - private int AmmoID; - - //AI - public bool aiControlled = false; - public bool autoFire; - public float autoFireLength = 0; - public float autoFireTimer = 0; - - //used by AI to lead moving targets - private float targetDistance; - private Vector3 targetPosition; - private Vector3 targetVelocity; // local frame velocity - private Vector3 targetAcceleration; // local frame - private Vector3 targetVelocityPrevious; // for acceleration calculation - private Vector3 targetAccelerationPrevious; - private Vector3 relativeVelocity; - Vector3 finalAimTarget; - Vector3 lastFinalAimTarget; - public Vessel visualTargetVessel; - bool targetAcquired; - - public Vector3? FiringSolutionVector => finalAimTarget.IsZero() ? (Vector3?)null : (finalAimTarget - fireTransforms[0].position).normalized; - - public bool recentlyFiring //used by guard to know if it should evaid this - { - get { return Time.time - timeFired < 1; } - } - - //used to reduce volume of audio if multiple guns are being fired (needs to be improved/changed) - //private int numberOfGuns = 0; - - //AI will fire gun if target is within this Cos(angle) of barrel - public float maxAutoFireCosAngle = 0.9993908f; //corresponds to ~2 degrees - - //aimer textures - Vector3 pointingAtPosition; - Vector3 bulletPrediction; - Vector3 fixedLeadOffset = Vector3.zero; - - //gapless particles - List gaplessEmitters = new List(); - - //muzzleflash emitters - List muzzleFlashEmitters; - - //module references - [KSPField] public int turretID = 0; - public ModuleTurret turret; - MissileFire mf; - - public MissileFire weaponManager - { - get - { - if (mf) return mf; - List.Enumerator wm = vessel.FindPartModulesImplementing().GetEnumerator(); - while (wm.MoveNext()) - { - if (wm.Current == null) continue; - mf = wm.Current; - break; - } - wm.Dispose(); - return mf; - } - } - - LineRenderer[] laserRenderers; - - bool pointingAtSelf; //true if weapon is pointing at own vessel - bool userFiring; - Vector3 laserPoint; - public bool slaved; - - public Transform turretBaseTransform - { - get - { - if (turret) - { - return turret.yawTransform.parent; - } - else - { - return fireTransforms[0]; - } - } - } - - public float maxPitch - { - get { return turret ? turret.maxPitch : 0; } - } - - public float minPitch - { - get { return turret ? turret.minPitch : 0; } - } - - public float yawRange - { - get { return turret ? turret.yawRange : 0; } - } - - //weapon interface - public WeaponClasses GetWeaponClass() - { - return WeaponClasses.Gun; - } - - public Part GetPart() - { - return part; - } - - public double ammoCount; + public class ModuleWeapon : EngageableWeapon, IBDWeapon + { + #region Declarations + + public static ObjectPool bulletPool; + public static ObjectPool shellPool; + + Coroutine startupRoutine; + Coroutine shutdownRoutine; + + bool finalFire; + + public int rippleIndex = 0; + public string OriginalShortName { get; private set; } + + // WeaponTypes.Cannon is deprecated. identical behavior is achieved with WeaponType.Ballistic and bulletInfo.explosive = true. + public enum WeaponTypes + { + Ballistic, + Rocket, //Cannon's depreciated, lets use this for rocketlaunchers + Laser + } + + public enum WeaponStates + { + Enabled, + Disabled, + PoweringUp, + PoweringDown + } + + public enum BulletDragTypes + { + None, + AnalyticEstimate, + NumericalIntegration + } + + public WeaponStates weaponState = WeaponStates.Disabled; + + //animations + private float fireAnimSpeed = 1; + //is set when setting up animation so it plays a full animation for each shot (animation speed depends on rate of fire) + + public float bulletBallisticCoefficient; + + public WeaponTypes eWeaponType; + + public float heat; + public bool isOverheated; + + private bool wasFiring; + //used for knowing when to stop looped audio clip (when you're not shooting, but you were) + + AudioClip reloadCompleteAudioClip; + AudioClip fireSound; + AudioClip overheatSound; + AudioClip chargeSound; + AudioSource audioSource; + AudioSource audioSource2; + AudioLowPassFilter lowpassFilter; + + private BDStagingAreaGauge gauge; + private int AmmoID; + + //AI + public bool aiControlled = false; + public bool autoFire; + public float autoFireLength = 0; + public float autoFireTimer = 0; + + //used by AI to lead moving targets + private float targetDistance; + private Vector3 targetPosition; + private Vector3 targetVelocity; // local frame velocity + private Vector3 targetAcceleration; // local frame + private Vector3 targetVelocityPrevious; // for acceleration calculation + private Vector3 targetAccelerationPrevious; + private Vector3 relativeVelocity; + Vector3 finalAimTarget; + Vector3 lastFinalAimTarget; + public Vessel visualTargetVessel; + bool targetAcquired; + + public Vector3? FiringSolutionVector => finalAimTarget.IsZero() ? (Vector3?)null : (finalAimTarget - fireTransforms[0].position).normalized; + + public bool recentlyFiring //used by guard to know if it should evaid this + { + get { return Time.time - timeFired < 1; } + } + + //used to reduce volume of audio if multiple guns are being fired (needs to be improved/changed) + //private int numberOfGuns = 0; + + //AI will fire gun if target is within this Cos(angle) of barrel + public float maxAutoFireCosAngle = 0.9993908f; //corresponds to ~2 degrees + + //aimer textures + Vector3 pointingAtPosition; + Vector3 bulletPrediction; + Vector3 fixedLeadOffset = Vector3.zero; + + float predictedFlightTime = 1; + Vector3 trajectoryOffset = Vector3.zero; + + //gapless particles + List gaplessEmitters = new List(); + + //muzzleflash emitters + List muzzleFlashEmitters; + + //module references + [KSPField] public int turretID = 0; + public ModuleTurret turret; + MissileFire mf; + + public MissileFire weaponManager + { + get + { + if (mf) return mf; + List.Enumerator wm = vessel.FindPartModulesImplementing().GetEnumerator(); + while (wm.MoveNext()) + { + if (wm.Current == null) continue; + mf = wm.Current; + break; + } + wm.Dispose(); + return mf; + } + } + + LineRenderer[] laserRenderers; + + bool pointingAtSelf; //true if weapon is pointing at own vessel + bool userFiring; + Vector3 laserPoint; + public bool slaved; + + public Transform turretBaseTransform + { + get + { + if (turret) + { + return turret.yawTransform.parent; + } + else + { + return fireTransforms[0]; + } + } + } + + public float maxPitch + { + get { return turret ? turret.maxPitch : 0; } + } + + public float minPitch + { + get { return turret ? turret.minPitch : 0; } + } + + public float yawRange + { + get { return turret ? turret.yawRange : 0; } + } + + //weapon interface + public WeaponClasses GetWeaponClass() + { + if (eWeaponType == WeaponTypes.Ballistic) + { + return WeaponClasses.Gun; + } + else if (eWeaponType == WeaponTypes.Rocket) + { + return WeaponClasses.Rocket; + } + else + { + return WeaponClasses.DefenseLaser; + } + } + + public Part GetPart() + { + return part; + } + public double ammoCount; public string ammoLeft; //#191 - - public string GetSubLabel() //I think BDArmorySetup only calls this for the first instance of a particular ShortName, so this probably won't result in a group of n guns having n GetSublabelCalls per frame + + public string GetSubLabel() //think BDArmorySetup only calls this for the first instance of a particular ShortName, so this probably won't result in a group of n guns having n GetSublabelCalls per frame { List.Enumerator craftPart = vessel.parts.GetEnumerator(); ammoLeft = "Ammo Left: " + ammoCount.ToString("0"); @@ -207,7 +220,7 @@ public Part GetPart() { if (weapon.Current == null) continue; if (weapon.Current.GetShortName() != this.GetShortName()) continue; - if (weapon.Current.AmmoID != this.AmmoID && weapon.Current. AmmoID != lastAmmoID) + if (weapon.Current.AmmoID != this.AmmoID && weapon.Current.AmmoID != lastAmmoID) { vessel.GetConnectedResourceTotals(weapon.Current.AmmoID, out double ammoCurrent, out double ammoMax); ammoLeft += "; " + ammoCurrent.ToString("0"); @@ -219,2343 +232,2839 @@ public Part GetPart() return ammoLeft; } - public string GetMissileType() - { - return string.Empty; - } + + public string GetMissileType() + { + return string.Empty; + } #if DEBUG - Vector3 relVelAdj; - Vector3 accAdj; - Vector3 gravAdj; + Vector3 relVelAdj; + Vector3 accAdj; + Vector3 gravAdj; #endif - #endregion Declarations + #endregion Declarations + + #region KSPFields + + [KSPField(isPersistant = true, guiActive = true, guiName = "#LOC_BDArmory_WeaponName", guiActiveEditor = true), UI_Label(affectSymCounterparts = UI_Scene.All, scene = UI_Scene.All)]//Weapon Name + public string WeaponName; + + [KSPField] + public string fireTransformName = "fireTransform"; + public Transform[] fireTransforms; + + [KSPField] + public string shellEjectTransformName = "shellEject"; + public Transform[] shellEjectTransforms; + + [KSPField] + public bool hasDeployAnim = false; + + [KSPField] + public string deployAnimName = "deployAnim"; + AnimationState deployState; + + [KSPField] + public bool hasFireAnimation = false; + + [KSPField] + public string fireAnimName = "fireAnim"; + private AnimationState fireState; + + [KSPField] + public bool spinDownAnimation = false; + private bool spinningDown; + + //weapon specifications + [KSPField] + public float maxTargetingRange = 5000; //max range for raycasting and sighting + + [KSPField(isPersistant = true, guiActive = false, guiActiveEditor = true, guiName = "Rate of Fire"), + UI_FloatRange(minValue = 100f, maxValue = 1500, stepIncrement = 25f, scene = UI_Scene.Editor, affectSymCounterparts = UI_Scene.All)] + public float roundsPerMinute; //rocket RoF slider + [KSPField] + public float maxDeviation = 1; //inaccuracy two standard deviations in degrees (two because backwards compatibility :) + + [KSPField] + public float maxEffectiveDistance = 2500; //used by AI to select appropriate weapon + + [KSPField] + public float bulletMass = 0.3880f; //mass in KG - used for damage and recoil and drag + + [KSPField] + public float caliber = 30; //caliber in mm, used for penetration calcs + + [KSPField] + public float bulletDmgMult = 1; //Used for heat damage modifier for non-explosive bullets + + [KSPField] + public float bulletVelocity = 1030; //velocity in meters/second + + [KSPField] + public float ECPerShot = 0; //EC to use per shot for weapons like railguns + + [KSPField] + public bool BeltFed = true; // + + [KSPField] + public int RoundsPerMag = 1; //For weapons fed from clips/mags. left at one as sanity check, incase this not set if !BeltFed + + public int RoundsRemaining = 0; + + public bool isReloading; + + [KSPField] + public bool crewserved = false; + + public bool hasGunner = true; + + [KSPField] + public float ReloadTime = 10; + + public float ReloadTimer = 0; + + [KSPField] + public bool BurstFire = false; + + [KSPField] + public string bulletDragTypeName = "AnalyticEstimate"; + public BulletDragTypes bulletDragType; + + //drag area of the bullet in m^2; equal to Cd * A with A being the frontal area of the bullet; as a first approximation, take Cd to be 0.3 + //bullet mass / bullet drag area. Used in analytic estimate to speed up code + [KSPField] + public float bulletDragArea = 1.209675e-5f; + + private BulletInfo bulletInfo; + + [KSPField] + public string bulletType = "def"; + + [KSPField] + public string ammoName = "50CalAmmo"; //resource usage + + [KSPField] + public float requestResourceAmount = 1; //amount of resource/ammo to deplete per shot + + [KSPField] + public float shellScale = 0.66f; //scale of shell to eject + + [KSPField] + public bool hasRecoil = true; + + [KSPField] + public float recoilReduction = 1; //for reducing recoil on large guns with built in compensation + + [KSPField(isPersistant = true, guiActive = false, guiActiveEditor = true, guiName = "#LOC_BDArmory_FireLimits"),//Fire Limits + UI_Toggle(disabledText = "#LOC_BDArmory_FireLimits_disabledText", enabledText = "#LOC_BDArmory_FireLimits_enabledText")]//None--In range + public bool onlyFireInRange = true; + //prevent firing when gun's turret is trying to exceed gimbal limits + + [KSPField] + public bool bulletDrop = true; //projectiles are affected by gravity + + [KSPField] + public string weaponType = "ballistic"; + //ballistic, rocket or laser + + //TODO: deprectated, moved to bullet config + [KSPField] + public float cannonShellRadius = 30; //max radius of explosion forces/damage + [KSPField] + public float cannonShellPower = 8; //explosion's impulse force + [KSPField] + public float cannonShellHeat = -1; //if non-negative, heat damage + + [KSPField] + public float laserDamage = 10000; //base damage/second of lasers + + //Rocket info; + + [KSPField(isPersistant = false)] public string rocketModelPath; + [KSPField(isPersistant = false)] public float rocketMass = 1; + [KSPField(isPersistant = false)] public float thrust = 1; + [KSPField(isPersistant = false)] public float thrustTime = 1; + [KSPField(isPersistant = false)] public float blastRadius = 1; + [KSPField(isPersistant = false)] public float blastForce = 1; + [KSPField] public float blastHeat = -1; + [KSPField(isPersistant = false)] public bool descendingOrder = true; + [KSPField] public float thrustDeviation = 0.10f; + [KSPField] public bool rocketPod = true; //is the RL a rocketpod, or a gyrojet gun? + [KSPField] public bool externalAmmo = false; //used for rocketlaunchers that are Gyrojet guns drawing from ammoboxes instead of internals + Transform[] rockets; + double rocketsMax; + + //projectile graphics + [KSPField] + public string projectileColor = "255, 130, 0, 255"; //final color of projectile + Color projectileColorC; + + [KSPField] + public bool fadeColor = false; + + [KSPField] + public string startColor = "255, 160, 0, 200"; + //if fade color is true, projectile starts at this color + + Color startColorC; + + [KSPField] + public float tracerStartWidth = 0.25f; + + [KSPField] + public float tracerEndWidth = 0.2f; + + [KSPField] + public float tracerLength = 0; + //if set to zero, tracer will be the length of the distance covered by the projectile in one physics timestep + + [KSPField] + public float tracerDeltaFactor = 2.65f; - #region KSPFields + [KSPField] + public float nonTracerWidth = 0.01f; - [KSPField(isPersistant = true, guiActive = true, guiName = "#LOC_BDArmory_WeaponName", guiActiveEditor = true), UI_Label(affectSymCounterparts = UI_Scene.All, scene = UI_Scene.All)]//Weapon Name - public string WeaponName; + [KSPField] + public int tracerInterval = 0; - [KSPField] - public string fireTransformName = "fireTransform"; - public Transform[] fireTransforms; + [KSPField] + public float tracerLuminance = 1.75f; + int tracerIntervalCounter; - [KSPField] - public string shellEjectTransformName = "shellEject"; - public Transform[] shellEjectTransforms; + [KSPField] + public string bulletTexturePath = "BDArmory/Textures/bullet"; - [KSPField] - public bool hasDeployAnim = false; + [KSPField] + public string laserTexturePath = "BDArmory/Textures/laser"; - [KSPField] - public string deployAnimName = "deployAnim"; - AnimationState deployState; + [KSPField] + public bool oneShotWorldParticles = false; - [KSPField] - public bool hasFireAnimation = false; + //heat + [KSPField] + public float maxHeat = 3600; - [KSPField] - public string fireAnimName = "fireAnim"; - private AnimationState fireState; + [KSPField] + public float heatPerShot = 75; - [KSPField] - public bool spinDownAnimation = false; - private bool spinningDown; + [KSPField] + public float heatLoss = 250; - //weapon specifications - [KSPField] - public float maxTargetingRange = 2000; //max range for raycasting and sighting + //canon explosion effects + [KSPField] + public string explModelPath = "BDArmory/Models/explosion/explosion"; - [KSPField] - public float roundsPerMinute = 850; //rate of fire + [KSPField] + public string explSoundPath = "BDArmory/Sounds/explode1"; - [KSPField] - public float maxDeviation = 1; //inaccuracy two standard deviations in degrees (two because backwards compatibility :) + //Used for scaling laser damage down based on distance. + [KSPField] + 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). - [KSPField] - public float maxEffectiveDistance = 2500; //used by AI to select appropriate weapon + //audioclip paths + [KSPField] + public string fireSoundPath = "BDArmory/Parts/50CalTurret/sounds/shot"; - [KSPField] - public float bulletMass = 0.3880f; //mass in KG - used for damage and recoil and drag + [KSPField] + public string overheatSoundPath = "BDArmory/Parts/50CalTurret/sounds/turretOverheat"; - [KSPField] - public float caliber = 30; //caliber in mm, used for penetration calcs + [KSPField] + public string chargeSoundPath = "BDArmory/Parts/laserTest/sounds/charge"; - [KSPField] - public float bulletDmgMult = 1; //Used for heat damage modifier for non-explosive bullets + [KSPField] + public string launchSoundPath = "BDArmory/Sounds/launch"; - [KSPField] - public float bulletVelocity = 1030; //velocity in meters/second + //audio + [KSPField] + public bool oneShotSound = true; + //play audioclip on every shot, instead of playing looping audio while firing - [KSPField] - public float ECPerShot = 0; //EC to use per shot for weapons like railguns + [KSPField] + public float soundRepeatTime = 1; + //looped audio will loop back to this time (used for not playing the opening bit, eg the ramp up in pitch of gatling guns) - [KSPField] - public string bulletDragTypeName = "AnalyticEstimate"; - public BulletDragTypes bulletDragType; + [KSPField] + public string reloadAudioPath = string.Empty; + AudioClip reloadAudioClip; - //drag area of the bullet in m^2; equal to Cd * A with A being the frontal area of the bullet; as a first approximation, take Cd to be 0.3 - //bullet mass / bullet drag area. Used in analytic estimate to speed up code - [KSPField] - public float bulletDragArea = 1.209675e-5f; + [KSPField] + public string reloadCompletePath = string.Empty; - private BulletInfo bulletInfo; + [KSPField] + public bool showReloadMeter = false; //used for cannons or guns with extremely low rate of fire - [KSPField] - public string bulletType = "def"; + //Air Detonating Rounds + [KSPField] + public bool airDetonation = false; - [KSPField] - public string ammoName = "50CalAmmo"; //resource usage + [KSPField] + public bool proximityDetonation = false; - [KSPField] - public float requestResourceAmount = 1; //amount of resource/ammo to deplete per shot + [KSPField(isPersistant = true, guiActive = true, guiName = "#LOC_BDArmory_DefaultDetonationRange", guiActiveEditor = false)]//Fuzed Detonation Range + public float defaultDetonationRange = 3500; // maxairDetrange works for altitude fuzing, use this for VT fuzing - [KSPField] - public float shellScale = 0.66f; //scale of shell to eject + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_ProximityFuzeRadius"), UI_FloatRange(minValue = 0f, maxValue = 100f, stepIncrement = 1f, scene = UI_Scene.Editor, affectSymCounterparts = UI_Scene.All)]//Proximity Fuze Radius + public float detonationRange = -1f; // give ability to set proximity range - [KSPField] - public bool hasRecoil = true; + [KSPField(isPersistant = true, guiActive = false, guiActiveEditor = true, guiName = "#LOC_BDArmory_MaxDetonationRange"),//Max Detonation Range + UI_FloatRange(minValue = 500, maxValue = 8000f, stepIncrement = 5f, scene = UI_Scene.All)] + public float maxAirDetonationRange = 3500; // could probably get rid of this entirely, max engagement range more or less already does this - [KSPField] - public float recoilReduction = 1; //for reducing recoil on large guns with built in compensation + [KSPField] + public bool airDetonationTiming = true; - [KSPField(isPersistant = true, guiActive = false, guiActiveEditor = true, guiName = "#LOC_BDArmory_FireLimits"),//Fire Limits - UI_Toggle(disabledText = "#LOC_BDArmory_FireLimits_disabledText", enabledText = "#LOC_BDArmory_FireLimits_enabledText")]//None--In range - public bool onlyFireInRange = true; - //prevent firing when gun's turret is trying to exceed gimbal limits + //auto proximity tracking + [KSPField] + public float autoProxyTrackRange = 0; + bool atprAcquired; + int aptrTicker; - [KSPField] - public bool bulletDrop = true; //projectiles are affected by gravity + float timeFired; + public float initialFireDelay = 0; //used to ripple fire multiple weapons of this type - [KSPField] - public string weaponType = "ballistic"; - //ballistic, cannon or laser + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_Barrage")]//Barrage + public bool + useRippleFire = true; - [KSPField] - public float laserDamage = 10000; //base damage/second of lasers + [KSPEvent(guiActive = false, guiActiveEditor = true, guiName = "#LOC_BDArmory_ToggleBarrage")]//Toggle Barrage + public void ToggleRipple() + { + List.Enumerator craftPart = EditorLogic.fetch.ship.parts.GetEnumerator(); + while (craftPart.MoveNext()) + { + if (craftPart.Current == null) continue; + //if (craftPart.Current.name != part.name) continue; // would this potentially result in a group of two different types of weapons only toggling ripple on one of them? + List.Enumerator weapon = craftPart.Current.FindModulesImplementing().GetEnumerator(); + while (weapon.MoveNext()) + { + if (weapon.Current == null) continue; + weapon.Current.useRippleFire = !weapon.Current.useRippleFire; + } + weapon.Dispose(); + } + craftPart.Dispose(); + } + + IEnumerator IncrementRippleIndex(float delay) + { + if (delay > 0) + { + yield return new WaitForSeconds(delay); + } + weaponManager.gunRippleIndex = weaponManager.gunRippleIndex + 1; + + //Debug.Log("incrementing ripple index to: " + weaponManager.gunRippleIndex); + } - //cannon shell specfications - //TODO: deprectated, moved to bullet config - [KSPField] - public float cannonShellRadius = 30; //max radius of explosion forces/damage + #endregion KSPFields - [KSPField] - public float cannonShellPower = 8; //explosion's impulse force + #region KSPActions - [KSPField] - public float cannonShellHeat = -1; //if non-negative, heat damage + [KSPAction("Toggle Weapon")] + public void AGToggle(KSPActionParam param) + { + Toggle(); + } - //projectile graphics - [KSPField] - public string projectileColor = "255, 130, 0, 255"; //final color of projectile - Color projectileColorC; + [KSPField(guiActive = true, guiActiveEditor = false, guiName = "#LOC_BDArmory_Status")]//Status + public string guiStatusString = + "Disabled"; + + //PartWindow buttons + [KSPEvent(guiActive = true, guiActiveEditor = false, guiName = "#LOC_BDArmory_Toggle")]//Toggle + public void Toggle() + { + if (weaponState == WeaponStates.Disabled || weaponState == WeaponStates.PoweringDown) + { + EnableWeapon(); + } + else + { + DisableWeapon(); + } + } - [KSPField] - public bool fadeColor = false; + bool agHoldFiring; - [KSPField] - public string startColor = "255, 160, 0, 200"; - //if fade color is true, projectile starts at this color + [KSPAction("Fire (Toggle)")] + public void AGFireToggle(KSPActionParam param) + { + agHoldFiring = (param.type == KSPActionType.Activate); + } - Color startColorC; + [KSPAction("Fire (Hold)")] + public void AGFireHold(KSPActionParam param) + { + StartCoroutine(FireHoldRoutine(param.group)); + } - [KSPField] - public float tracerStartWidth = 0.25f; + [KSPEvent(guiActive = true, guiName = "#LOC_BDArmory_Jettison", active = true, guiActiveEditor = false)]//Jettison + public void Jettison() // make rocketpods jettisonable + { + if ((turret || eWeaponType != WeaponTypes.Rocket) || (eWeaponType == WeaponTypes.Rocket && (!rocketPod || (rocketPod && externalAmmo)))) + { + return; + } + part.decouple(0); + if (BDArmorySetup.Instance.ActiveWeaponManager != null) + BDArmorySetup.Instance.ActiveWeaponManager.UpdateList(); + } - [KSPField] - public float tracerEndWidth = 0.2f; + IEnumerator FireHoldRoutine(KSPActionGroup group) + { + KeyBinding key = Misc.Misc.AGEnumToKeybinding(group); + if (key == null) + { + yield break; + } - [KSPField] - public float tracerLength = 0; - //if set to zero, tracer will be the length of the distance covered by the projectile in one physics timestep + while (key.GetKey()) + { + agHoldFiring = true; + yield return null; + } - [KSPField] - public float tracerDeltaFactor = 2.65f; + agHoldFiring = false; + yield break; + } - [KSPField] - public float nonTracerWidth = 0.01f; + #endregion KSPActions - [KSPField] - public int tracerInterval = 0; + #region KSP Events - [KSPField] - public float tracerLuminance = 1.75f; - int tracerIntervalCounter; + public override void OnAwake() + { + base.OnAwake(); - [KSPField] - public string bulletTexturePath = "BDArmory/Textures/bullet"; + part.stagingIconAlwaysShown = true; + this.part.stackIconGrouping = StackIconGrouping.SAME_TYPE; + } - [KSPField] - public bool oneShotWorldParticles = false; + public void Start() + { + part.stagingIconAlwaysShown = true; + this.part.stackIconGrouping = StackIconGrouping.SAME_TYPE; - //heat - [KSPField] - public float maxHeat = 3600; + Events["HideUI"].active = false; + Events["ShowUI"].active = true; - [KSPField] - public float heatPerShot = 75; + ParseWeaponType(); + // extension for feature_engagementenvelope + InitializeEngagementRange(0, maxEffectiveDistance); + if (string.IsNullOrEmpty(GetShortName())) + { + shortName = part.partInfo.title; + } + OriginalShortName = shortName; + WeaponName = shortName; + IEnumerator emitter = part.FindModelComponents().AsEnumerable().GetEnumerator(); + while (emitter.MoveNext()) + { + if (emitter.Current == null) continue; + emitter.Current.emit = false; + EffectBehaviour.AddParticleEmitter(emitter.Current); + } + emitter.Dispose(); - [KSPField] - public float heatLoss = 250; + if (roundsPerMinute >= 1500) + { + Events["ToggleRipple"].guiActiveEditor = false; + Fields["useRippleFire"].guiActiveEditor = false; + } - //canon explosion effects - [KSPField] - public string explModelPath = "BDArmory/Models/explosion/explosion"; + if (eWeaponType != WeaponTypes.Rocket)//disable rocket RoF slider for non rockets + { + Fields["roundsPerMinute"].guiActiveEditor = false; + } - [KSPField] - public string explSoundPath = "BDArmory/Sounds/explode1"; + vessel.Velocity(); + if (eWeaponType == WeaponTypes.Ballistic) + { + if (airDetonation) + { + UI_FloatRange detRange = (UI_FloatRange)Fields["maxAirDetonationRange"].uiControlEditor; + detRange.maxValue = maxEffectiveDistance; //altitude fuzing clamped to max range + } + else //disable fuze GUI elements on un-fuzed munitions + { + Fields["maxAirDetonationRange"].guiActive = false; + Fields["maxAirDetonationRange"].guiActiveEditor = false; + Fields["defaultDetonationRange"].guiActive = false; + Fields["defaultDetonationRange"].guiActiveEditor = false; + Fields["detonationRange"].guiActive = false; + Fields["detonationRange"].guiActiveEditor = false; + } + } + if (eWeaponType == WeaponTypes.Rocket) + { + if (!proximityDetonation) + { + Fields["maxAirDetonationRange"].guiActive = false; + Fields["maxAirDetonationRange"].guiActiveEditor = false; + Fields["defaultDetonationRange"].guiActive = false; + Fields["defaultDetonationRange"].guiActiveEditor = false; + Fields["detonationRange"].guiActive = false; + Fields["detonationRange"].guiActiveEditor = false; + } + if (rocketPod && externalAmmo) + { + BeltFed = false; + } + if (!rocketPod) + { + externalAmmo = true; + } + } + muzzleFlashEmitters = new List(); + IEnumerator mtf = part.FindModelTransforms("muzzleTransform").AsEnumerable().GetEnumerator(); + while (mtf.MoveNext()) + { + if (mtf.Current == null) continue; + KSPParticleEmitter kpe = mtf.Current.GetComponent(); + EffectBehaviour.AddParticleEmitter(kpe); + muzzleFlashEmitters.Add(kpe); + kpe.emit = false; + } + mtf.Dispose(); - //Used for scaling laser damage down based on distance. - [KSPField] - 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). + if (HighLogic.LoadedSceneIsFlight) + { + if (eWeaponType == WeaponTypes.Ballistic) + { + if (bulletPool == null) + { + SetupBulletPool(); + } + if (shellPool == null) + { + SetupShellPool(); + } + } + if (eWeaponType == WeaponTypes.Rocket && rocketPod)// only call these for rocket pods + { + MakeRocketArray(); + UpdateRocketScales(); + } + //setup transforms + fireTransforms = part.FindModelTransforms(fireTransformName); + shellEjectTransforms = part.FindModelTransforms(shellEjectTransformName); - //audioclip paths - [KSPField] - public string fireSoundPath = "BDArmory/Parts/50CalTurret/sounds/shot"; + //setup emitters + IEnumerator pe = part.FindModelComponents().AsEnumerable().GetEnumerator(); + while (pe.MoveNext()) + { + if (pe.Current == null) continue; + pe.Current.maxSize *= part.rescaleFactor; + pe.Current.minSize *= part.rescaleFactor; + pe.Current.shape3D *= part.rescaleFactor; + pe.Current.shape2D *= part.rescaleFactor; + pe.Current.shape1D *= part.rescaleFactor; + + if (pe.Current.useWorldSpace && !oneShotWorldParticles) + { + BDAGaplessParticleEmitter gpe = pe.Current.gameObject.AddComponent(); + gpe.part = part; + gaplessEmitters.Add(gpe); + } + else + { + EffectBehaviour.AddParticleEmitter(pe.Current); + } + } + pe.Dispose(); + + //setup projectile colors + projectileColorC = Misc.Misc.ParseColor255(projectileColor); + startColorC = Misc.Misc.ParseColor255(startColor); + + //init and zero points + targetPosition = Vector3.zero; + pointingAtPosition = Vector3.zero; + bulletPrediction = Vector3.zero; + + //setup audio + SetupAudio(); + + // Setup gauges + gauge = (BDStagingAreaGauge)part.AddModule("BDStagingAreaGauge"); + gauge.AmmoName = ammoName; + gauge.AudioSource = audioSource; + gauge.ReloadAudioClip = reloadAudioClip; + gauge.ReloadCompleteAudioClip = reloadCompleteAudioClip; + + //if ((eWeaponType != WeaponTypes.Rocket) || (eWeaponType == WeaponTypes.Rocket && externalAmmo)) + //{// start ammo tracking for non rocketpods + AmmoID = PartResourceLibrary.Instance.GetDefinition(ammoName).id; + //} + //laser setup + if (eWeaponType == WeaponTypes.Laser) + { + SetupLaserSpecifics(); + if (maxTargetingRange < maxEffectiveDistance) + { + maxEffectiveDistance = maxTargetingRange; + } + } - [KSPField] - public string overheatSoundPath = "BDArmory/Parts/50CalTurret/sounds/turretOverheat"; + if (crewserved) + { + KerbalSeat KS = part.Modules.OfType().First(); + if (KS.Occupant == null) + { + hasGunner = false; + } + else + { + hasGunner = true; + } + } + } + else if (HighLogic.LoadedSceneIsEditor) + { + fireTransforms = part.FindModelTransforms(fireTransformName); + WeaponNameWindow.OnActionGroupEditorOpened.Add(OnActionGroupEditorOpened); + WeaponNameWindow.OnActionGroupEditorClosed.Add(OnActionGroupEditorClosed); + } + //turret setup + List.Enumerator turr = part.FindModulesImplementing().GetEnumerator(); + while (turr.MoveNext()) + { + if (turr.Current == null) continue; + if (turr.Current.turretID != turretID) continue; + turret = turr.Current; + turret.SetReferenceTransform(fireTransforms[0]); + break; + } + turr.Dispose(); - [KSPField] - public string chargeSoundPath = "BDArmory/Parts/laserTest/sounds/charge"; + if (!turret) + { + Fields["onlyFireInRange"].guiActive = false; + Fields["onlyFireInRange"].guiActiveEditor = false; + } + if (HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight) + { + if ((turret || eWeaponType != WeaponTypes.Rocket) || (eWeaponType == WeaponTypes.Rocket && (!rocketPod || (rocketPod && externalAmmo)))) + { + Events["Jettison"].guiActive = false; + } + } + //setup animations + //if (!string.IsNullOrEmpty(deployAnimName) && eWeaponType == WeaponTypes.Rocket) // hack to grab the hasAnim bool for legacy RLs converted via MM + //{ + // hasDeployAnim = true; + //} + if (hasDeployAnim) + { + deployState = Misc.Misc.SetUpSingleAnimation(deployAnimName, part); + deployState.normalizedTime = 0; + deployState.speed = 0; + deployState.enabled = true; + } + if (hasFireAnimation) + { + fireState = Misc.Misc.SetUpSingleAnimation(fireAnimName, part); + fireState.enabled = false; + } - //audio - [KSPField] - public bool oneShotSound = true; - //play audioclip on every shot, instead of playing looping audio while firing - - [KSPField] - public float soundRepeatTime = 1; - //looped audio will loop back to this time (used for not playing the opening bit, eg the ramp up in pitch of gatling guns) - - [KSPField] - public string reloadAudioPath = string.Empty; - AudioClip reloadAudioClip; - - [KSPField] - public string reloadCompletePath = string.Empty; - - [KSPField] - public bool showReloadMeter = false; //used for cannons or guns with extremely low rate of fire - - //Air Detonating Rounds - [KSPField] - public bool airDetonation = false; - - [KSPField] - public bool proximityDetonation = false; - - [KSPField(isPersistant = true, guiActive = true, guiName = "#LOC_BDArmory_DefaultDetonationRange", guiActiveEditor = false)]//Fuzed Detonation Range - public float defaultDetonationRange = 3500; // maxairDetrange works for altitude fuzing, use this for VT fuzing - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_ProximityFuzeRadius"), UI_FloatRange(minValue = 0f, maxValue = 100f, stepIncrement = 1f, scene = UI_Scene.Editor, affectSymCounterparts = UI_Scene.All)]//Proximity Fuze Radius - public float detonationRange = -1f; // give ability to set proximity range - - [KSPField(isPersistant = true, guiActive = false, guiActiveEditor = true, guiName = "#LOC_BDArmory_MaxDetonationRange"),//Max Detonation Range - UI_FloatRange(minValue = 500, maxValue = 8000f, stepIncrement = 5f, scene = UI_Scene.All)] - public float maxAirDetonationRange = 3500; // could probably get rid of this entirely, max engagement range more or less already does this - - [KSPField] - public bool airDetonationTiming = true; - - //auto proximity tracking - [KSPField] - public float autoProxyTrackRange = 0; - bool atprAcquired; - int aptrTicker; - - float timeFired; - public float initialFireDelay = 0; //used to ripple fire multiple weapons of this type - - [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "#LOC_BDArmory_Barrage")]//Barrage - public bool - useRippleFire = true; - - [KSPEvent(guiActive = false, guiActiveEditor = true, guiName = "#LOC_BDArmory_ToggleBarrage")]//Toggle Barrage - public void ToggleRipple() - { - List.Enumerator craftPart = EditorLogic.fetch.ship.parts.GetEnumerator(); - while (craftPart.MoveNext()) - { - if (craftPart.Current == null) continue; - if (craftPart.Current.name != part.name) continue; - List.Enumerator weapon = craftPart.Current.FindModulesImplementing().GetEnumerator(); - while (weapon.MoveNext()) - { - if (weapon.Current == null) continue; - weapon.Current.useRippleFire = !weapon.Current.useRippleFire; - } - weapon.Dispose(); - } - craftPart.Dispose(); - } - - IEnumerator IncrementRippleIndex(float delay) - { - if (delay > 0) - { - yield return new WaitForSeconds(delay); - } - weaponManager.gunRippleIndex = weaponManager.gunRippleIndex + 1; - - //Debug.Log("incrementing ripple index to: " + weaponManager.gunRippleIndex); - } - - #endregion KSPFields - - #region KSPActions - - [KSPAction("Toggle Weapon")] - public void AGToggle(KSPActionParam param) - { - Toggle(); - } - - [KSPField(guiActive = true, guiActiveEditor = false, guiName = "#LOC_BDArmory_Status")]//Status - public string guiStatusString = - "Disabled"; - - //PartWindow buttons - [KSPEvent(guiActive = true, guiActiveEditor = false, guiName = "#LOC_BDArmory_Toggle")]//Toggle - public void Toggle() - { - if (weaponState == WeaponStates.Disabled || weaponState == WeaponStates.PoweringDown) - { - EnableWeapon(); - } - else - { - DisableWeapon(); - } - } - - bool agHoldFiring; - - [KSPAction("Fire (Toggle)")] - public void AGFireToggle(KSPActionParam param) - { - agHoldFiring = (param.type == KSPActionType.Activate); - } - - [KSPAction("Fire (Hold)")] - public void AGFireHold(KSPActionParam param) - { - StartCoroutine(FireHoldRoutine(param.group)); - } - - IEnumerator FireHoldRoutine(KSPActionGroup group) - { - KeyBinding key = Misc.Misc.AGEnumToKeybinding(group); - if (key == null) - { - yield break; - } - - while (key.GetKey()) - { - agHoldFiring = true; - yield return null; - } - - agHoldFiring = false; - yield break; - } - - #endregion KSPActions - - #region KSP Events - - public override void OnAwake() - { - base.OnAwake(); - - part.stagingIconAlwaysShown = true; - this.part.stackIconGrouping = StackIconGrouping.SAME_TYPE; - } - - public void Start() - { - part.stagingIconAlwaysShown = true; - this.part.stackIconGrouping = StackIconGrouping.SAME_TYPE; - - Events["HideUI"].active = false; - Events["ShowUI"].active = true; - ParseWeaponType(); - - // extension for feature_engagementenvelope - InitializeEngagementRange(0, maxEffectiveDistance); - if (string.IsNullOrEmpty(GetShortName())) - { - shortName = part.partInfo.title; - } - OriginalShortName = shortName; - WeaponName = shortName; - IEnumerator emitter = part.FindModelComponents().AsEnumerable().GetEnumerator(); - while (emitter.MoveNext()) - { - if (emitter.Current == null) continue; - emitter.Current.emit = false; - EffectBehaviour.AddParticleEmitter(emitter.Current); - } - emitter.Dispose(); - - if (roundsPerMinute >= 1500) - { - Events["ToggleRipple"].guiActiveEditor = false; - Fields["useRippleFire"].guiActiveEditor = false; - } - vessel.Velocity(); - if (airDetonation) - { - UI_FloatRange detRange = (UI_FloatRange)Fields["maxAirDetonationRange"].uiControlEditor; - detRange.maxValue = maxEffectiveDistance; //altitude fuzing clamped to max range - } - else //disable fuze GUI elements on un-fuzed munitions - { - Fields["maxAirDetonationRange"].guiActive = false; - Fields["maxAirDetonationRange"].guiActiveEditor = false; - Fields["defaultDetonationRange"].guiActive = false; - Fields["defaultDetonationRange"].guiActiveEditor = false; - Fields["detonationRange"].guiActive = false; - Fields["detonationRange"].guiActiveEditor = false; - } - muzzleFlashEmitters = new List(); - IEnumerator mtf = part.FindModelTransforms("muzzleTransform").AsEnumerable().GetEnumerator(); - while (mtf.MoveNext()) - { - if (mtf.Current == null) continue; - KSPParticleEmitter kpe = mtf.Current.GetComponent(); - EffectBehaviour.AddParticleEmitter(kpe); - muzzleFlashEmitters.Add(kpe); - kpe.emit = false; - } - mtf.Dispose(); - - if (HighLogic.LoadedSceneIsFlight) - { - if (eWeaponType != WeaponTypes.Laser) - { - if (bulletPool == null) - { - SetupBulletPool(); - } - if (shellPool == null) - { - SetupShellPool(); - } - } - - //setup transforms - fireTransforms = part.FindModelTransforms(fireTransformName); - shellEjectTransforms = part.FindModelTransforms(shellEjectTransformName); - - //setup emitters - IEnumerator pe = part.FindModelComponents().AsEnumerable().GetEnumerator(); - while (pe.MoveNext()) - { - if (pe.Current == null) continue; - pe.Current.maxSize *= part.rescaleFactor; - pe.Current.minSize *= part.rescaleFactor; - pe.Current.shape3D *= part.rescaleFactor; - pe.Current.shape2D *= part.rescaleFactor; - pe.Current.shape1D *= part.rescaleFactor; - - if (pe.Current.useWorldSpace && !oneShotWorldParticles) - { - BDAGaplessParticleEmitter gpe = pe.Current.gameObject.AddComponent(); - gpe.part = part; - gaplessEmitters.Add(gpe); - } - else - { - EffectBehaviour.AddParticleEmitter(pe.Current); - } - } - pe.Dispose(); - - //setup projectile colors - projectileColorC = Misc.Misc.ParseColor255(projectileColor); - startColorC = Misc.Misc.ParseColor255(startColor); - - //init and zero points - targetPosition = Vector3.zero; - pointingAtPosition = Vector3.zero; - bulletPrediction = Vector3.zero; - - //setup audio - SetupAudio(); - - // Setup gauges - gauge = (BDStagingAreaGauge)part.AddModule("BDStagingAreaGauge"); - gauge.AmmoName = ammoName; - gauge.AudioSource = audioSource; - gauge.ReloadAudioClip = reloadAudioClip; - gauge.ReloadCompleteAudioClip = reloadCompleteAudioClip; - - AmmoID = PartResourceLibrary.Instance.GetDefinition(ammoName).id; - - //laser setup - if (eWeaponType == WeaponTypes.Laser) - { - SetupLaserSpecifics(); - if (maxTargetingRange < maxEffectiveDistance) - { - maxEffectiveDistance = maxTargetingRange; - } - } - } - else if (HighLogic.LoadedSceneIsEditor) - { - fireTransforms = part.FindModelTransforms(fireTransformName); - WeaponNameWindow.OnActionGroupEditorOpened.Add(OnActionGroupEditorOpened); - WeaponNameWindow.OnActionGroupEditorClosed.Add(OnActionGroupEditorClosed); - } - //turret setup - List.Enumerator turr = part.FindModulesImplementing().GetEnumerator(); - while (turr.MoveNext()) - { - if (turr.Current == null) continue; - if (turr.Current.turretID != turretID) continue; - turret = turr.Current; - turret.SetReferenceTransform(fireTransforms[0]); - break; - } - turr.Dispose(); - - if (!turret) - { - Fields["onlyFireInRange"].guiActive = false; - Fields["onlyFireInRange"].guiActiveEditor = false; - } - - //setup animations - if (hasDeployAnim) - { - deployState = Misc.Misc.SetUpSingleAnimation(deployAnimName, part); - deployState.normalizedTime = 0; - deployState.speed = 0; - deployState.enabled = true; - } - if (hasFireAnimation) - { - fireState = Misc.Misc.SetUpSingleAnimation(fireAnimName, part); - fireState.enabled = false; - } - - SetupBullet(); + SetupBullet(); SetInitialDetonationDistance(); - if (bulletInfo == null) - { - if (BDArmorySettings.DRAW_DEBUG_LABELS) - Debug.Log("[BDArmory]: Failed To load bullet : " + bulletType); - } - else - { - if (BDArmorySettings.DRAW_DEBUG_LABELS) - Debug.Log("[BDArmory]: BulletType Loaded : " + bulletType); - } - - BDArmorySetup.OnVolumeChange += UpdateVolume; - } - - void OnDestroy() - { - BDArmorySetup.OnVolumeChange -= UpdateVolume; - WeaponNameWindow.OnActionGroupEditorOpened.Remove(OnActionGroupEditorOpened); - WeaponNameWindow.OnActionGroupEditorClosed.Remove(OnActionGroupEditorClosed); - } - - void Update() - { - if (HighLogic.LoadedSceneIsFlight && FlightGlobals.ready && !vessel.packed && vessel.IsControllable) - { - if (lowpassFilter) - { - if (InternalCamera.Instance && InternalCamera.Instance.isActive) - { - lowpassFilter.enabled = true; - } - else - { - lowpassFilter.enabled = false; - } - } - - if (weaponState == WeaponStates.Enabled && - (TimeWarp.WarpMode != TimeWarp.Modes.HIGH || TimeWarp.CurrentRate == 1)) - { - userFiring = (BDInputUtils.GetKey(BDInputSettingsFields.WEAP_FIRE_KEY) && - (vessel.isActiveVessel || BDArmorySettings.REMOTE_SHOOTING) && !MapView.MapIsEnabled && - !aiControlled); - if ((userFiring || autoFire || agHoldFiring) && - (yawRange == 0 || (maxPitch - minPitch) == 0 || - turret.TargetInRange(finalAimTarget, 10, float.MaxValue))) - { - if (useRippleFire && (pointingAtSelf || isOverheated)) - { - StartCoroutine(IncrementRippleIndex(0)); - finalFire = false; - } - else if (eWeaponType == WeaponTypes.Ballistic || eWeaponType == WeaponTypes.Cannon) //WeaponTypes.Cannon is deprecated - { - finalFire = true; - } - } - else - { - if (spinDownAnimation) spinningDown = true; - if (eWeaponType == WeaponTypes.Laser) audioSource.Stop(); - if (!oneShotSound && wasFiring) - { - audioSource.Stop(); - wasFiring = false; - audioSource2.PlayOneShot(overheatSound); - } - } - } - else - { - audioSource.Stop(); - autoFire = false; - } - - if (spinningDown && spinDownAnimation && hasFireAnimation) - { - if (fireState.normalizedTime > 1) fireState.normalizedTime = 0; - fireState.speed = fireAnimSpeed; - fireAnimSpeed = Mathf.Lerp(fireAnimSpeed, 0, 0.04f); - } - - // Draw gauges - if (vessel.isActiveVessel) - { - vessel.GetConnectedResourceTotals(AmmoID, out double ammoCurrent, out double ammoMax); - gauge.UpdateAmmoMeter((float)(ammoCurrent / ammoMax)); - - if (showReloadMeter) - { - gauge.UpdateReloadMeter((Time.time - timeFired) * roundsPerMinute / 60); - } - else - { - gauge.UpdateHeatMeter(heat / maxHeat); - } - } - } - } - - void FixedUpdate() - { - if (HighLogic.LoadedSceneIsFlight && !vessel.packed) - { - if (!vessel.IsControllable) - { - if (weaponState != WeaponStates.PoweringDown || weaponState != WeaponStates.Disabled) - { - DisableWeapon(); - } - return; - } - - UpdateHeat(); - if (weaponState == WeaponStates.Enabled && - (TimeWarp.WarpMode != TimeWarp.Modes.HIGH || TimeWarp.CurrentRate == 1)) - { - //Aim(); - StartCoroutine(AimAndFireAtEndOfFrame()); - - if (eWeaponType == WeaponTypes.Laser) - { - if ((userFiring || autoFire || agHoldFiring) && - (!turret || turret.TargetInRange(targetPosition, 10, float.MaxValue))) - { - finalFire = true; - } - else - { - for (int i = 0; i < laserRenderers.Length; i++) - { - laserRenderers[i].enabled = false; - } - audioSource.Stop(); - } - } - } - else if (eWeaponType == WeaponTypes.Laser) - { - for (int i = 0; i < laserRenderers.Length; i++) - { - laserRenderers[i].enabled = false; - } - audioSource.Stop(); - } - } - lastFinalAimTarget = finalAimTarget; - } - - private void UpdateMenus(bool visible) - { - Events["HideUI"].active = visible; - Events["ShowUI"].active = !visible; - } - - private void OnActionGroupEditorOpened() - { - Events["HideUI"].active = false; - Events["ShowUI"].active = false; - } - - private void OnActionGroupEditorClosed() - { - Events["HideUI"].active = false; - Events["ShowUI"].active = true; - } - - [KSPEvent(guiActiveEditor = true, guiName = "#LOC_BDArmory_HideWeaponGroupUI", active = false)]//Hide Weapon Group UI - public void HideUI() - { - WeaponGroupWindow.HideGUI(); - UpdateMenus(false); - } - - [KSPEvent(guiActiveEditor = true, guiName = "#LOC_BDArmory_SetWeaponGroupUI", active = false)]//Set Weapon Group UI - public void ShowUI() - { - WeaponGroupWindow.ShowGUI(this); - UpdateMenus(true); - } - - void OnGUI() - { - if (weaponState == WeaponStates.Enabled && vessel && !vessel.packed && vessel.isActiveVessel && - BDArmorySettings.DRAW_AIMERS && !aiControlled && !MapView.MapIsEnabled && !pointingAtSelf) - { - float size = 30; - - Vector3 reticlePosition; - if (BDArmorySettings.AIM_ASSIST) - { - if (targetAcquired && (slaved || yawRange < 1 || maxPitch - minPitch < 1)) - { - reticlePosition = pointingAtPosition + fixedLeadOffset; - - if (!slaved) - { - BDGUIUtils.DrawLineBetweenWorldPositions(pointingAtPosition, reticlePosition, 2, - new Color(0, 1, 0, 0.6f)); - } - - BDGUIUtils.DrawTextureOnWorldPos(pointingAtPosition, BDArmorySetup.Instance.greenDotTexture, - new Vector2(6, 6), 0); - - if (atprAcquired) - { - BDGUIUtils.DrawTextureOnWorldPos(targetPosition, BDArmorySetup.Instance.openGreenSquare, - new Vector2(20, 20), 0); - } - } - else - { - reticlePosition = bulletPrediction; - } - } - else - { - reticlePosition = pointingAtPosition; - } - - Texture2D texture; - if (Vector3.Angle(pointingAtPosition - transform.position, finalAimTarget - transform.position) < 1f) - { - texture = BDArmorySetup.Instance.greenSpikedPointCircleTexture; - } - else - { - texture = BDArmorySetup.Instance.greenPointCircleTexture; - } - BDGUIUtils.DrawTextureOnWorldPos(reticlePosition, texture, new Vector2(size, size), 0); - - if (BDArmorySettings.DRAW_DEBUG_LINES) - { - if (targetAcquired) - { - BDGUIUtils.DrawLineBetweenWorldPositions(fireTransforms[0].position, targetPosition, 2, - Color.blue); - } - } - } - - if (HighLogic.LoadedSceneIsEditor && BDArmorySetup.showWeaponAlignment) - { - DrawAlignmentIndicator(); - } + if (bulletInfo == null) + { + if (BDArmorySettings.DRAW_DEBUG_LABELS) + Debug.Log("[BDArmory]: Failed To load bullet : " + bulletType); + } + else + { + if (BDArmorySettings.DRAW_DEBUG_LABELS) + Debug.Log("[BDArmory]: BulletType Loaded : " + bulletType); + } -#if DEBUG - if (BDArmorySettings.DRAW_DEBUG_LINES && weaponState == WeaponStates.Enabled && vessel && !vessel.packed && !MapView.MapIsEnabled) - { - BDGUIUtils.MarkPosition(targetPosition, transform, Color.cyan); - BDGUIUtils.DrawLineBetweenWorldPositions(targetPosition, targetPosition + relVelAdj, 2, Color.green); - BDGUIUtils.DrawLineBetweenWorldPositions(targetPosition + relVelAdj, targetPosition + relVelAdj + accAdj, 2, Color.magenta); - BDGUIUtils.DrawLineBetweenWorldPositions(targetPosition + relVelAdj + accAdj, targetPosition + relVelAdj + accAdj + gravAdj, 2, Color.yellow); - BDGUIUtils.MarkPosition(finalAimTarget, transform, Color.cyan, size: 4); - } -#endif - } - - #endregion KSP Events - - #region Fire - - private void Fire() - { - if (BDArmorySetup.GameIsPaused) - { - if (audioSource.isPlaying) - { - audioSource.Stop(); - } - return; - } - - float timeGap = (60 / roundsPerMinute) * TimeWarp.CurrentRate; - if (Time.time - timeFired > timeGap - && !isOverheated - && !pointingAtSelf - && (aiControlled || !Misc.Misc.CheckMouseIsOnGui()) - && WMgrAuthorized()) - { - bool effectsShot = false; - //Transform[] fireTransforms = part.FindModelTransforms("fireTransform"); - for (float iTime = Mathf.Min(Time.time - timeFired - timeGap, TimeWarp.fixedDeltaTime); iTime >= 0; iTime -= timeGap) - for (int i = 0; i < fireTransforms.Length; i++) - { - //if ((BDArmorySettings.INFINITE_AMMO || part.RequestResource(ammoName, requestResourceAmount) > 0)) - if (CanFire()) - { - Transform fireTransform = fireTransforms[i]; - spinningDown = false; - - //recoil - if (hasRecoil) - { - part.rb.AddForceAtPosition((-fireTransform.forward) * (bulletVelocity * bulletMass / 1000 * BDArmorySettings.RECOIL_FACTOR * recoilReduction), - fireTransform.position, ForceMode.Impulse); - } - - if (!effectsShot) - { - //sound - if (oneShotSound) - { - audioSource.Stop(); - audioSource.PlayOneShot(fireSound); - } - else - { - wasFiring = true; - if (!audioSource.isPlaying) - { - audioSource.clip = fireSound; - audioSource.loop = false; - audioSource.time = 0; - audioSource.Play(); - } - else - { - if (audioSource.time >= fireSound.length) - { - audioSource.time = soundRepeatTime; - } - } - } - - //animation - if (hasFireAnimation) - { - float unclampedSpeed = (roundsPerMinute * fireState.length) / 60f; - float lowFramerateFix = 1; - if (roundsPerMinute > 500f) - { - lowFramerateFix = (0.02f / Time.deltaTime); - } - fireAnimSpeed = Mathf.Clamp(unclampedSpeed, 1f * lowFramerateFix, 20f * lowFramerateFix); - fireState.enabled = true; - if (unclampedSpeed == fireAnimSpeed || fireState.normalizedTime > 1) - { - fireState.normalizedTime = 0; - } - fireState.speed = fireAnimSpeed; - fireState.normalizedTime = Mathf.Repeat(fireState.normalizedTime, 1); - - //Debug.Log("fireAnim time: " + fireState.normalizedTime + ", speed; " + fireState.speed); - } - - //muzzle flash - List.Enumerator pEmitter = muzzleFlashEmitters.GetEnumerator(); - while (pEmitter.MoveNext()) - { - if (pEmitter.Current == null) continue; - //KSPParticleEmitter pEmitter = mtf.gameObject.GetComponent(); - if (pEmitter.Current.useWorldSpace && !oneShotWorldParticles) continue; - if (pEmitter.Current.maxEnergy < 0.5f) - { - float twoFrameTime = Mathf.Clamp(Time.deltaTime * 2f, 0.02f, 0.499f); - pEmitter.Current.maxEnergy = twoFrameTime; - pEmitter.Current.minEnergy = twoFrameTime / 3f; - } - pEmitter.Current.Emit(); - } - pEmitter.Dispose(); - - List.Enumerator gpe = gaplessEmitters.GetEnumerator(); - while (gpe.MoveNext()) - { - if (gpe.Current == null) continue; - gpe.Current.EmitParticles(); - } - gpe.Dispose(); - - //shell ejection - if (BDArmorySettings.EJECT_SHELLS) - { - IEnumerator sTf = shellEjectTransforms.AsEnumerable().GetEnumerator(); - while (sTf.MoveNext()) - { - if (sTf.Current == null) continue; - GameObject ejectedShell = shellPool.GetPooledObject(); - ejectedShell.transform.position = sTf.Current.position; - //+(part.rb.velocity*TimeWarp.fixedDeltaTime); - ejectedShell.transform.rotation = sTf.Current.rotation; - ejectedShell.transform.localScale = Vector3.one * shellScale; - ShellCasing shellComponent = ejectedShell.GetComponent(); - shellComponent.initialV = part.rb.velocity; - ejectedShell.SetActive(true); - } - sTf.Dispose(); - } - effectsShot = true; - } - - //firing bullet - GameObject firedBullet = bulletPool.GetPooledObject(); - PooledBullet pBullet = firedBullet.GetComponent(); - - firedBullet.transform.position = fireTransform.position; - - pBullet.caliber = bulletInfo.caliber; - pBullet.bulletVelocity = bulletInfo.bulletVelocity; - pBullet.bulletMass = bulletInfo.bulletMass; - pBullet.explosive = bulletInfo.explosive; - pBullet.apBulletMod = bulletInfo.apBulletMod; - pBullet.bulletDmgMult = bulletDmgMult; - - //A = π x (Ø / 2)^2 - bulletDragArea = Mathf.PI * Mathf.Pow(caliber / 2f, 2f); - - //Bc = m/Cd * A - bulletBallisticCoefficient = bulletMass / ((bulletDragArea / 1000000f) * 0.295f); // mm^2 to m^2 - - //Bc = m/d^2 * i where i = 0.484 - //bulletBallisticCoefficient = bulletMass / Mathf.Pow(caliber / 1000, 2f) * 0.484f; - - pBullet.ballisticCoefficient = bulletBallisticCoefficient; - - pBullet.flightTimeElapsed = iTime; - // measure bullet lifetime in time rather than in distance, because distances get very relative in orbit - pBullet.timeToLiveUntil = Mathf.Max(maxTargetingRange, maxEffectiveDistance) / bulletVelocity * 1.1f + Time.time; - - timeFired = Time.time - iTime; - - Vector3 firedVelocity = - VectorUtils.GaussianDirectionDeviation(fireTransform.forward, maxDeviation / 4) * bulletVelocity; - - pBullet.currentVelocity = (part.rb.velocity + Krakensbane.GetFrameVelocityV3f()) + firedVelocity; // use the real velocity, w/o offloading - firedBullet.transform.position += (part.rb.velocity + Krakensbane.GetFrameVelocityV3f()) * Time.fixedDeltaTime - + pBullet.currentVelocity * iTime; - - pBullet.sourceVessel = vessel; - pBullet.bulletTexturePath = bulletTexturePath; - pBullet.projectileColor = projectileColorC; - pBullet.startColor = startColorC; - pBullet.fadeColor = fadeColor; - tracerIntervalCounter++; - if (tracerIntervalCounter > tracerInterval) - { - tracerIntervalCounter = 0; - pBullet.tracerStartWidth = tracerStartWidth; - pBullet.tracerEndWidth = tracerEndWidth; - } - else - { - pBullet.tracerStartWidth = nonTracerWidth; - pBullet.tracerEndWidth = nonTracerWidth; - pBullet.startColor.a *= 0.5f; - pBullet.projectileColor.a *= 0.5f; - } - pBullet.tracerLength = tracerLength; - pBullet.tracerDeltaFactor = tracerDeltaFactor; - pBullet.tracerLuminance = tracerLuminance; - pBullet.bulletDrop = bulletDrop; - - if ((eWeaponType == WeaponTypes.Ballistic && bulletInfo.explosive) || eWeaponType == WeaponTypes.Cannon) //WeaponTypes.Cannon is deprecated - { - if (bulletType == "def") - { - //legacy model, per weapon config - pBullet.bulletType = PooledBullet.PooledBulletTypes.Explosive; - pBullet.explModelPath = explModelPath; - pBullet.explSoundPath = explSoundPath; - pBullet.blastPower = cannonShellPower; - pBullet.blastHeat = cannonShellHeat; - pBullet.radius = cannonShellRadius; - pBullet.airDetonation = airDetonation; - pBullet.detonationRange = detonationRange; - pBullet.maxAirDetonationRange = maxAirDetonationRange; - pBullet.defaultDetonationRange = defaultDetonationRange; - pBullet.proximityDetonation = proximityDetonation; - } - else - { - //use values from bullets.cfg - pBullet.bulletType = PooledBullet.PooledBulletTypes.Explosive; - pBullet.explModelPath = explModelPath; - pBullet.explSoundPath = explSoundPath; - - pBullet.tntMass = bulletInfo.tntMass; - pBullet.blastPower = bulletInfo.blastPower; - pBullet.blastHeat = bulletInfo.blastHeat; - pBullet.radius = bulletInfo.blastRadius; - - pBullet.airDetonation = airDetonation; - pBullet.detonationRange = detonationRange; - pBullet.maxAirDetonationRange = maxAirDetonationRange; - pBullet.defaultDetonationRange = defaultDetonationRange; - pBullet.proximityDetonation = proximityDetonation; - } - } - else - { - pBullet.bulletType = PooledBullet.PooledBulletTypes.Standard; - pBullet.airDetonation = false; - } - switch (bulletDragType) - { - case BulletDragTypes.None: - pBullet.dragType = PooledBullet.BulletDragTypes.None; - break; - - case BulletDragTypes.AnalyticEstimate: - pBullet.dragType = PooledBullet.BulletDragTypes.AnalyticEstimate; - break; - - case BulletDragTypes.NumericalIntegration: - pBullet.dragType = PooledBullet.BulletDragTypes.NumericalIntegration; - break; - } - - pBullet.bullet = BulletInfo.bullets[bulletType]; - pBullet.gameObject.SetActive(true); - - //heat - heat += heatPerShot; - //EC - DrainECPerShot(); - } - else - { - spinningDown = true; - if (!oneShotSound && wasFiring) - { - audioSource.Stop(); - wasFiring = false; - audioSource2.PlayOneShot(overheatSound); - } - } - } - - if (useRippleFire) - { - StartCoroutine(IncrementRippleIndex(initialFireDelay * TimeWarp.CurrentRate)); - } - } - else - { - spinningDown = true; - } - } - - private bool FireLaser() - { - float chargeAmount = requestResourceAmount * TimeWarp.fixedDeltaTime; - - if (!pointingAtSelf && !Misc.Misc.CheckMouseIsOnGui() && WMgrAuthorized() && !isOverheated && - (part.RequestResource(ammoName.GetHashCode(), (double)chargeAmount) >= chargeAmount || BDArmorySettings.INFINITE_AMMO)) - { - if (!audioSource.isPlaying) - { - audioSource.PlayOneShot(chargeSound); - audioSource.Play(); - audioSource.loop = true; - } - for (int i = 0; i < fireTransforms.Length; i++) - { - Transform tf = fireTransforms[i]; - - LineRenderer lr = laserRenderers[i]; - - Vector3 rayDirection = tf.forward; - - Vector3 targetDirection = Vector3.zero; //autoTrack enhancer - Vector3 targetDirectionLR = tf.forward; - - if (((visualTargetVessel != null && visualTargetVessel.loaded) || slaved) - && Vector3.Angle(rayDirection, targetDirection) < 1) - { - targetDirection = targetPosition - tf.position; - rayDirection = targetDirection; - targetDirectionLR = targetDirection.normalized; - } - - Ray ray = new Ray(tf.position, rayDirection); - lr.useWorldSpace = false; - lr.SetPosition(0, Vector3.zero); - RaycastHit hit; - - if (Physics.Raycast(ray, out hit, maxTargetingRange, 9076737)) - { - lr.useWorldSpace = true; - laserPoint = hit.point + targetVelocity * Time.fixedDeltaTime; - - lr.SetPosition(0, tf.position + (part.rb.velocity * Time.fixedDeltaTime)); - lr.SetPosition(1, laserPoint); - - KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards(); - Part p = eva ? eva.part : hit.collider.gameObject.GetComponentInParent(); - - if (p && p.vessel && p.vessel != vessel) - { - float distance = 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. - p.AddDamage(laserDamage / (1 + Mathf.PI * Mathf.Pow(tanAngle * distance, 2)) * - TimeWarp.fixedDeltaTime - * 0.425f); - - if (BDArmorySettings.INSTAKILL) p.Destroy(); - } - - if (Time.time - timeFired > 6 / 120 && BDArmorySettings.BULLET_HITS) - { - BulletHitFX.CreateBulletHit(p, laserPoint, hit, hit.normal, false, 0, 0); - } - } - else - { - laserPoint = lr.transform.InverseTransformPoint((targetDirectionLR * maxTargetingRange) + tf.position); - lr.SetPosition(1, laserPoint); - } - } - heat += heatPerShot * TimeWarp.CurrentRate; - return true; - } - else - { - return false; - } - } - - void SetupLaserSpecifics() - { - chargeSound = GameDatabase.Instance.GetAudioClip(chargeSoundPath); - if (HighLogic.LoadedSceneIsFlight) - { - audioSource.clip = fireSound; - } - - laserRenderers = new LineRenderer[fireTransforms.Length]; - - for (int i = 0; i < fireTransforms.Length; i++) - { - Transform tf = fireTransforms[i]; - laserRenderers[i] = tf.gameObject.AddComponent(); - Color laserColor = Misc.Misc.ParseColor255(projectileColor); - laserColor.a = laserColor.a / 2; - laserRenderers[i].material = new Material(Shader.Find("KSP/Particles/Alpha Blended")); - laserRenderers[i].material.SetColor("_TintColor", laserColor); - laserRenderers[i].material.mainTexture = GameDatabase.Instance.GetTexture("BDArmory/Textures/laser", false); - laserRenderers[i].shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; //= false; - laserRenderers[i].receiveShadows = false; - laserRenderers[i].startWidth = tracerStartWidth; - laserRenderers[i].endWidth = tracerEndWidth; - laserRenderers[i].positionCount = 2; - laserRenderers[i].SetPosition(0, Vector3.zero); - laserRenderers[i].SetPosition(1, Vector3.zero); - laserRenderers[i].useWorldSpace = false; - laserRenderers[i].enabled = false; - } - } - - bool WMgrAuthorized() - { - MissileFire manager = BDArmorySetup.Instance.ActiveWeaponManager; - if (manager != null && manager.vessel == vessel) - { - if (manager.hasSingleFired) return false; - else return true; - } - else - { - return true; - } - } - - void CheckWeaponSafety() - { - pointingAtSelf = false; - - // While I'm not saying vessels larger than 500m are impossible, let's be practical here - const float maxCheckRange = 500f; - float checkRange = Mathf.Min(targetAcquired ? targetDistance : maxTargetingRange, maxCheckRange); - - for (int i = 0; i < fireTransforms.Length; i++) - { - Ray ray = new Ray(fireTransforms[i].position, fireTransforms[i].forward); - RaycastHit hit; - - if (Physics.Raycast(ray, out hit, maxTargetingRange, 9076737)) - { - KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards(); - Part p = eva ? eva.part : hit.collider.gameObject.GetComponentInParent(); - if (p && p.vessel && p.vessel == vessel) - { - pointingAtSelf = true; - break; - } - } - - pointingAtPosition = fireTransforms[i].position + (ray.direction * targetDistance); - } - } - - public void EnableWeapon() - { - if (weaponState == WeaponStates.Enabled || weaponState == WeaponStates.PoweringUp) - { - return; - } - - StopShutdownStartupRoutines(); - - startupRoutine = StartCoroutine(StartupRoutine()); - } - - public void DisableWeapon() - { - if (weaponState == WeaponStates.Disabled || weaponState == WeaponStates.PoweringDown) - { - return; - } - - StopShutdownStartupRoutines(); - - shutdownRoutine = StartCoroutine(ShutdownRoutine()); - } - - void ParseWeaponType() - { - weaponType = weaponType.ToLower(); - - switch (weaponType) - { - case "ballistic": - eWeaponType = WeaponTypes.Ballistic; - break; - - case "cannon": - // Note: this type is deprecated. behavior is duplicated with Ballistic and bulletInfo.explosive = true - // Type remains for backward compatability for now. - eWeaponType = WeaponTypes.Cannon; - break; - - case "laser": - eWeaponType = WeaponTypes.Laser; - break; - } - } - - void DrainECPerShot() - { - if (ECPerShot == 0) return; - //double drainAmount = ECPerShot * TimeWarp.fixedDeltaTime; - double drainAmount = ECPerShot; - double chargeAvailable = part.RequestResource("ElectricCharge", drainAmount, ResourceFlowMode.ALL_VESSEL); - } - - bool CanFire() - { - if (ECPerShot != 0) - { - double chargeAvailable = part.RequestResource("ElectricCharge", ECPerShot, ResourceFlowMode.ALL_VESSEL); - if (chargeAvailable < ECPerShot * 0.95f) - { - ScreenMessages.PostScreenMessage("Weapon Requires EC", 5.0f, ScreenMessageStyle.UPPER_CENTER); - return false; - } - } - - if ((BDArmorySettings.INFINITE_AMMO || part.RequestResource(ammoName.GetHashCode(), (double)requestResourceAmount) > 0)) - { - return true; - } - - return false; - } - - #endregion Fire - - #region Audio - - void UpdateVolume() - { - if (audioSource) - { - audioSource.volume = BDArmorySettings.BDARMORY_WEAPONS_VOLUME; - } - if (audioSource2) - { - audioSource2.volume = BDArmorySettings.BDARMORY_WEAPONS_VOLUME; - } - if (lowpassFilter) - { - lowpassFilter.cutoffFrequency = BDArmorySettings.IVA_LOWPASS_FREQ; - } - } - - void SetupAudio() - { - fireSound = GameDatabase.Instance.GetAudioClip(fireSoundPath); - overheatSound = GameDatabase.Instance.GetAudioClip(overheatSoundPath); - if (!audioSource) - { - audioSource = gameObject.AddComponent(); - audioSource.bypassListenerEffects = true; - audioSource.minDistance = .3f; - audioSource.maxDistance = 1000; - audioSource.priority = 10; - audioSource.dopplerLevel = 0; - audioSource.spatialBlend = 1; - } - - if (!audioSource2) - { - audioSource2 = gameObject.AddComponent(); - audioSource2.bypassListenerEffects = true; - audioSource2.minDistance = .3f; - audioSource2.maxDistance = 1000; - audioSource2.dopplerLevel = 0; - audioSource2.priority = 10; - audioSource2.spatialBlend = 1; - } - - if (reloadAudioPath != string.Empty) - { - reloadAudioClip = (AudioClip)GameDatabase.Instance.GetAudioClip(reloadAudioPath); - } - if (reloadCompletePath != string.Empty) - { - reloadCompleteAudioClip = (AudioClip)GameDatabase.Instance.GetAudioClip(reloadCompletePath); - } - - if (!lowpassFilter && gameObject.GetComponents().Length == 0) - { - lowpassFilter = gameObject.AddComponent(); - lowpassFilter.cutoffFrequency = BDArmorySettings.IVA_LOWPASS_FREQ; - lowpassFilter.lowpassResonanceQ = 1f; - } - - UpdateVolume(); - } - - #endregion Audio - - #region Targeting - - void Aim() - { - //AI control - if (aiControlled && !slaved) - { - if (!targetAcquired) - { - autoFire = false; - return; - } - } - - if (!slaved && !aiControlled && (yawRange > 0 || maxPitch - minPitch > 0)) - { - //MouseControl - Vector3 mouseAim = new Vector3(Input.mousePosition.x / Screen.width, Input.mousePosition.y / Screen.height, - 0); - Ray ray = FlightCamera.fetch.mainCamera.ViewportPointToRay(mouseAim); - RaycastHit hit; - - if (Physics.Raycast(ray, out hit, maxTargetingRange, 9076737)) - { - targetPosition = hit.point; - - //aim through self vessel if occluding mouseray - - KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards(); - Part p = eva ? eva.part : hit.collider.gameObject.GetComponentInParent(); - - if (p && p.vessel && p.vessel == vessel) - { - targetPosition = ray.direction * maxTargetingRange + - FlightCamera.fetch.mainCamera.transform.position; - } - } - else - { - targetPosition = (ray.direction * (maxTargetingRange + (FlightCamera.fetch.Distance * 0.75f))) + - FlightCamera.fetch.mainCamera.transform.position; - if (visualTargetVessel != null && visualTargetVessel.loaded) - { - targetPosition = ray.direction * - Vector3.Distance(visualTargetVessel.transform.position, - FlightCamera.fetch.mainCamera.transform.position) + - FlightCamera.fetch.mainCamera.transform.position; - } - } - } - - //aim assist - Vector3 finalTarget = targetPosition; - Vector3 originalTarget = targetPosition; - targetDistance = Vector3.Distance(finalTarget, fireTransforms[0].position); - - if ((BDArmorySettings.AIM_ASSIST || aiControlled) && eWeaponType != WeaponTypes.Laser) - { - float effectiveVelocity = bulletVelocity; - Vector3 relativeVelocity = targetVelocity - part.rb.velocity; - Quaternion.FromToRotation(targetAccelerationPrevious, targetAcceleration).ToAngleAxis(out float accelDAngle, out Vector3 accelDAxis); - - Vector3 leadTarget = targetPosition; - - int iterations = 6; - while (--iterations >= 0) - { - finalTarget = targetPosition; - float time = (leadTarget - fireTransforms[0].position).magnitude / effectiveVelocity - (Time.fixedDeltaTime * 1.5f); - - if (targetAcquired) - { - finalTarget += relativeVelocity * time; -#if DEBUG - relVelAdj = relativeVelocity * time; - var vc = finalTarget; -#endif - var accelDExtAngle = accelDAngle * time / 3; - var extrapolatedAcceleration = - Quaternion.AngleAxis(accelDExtAngle, accelDAxis) - * targetAcceleration - * Mathf.Cos(accelDExtAngle * Mathf.Deg2Rad * 2.222f); - finalTarget += 0.5f * extrapolatedAcceleration * time * time; -#if DEBUG - accAdj = (finalTarget - vc); -#endif - } - else if (Misc.Misc.GetRadarAltitudeAtPos(targetPosition) < 2000) - { - //this vessel velocity compensation against stationary - finalTarget += (-(part.rb.velocity + Krakensbane.GetFrameVelocityV3f()) * time); - } + BDArmorySetup.OnVolumeChange += UpdateVolume; + } - leadTarget = finalTarget; + void OnDestroy() + { + BDArmorySetup.OnVolumeChange -= UpdateVolume; + WeaponNameWindow.OnActionGroupEditorOpened.Remove(OnActionGroupEditorOpened); + WeaponNameWindow.OnActionGroupEditorClosed.Remove(OnActionGroupEditorClosed); + } - if (bulletDrop) - { -#if DEBUG - var vc = finalTarget; -#endif - Vector3 up = (VectorUtils.GetUpDirection(finalTarget) + 2 * VectorUtils.GetUpDirection(fireTransforms[0].position)).normalized; - float gAccel = ((float)FlightGlobals.getGeeForceAtPosition(finalTarget).magnitude - + (float)FlightGlobals.getGeeForceAtPosition(fireTransforms[0].position).magnitude * 2) / 3; - Vector3 intermediateTarget = finalTarget + (0.5f * gAccel * time * time * up); - - var avGrav = (FlightGlobals.getGeeForceAtPosition(finalTarget) + 2 * FlightGlobals.getGeeForceAtPosition(fireTransforms[0].position)) / 3; - effectiveVelocity = bulletVelocity - * (float)Vector3d.Dot((intermediateTarget - fireTransforms[0].position).normalized, (finalTarget - fireTransforms[0].position).normalized) - + Vector3.Project(avGrav, finalTarget - fireTransforms[0].position).magnitude * time / 2 * (Vector3.Dot(avGrav, finalTarget - fireTransforms[0].position) < 0 ? -1 : 1); - finalTarget = intermediateTarget; -#if DEBUG - gravAdj = (finalTarget - vc); -#endif - } - } - - targetDistance = Vector3.Distance(finalTarget, fireTransforms[0].position); - fixedLeadOffset = originalTarget - finalTarget; //for aiming fixed guns to moving target - - //airdetonation - if (airDetonation) - { - if (targetAcquired && airDetonationTiming) - { - //detonationRange = BlastPhysicsUtils.CalculateBlastRange(bulletInfo.tntMass); //this returns 0, use detonationRange GUI tweakable instead - defaultDetonationRange = targetDistance;// adds variable time fuze if/when proximity fuzes fail - } - else - { - //detonationRange = defaultDetonationRange; - defaultDetonationRange = maxAirDetonationRange; //airburst at max range - } - } - } - - //removed the detonationange += UnityEngine.random, that gets called every frame and just causes the prox fuze range to wander - - finalAimTarget = finalTarget; - - //final turret aiming - if (slaved && !targetAcquired) return; - if (turret) - { - bool origSmooth = turret.smoothRotation; - if (aiControlled || slaved) - { - turret.smoothRotation = false; - } - turret.AimToTarget(finalTarget); - turret.smoothRotation = origSmooth; - } - } - - void CheckAIAutofire() - { - //autofiring with AI - if (targetAcquired && aiControlled) - { - Transform fireTransform = fireTransforms[0]; - Vector3 targetRelPos = (finalAimTarget) - fireTransform.position; - Vector3 aimDirection = fireTransform.forward; - float targetCosAngle = Vector3.Dot(aimDirection, targetRelPos.normalized); - - Vector3 targetDiffVec = finalAimTarget - lastFinalAimTarget; - Vector3 projectedTargetPos = targetDiffVec; - //projectedTargetPos /= TimeWarp.fixedDeltaTime; - //projectedTargetPos *= TimeWarp.fixedDeltaTime; - projectedTargetPos *= 2; //project where the target will be in 2 timesteps - projectedTargetPos += finalAimTarget; - - targetDiffVec.Normalize(); - Vector3 lastTargetRelPos = (lastFinalAimTarget) - fireTransform.position; - - if (BDATargetManager.CheckSafeToFireGuns(weaponManager, aimDirection, 1000, 0.999848f) //~1 degree of unsafe angle - && targetCosAngle >= maxAutoFireCosAngle) //check if directly on target - { - autoFire = true; - } - else - { - autoFire = false; - } - } - else - { - autoFire = false; - } - - //disable autofire after burst length - if (autoFire && Time.time - autoFireTimer > autoFireLength) - { - autoFire = false; - visualTargetVessel = null; - } - } - - IEnumerator AimAndFireAtEndOfFrame() - { - if (eWeaponType != WeaponTypes.Laser) yield return new WaitForEndOfFrame(); - - UpdateTargetVessel(); - updateAcceleration(targetVelocity, targetPosition); - relativeVelocity = targetVelocity - vessel.rb_velocity; - - RunTrajectorySimulation(); - Aim(); - CheckWeaponSafety(); - CheckAIAutofire(); - - if (finalFire) - { - if (eWeaponType == WeaponTypes.Laser) - { - if (FireLaser()) - { - for (int i = 0; i < laserRenderers.Length; i++) - { - laserRenderers[i].enabled = true; - } - } - else - { - for (int i = 0; i < laserRenderers.Length; i++) - { - laserRenderers[i].enabled = false; - } - audioSource.Stop(); - } - } - else - { - if (useRippleFire && weaponManager.gunRippleIndex != rippleIndex) - { - //timeFired = Time.time + (initialFireDelay - (60f / roundsPerMinute)) * TimeWarp.CurrentRate; - finalFire = false; - } - else - { - finalFire = true; - } - - if (finalFire) - Fire(); - } - - finalFire = false; - } - - yield break; - } - - public Vector3 GetLeadOffset() - { - return fixedLeadOffset; - } - - void RunTrajectorySimulation() - { - //trajectory simulation - if (BDArmorySettings.AIM_ASSIST && BDArmorySettings.DRAW_AIMERS - && (BDArmorySettings.DRAW_DEBUG_LINES - || (vessel && vessel.isActiveVessel && !aiControlled && !MapView.MapIsEnabled && !pointingAtSelf))) - { - Transform fireTransform = fireTransforms[0]; - - if (eWeaponType == WeaponTypes.Laser) - { - Ray ray = new Ray(fireTransform.position, fireTransform.forward); - RaycastHit rayHit; - if (Physics.Raycast(ray, out rayHit, maxTargetingRange, 9076737)) - { - bulletPrediction = rayHit.point; - } - else - { - bulletPrediction = ray.GetPoint(maxTargetingRange); - } - - pointingAtPosition = ray.GetPoint(maxTargetingRange); - } - else //ballistic/cannon weapons - { - float simDeltaTime = 0.155f; - - Vector3 simVelocity = part.rb.velocity + Krakensbane.GetFrameVelocityV3f() + (bulletVelocity * fireTransform.forward); - Vector3 simCurrPos = fireTransform.position + ((part.rb.velocity + Krakensbane.GetFrameVelocityV3f()) * Time.fixedDeltaTime); - Vector3 simPrevPos = simCurrPos; - Vector3 simStartPos = simCurrPos; - bool simulating = true; - - List pointPositions = new List(); - pointPositions.Add(simCurrPos); - - while (simulating) - { - RaycastHit hit; - if (bulletDrop) simVelocity += FlightGlobals.getGeeForceAtPosition(simCurrPos) * simDeltaTime; - simCurrPos += simVelocity * simDeltaTime; - pointPositions.Add(simCurrPos); - - if (Physics.Raycast(simPrevPos, simCurrPos - simPrevPos, out hit, - Vector3.Distance(simPrevPos, simCurrPos), 9076737)) - { - Vessel hitVessel = null; - try - { - KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards(); - hitVessel = (eva ? eva.part : hit.collider.gameObject.GetComponentInParent()).vessel; - } - catch (NullReferenceException) - { - } - - if (hitVessel == null || (hitVessel != null && hitVessel != vessel)) - { - bulletPrediction = hit.point; - simulating = false; - } - } - - simPrevPos = simCurrPos; - - if (visualTargetVessel != null && visualTargetVessel.loaded && !visualTargetVessel.Landed && - (simStartPos - simCurrPos).sqrMagnitude > targetDistance * targetDistance) - { - bulletPrediction = simStartPos + (simCurrPos - simStartPos).normalized * targetDistance; - simulating = false; - } - - if ((simStartPos - simCurrPos).sqrMagnitude > maxTargetingRange * maxTargetingRange) - { - bulletPrediction = simStartPos + ((simCurrPos - simStartPos).normalized * maxTargetingRange); - simulating = false; - } - } - - if (BDArmorySettings.DRAW_DEBUG_LINES && BDArmorySettings.DRAW_AIMERS) - { - Vector3[] pointsArray = pointPositions.ToArray(); - if (gameObject.GetComponent() == null) - { - LineRenderer lr = gameObject.AddComponent(); - lr.startWidth = .1f; - lr.endWidth = .1f; - lr.positionCount = pointsArray.Length; - for (int i = 0; i < pointsArray.Length; i++) - { - lr.SetPosition(i, pointsArray[i]); - } - } - else - { - LineRenderer lr = gameObject.GetComponent(); - lr.enabled = true; - lr.positionCount = pointsArray.Length; - for (int i = 0; i < pointsArray.Length; i++) - { - lr.SetPosition(i, pointsArray[i]); - } - } - } - } - } - } - - void DrawAlignmentIndicator() - { - if (fireTransforms == null || fireTransforms[0] == null) return; - - Transform refTransform = EditorLogic.RootPart.GetReferenceTransform(); - - if (!refTransform) return; - - Vector3 fwdPos = fireTransforms[0].position + (5 * fireTransforms[0].forward); - BDGUIUtils.DrawLineBetweenWorldPositions(fireTransforms[0].position, fwdPos, 4, Color.green); - - Vector3 referenceDirection = refTransform.up; - Vector3 refUp = -refTransform.forward; - Vector3 refRight = refTransform.right; - - Vector3 refFwdPos = fireTransforms[0].position + (5 * referenceDirection); - BDGUIUtils.DrawLineBetweenWorldPositions(fireTransforms[0].position, refFwdPos, 2, Color.white); - - BDGUIUtils.DrawLineBetweenWorldPositions(fwdPos, refFwdPos, 2, XKCDColors.Orange); - - Vector2 guiPos; - if (BDGUIUtils.WorldToGUIPos(fwdPos, out guiPos)) - { - Rect angleRect = new Rect(guiPos.x, guiPos.y, 100, 200); - - Vector3 pitchVector = (5 * Vector3.ProjectOnPlane(fireTransforms[0].forward, refRight)); - Vector3 yawVector = (5 * Vector3.ProjectOnPlane(fireTransforms[0].forward, refUp)); - - BDGUIUtils.DrawLineBetweenWorldPositions(fireTransforms[0].position + pitchVector, fwdPos, 3, - Color.white); - BDGUIUtils.DrawLineBetweenWorldPositions(fireTransforms[0].position + yawVector, fwdPos, 3, Color.white); - - float pitch = Vector3.Angle(pitchVector, referenceDirection); - float yaw = Vector3.Angle(yawVector, referenceDirection); - - string convergeDistance; - - Vector3 projAxis = Vector3.Project(refTransform.position - fireTransforms[0].transform.position, - refRight); - float xDist = projAxis.magnitude; - float convergeAngle = 90 - Vector3.Angle(yawVector, refTransform.up); - if (Vector3.Dot(fireTransforms[0].forward, projAxis) > 0) - { - convergeDistance = "Converge: " + - Mathf.Round((xDist * Mathf.Tan(convergeAngle * Mathf.Deg2Rad))).ToString() + "m"; - } - else - { - convergeDistance = "Diverging"; - } - - string xAngle = "X: " + Vector3.Angle(fireTransforms[0].forward, pitchVector).ToString("0.00"); - string yAngle = "Y: " + Vector3.Angle(fireTransforms[0].forward, yawVector).ToString("0.00"); - - GUI.Label(angleRect, xAngle + "\n" + yAngle + "\n" + convergeDistance); - } - } - - #endregion Targeting - - #region Updates - - void UpdateHeat() - { - heat = Mathf.Clamp(heat - heatLoss * TimeWarp.fixedDeltaTime, 0, Mathf.Infinity); - if (heat > maxHeat && !isOverheated) - { - isOverheated = true; - autoFire = false; - audioSource.Stop(); - wasFiring = false; - audioSource2.PlayOneShot(overheatSound); - weaponManager.ResetGuardInterval(); - } - if (heat < maxHeat / 3 && isOverheated) //reset on cooldown - { - isOverheated = false; - } - } - - void UpdateTargetVessel() - { - targetAcquired = false; - slaved = false; - bool atprWasAcquired = atprAcquired; - atprAcquired = false; - - if (weaponManager) - { - //legacy or visual range guard targeting - if (aiControlled && weaponManager && visualTargetVessel && - (visualTargetVessel.transform.position - transform.position).sqrMagnitude < weaponManager.guardRange * weaponManager.guardRange) - { - targetPosition = visualTargetVessel.CoM; - targetVelocity = visualTargetVessel.rb_velocity; - targetAcquired = true; - return; - } - - if (weaponManager.slavingTurrets && turret) - { - slaved = true; - targetPosition = weaponManager.slavedPosition; - targetVelocity = weaponManager.slavedTarget.vessel?.rb_velocity ?? (weaponManager.slavedVelocity - Krakensbane.GetFrameVelocityV3f()); - targetAcquired = true; - return; - } - - if (weaponManager.vesselRadarData && weaponManager.vesselRadarData.locked) - { - TargetSignatureData targetData = weaponManager.vesselRadarData.lockedTargetData.targetData; - targetVelocity = targetData.velocity - Krakensbane.GetFrameVelocityV3f(); - targetPosition = targetData.predictedPosition; - targetAcceleration = targetData.acceleration; - if (targetData.vessel) - { - targetVelocity = targetData.vessel?.rb_velocity ?? targetVelocity; - targetPosition = targetData.vessel.CoM; - } - targetAcquired = true; - return; - } - - //auto proxy tracking - if (vessel.isActiveVessel && autoProxyTrackRange > 0) - { - if (aptrTicker < 20) - { - aptrTicker++; - - if (atprWasAcquired) - { - targetAcquired = true; - atprAcquired = true; - } - } - else - { - aptrTicker = 0; - Vessel tgt = null; - float closestSqrDist = autoProxyTrackRange * autoProxyTrackRange; - List.Enumerator v = BDATargetManager.LoadedVessels.GetEnumerator(); - while (v.MoveNext()) - { - if (v.Current == null || !v.Current.loaded) continue; - if (!v.Current.IsControllable) continue; - if (v.Current == vessel) continue; - Vector3 targetVector = v.Current.transform.position - part.transform.position; - if (Vector3.Dot(targetVector, fireTransforms[0].forward) < 0) continue; - float sqrDist = (v.Current.transform.position - part.transform.position).sqrMagnitude; - if (sqrDist > closestSqrDist) continue; - if (Vector3.Angle(targetVector, fireTransforms[0].forward) > 20) continue; - tgt = v.Current; - closestSqrDist = sqrDist; - } - v.Dispose(); - - if (tgt == null) return; - targetAcquired = true; - atprAcquired = true; - targetPosition = tgt.CoM; - targetVelocity = tgt.rb_velocity; - } - } - } - } - - /// - /// Update target acceleration based on previous velocity. - /// Position is used to clamp acceleration for splashed targets, as ksp produces excessive bobbing. - /// - void updateAcceleration(Vector3 target_rb_velocity, Vector3 position) - { - targetAccelerationPrevious = targetAcceleration; - targetAcceleration = (target_rb_velocity - Krakensbane.GetLastCorrection() - targetVelocityPrevious) / Time.fixedDeltaTime; - float altitude = (float)FlightGlobals.currentMainBody.GetAltitude(position); - if (altitude < 12 && altitude > -10) - targetAcceleration = Vector3.ProjectOnPlane(targetAcceleration, VectorUtils.GetUpDirection(position)); - targetVelocityPrevious = target_rb_velocity; - } - - void UpdateGUIWeaponState() - { - guiStatusString = weaponState.ToString(); - } - - IEnumerator StartupRoutine() - { - weaponState = WeaponStates.PoweringUp; - UpdateGUIWeaponState(); - - if (hasDeployAnim && deployState) - { - deployState.enabled = true; - deployState.speed = 1; - while (deployState.normalizedTime < 1) //wait for animation here - { - yield return null; - } - deployState.normalizedTime = 1; - deployState.speed = 0; - deployState.enabled = false; - } - - weaponState = WeaponStates.Enabled; - UpdateGUIWeaponState(); - BDArmorySetup.Instance.UpdateCursorState(); - } - - IEnumerator ShutdownRoutine() - { - weaponState = WeaponStates.PoweringDown; - UpdateGUIWeaponState(); - BDArmorySetup.Instance.UpdateCursorState(); - if (turret) - { - yield return new WaitForSeconds(0.2f); - - while (!turret.ReturnTurret()) //wait till turret has returned - { - yield return new WaitForFixedUpdate(); - } - } - - if (hasDeployAnim) - { - deployState.enabled = true; - deployState.speed = -1; - while (deployState.normalizedTime > 0) - { - yield return null; - } - deployState.normalizedTime = 0; - deployState.speed = 0; - deployState.enabled = false; - } - - weaponState = WeaponStates.Disabled; - UpdateGUIWeaponState(); - } - - void StopShutdownStartupRoutines() - { - if (shutdownRoutine != null) - { - StopCoroutine(shutdownRoutine); - shutdownRoutine = null; - } - - if (startupRoutine != null) - { - StopCoroutine(startupRoutine); - startupRoutine = null; - } - } - - #endregion Updates - - #region Bullets - - void ParseBulletDragType() - { - bulletDragTypeName = bulletDragTypeName.ToLower(); - - switch (bulletDragTypeName) - { - case "none": - bulletDragType = BulletDragTypes.None; - break; - - case "numericalintegration": - bulletDragType = BulletDragTypes.NumericalIntegration; - break; - - case "analyticestimate": - bulletDragType = BulletDragTypes.AnalyticEstimate; - break; - } - } - - void SetupBulletPool() - { - GameObject templateBullet = new GameObject("Bullet"); - templateBullet.AddComponent(); - templateBullet.SetActive(false); - bulletPool = ObjectPool.CreateObjectPool(templateBullet, 100, true, true); - } - - void SetupShellPool() - { - GameObject templateShell = - (GameObject)Instantiate(GameDatabase.Instance.GetModel("BDArmory/Models/shell/model")); - templateShell.SetActive(false); - templateShell.AddComponent(); - shellPool = ObjectPool.CreateObjectPool(templateShell, 50, true, true); - } - - void SetupBullet() - { - bulletInfo = BulletInfo.bullets[bulletType]; - if (bulletType != "def") - { - //use values from bullets.cfg if not the Part Module defaults are used - caliber = bulletInfo.caliber; - bulletVelocity = bulletInfo.bulletVelocity; - bulletMass = bulletInfo.bulletMass; - bulletDragTypeName = bulletInfo.bulletDragTypeName; - cannonShellHeat = bulletInfo.blastHeat; - cannonShellPower = bulletInfo.blastPower; - cannonShellRadius = bulletInfo.blastRadius; - } - ParseBulletDragType(); - } - - protected void SetInitialDetonationDistance() + void Update() { - if (this.detonationRange == -1) + if (HighLogic.LoadedSceneIsFlight && FlightGlobals.ready && !vessel.packed && vessel.IsControllable) { - if (eWeaponType == WeaponTypes.Ballistic && (bulletInfo.tntMass != 0 && (proximityDetonation || airDetonation))) + if (lowpassFilter) { - detonationRange = (BlastPhysicsUtils.CalculateBlastRange(bulletInfo.tntMass) * 0.66f); + if (InternalCamera.Instance && InternalCamera.Instance.isActive) + { + lowpassFilter.enabled = true; + } + else + { + lowpassFilter.enabled = false; + } + } + + if (weaponState == WeaponStates.Enabled && + (TimeWarp.WarpMode != TimeWarp.Modes.HIGH || TimeWarp.CurrentRate == 1)) + { + userFiring = (BDInputUtils.GetKey(BDInputSettingsFields.WEAP_FIRE_KEY) && + (vessel.isActiveVessel || BDArmorySettings.REMOTE_SHOOTING) && !MapView.MapIsEnabled && + !aiControlled); + if ((userFiring || autoFire || agHoldFiring) && + (yawRange == 0 || (maxPitch - minPitch) == 0 || + turret.TargetInRange(finalAimTarget, 10, float.MaxValue))) + { + if (useRippleFire && ((pointingAtSelf || isOverheated || isReloading) || (aiControlled && engageRangeMax < targetDistance)))// is weapon within set max range? + { + StartCoroutine(IncrementRippleIndex(0)); + finalFire = false; + } + else if (eWeaponType == WeaponTypes.Ballistic || eWeaponType == WeaponTypes.Rocket) //WeaponTypes.Cannon is deprecated + { + finalFire = true; + } + } + else + { + if (spinDownAnimation) spinningDown = true; + if (eWeaponType == WeaponTypes.Laser) audioSource.Stop(); + if (!oneShotSound && wasFiring) + { + audioSource.Stop(); + wasFiring = false; + audioSource2.PlayOneShot(overheatSound); + } + } } else { - detonationRange = 0f; - proximityDetonation = false; + audioSource.Stop(); + autoFire = false; + } + + if (spinningDown && spinDownAnimation && hasFireAnimation) + { + if (fireState.normalizedTime > 1) fireState.normalizedTime = 0; + fireState.speed = fireAnimSpeed; + fireAnimSpeed = Mathf.Lerp(fireAnimSpeed, 0, 0.04f); + } + + // Draw gauges + if (vessel.isActiveVessel) + { + vessel.GetConnectedResourceTotals(AmmoID, out double ammoCurrent, out double ammoMax); + gauge.UpdateAmmoMeter((float)(ammoCurrent / ammoMax)); + ammoCount = ammoCurrent; + if (showReloadMeter) + { + if (isReloading) + { + gauge.UpdateReloadMeter(ReloadTimer); + } + else + { + gauge.UpdateReloadMeter((Time.time - timeFired) * roundsPerMinute / 60); + } + } + //else + //{ + gauge.UpdateHeatMeter(heat / maxHeat); + //} + } + if (!BeltFed) + { + ReloadWeapon(); + } + if (crewserved) + { + KerbalSeat KS = part.Modules.OfType().First(); + if (KS.Occupant == null) + { + hasGunner = false; + } + else + { + hasGunner = true; + } } } - if (BDArmorySettings.DRAW_DEBUG_LABELS) + } + + void FixedUpdate() + { + if (HighLogic.LoadedSceneIsFlight && !vessel.packed) { - Debug.Log("[BDArmory]: DetonationDistance = : " + detonationRange); + if (!vessel.IsControllable) + { + if (weaponState != WeaponStates.PoweringDown || weaponState != WeaponStates.Disabled) + { + DisableWeapon(); + } + return; + } + + UpdateHeat(); + if (weaponState == WeaponStates.Enabled && + (TimeWarp.WarpMode != TimeWarp.Modes.HIGH || TimeWarp.CurrentRate == 1)) + { + //Aim(); + StartCoroutine(AimAndFireAtEndOfFrame()); + + if (eWeaponType == WeaponTypes.Laser) + { + if ((userFiring || autoFire || agHoldFiring) && + (!turret || turret.TargetInRange(targetPosition, 10, float.MaxValue))) + { + finalFire = true; + } + else + { + for (int i = 0; i < laserRenderers.Length; i++) + { + laserRenderers[i].enabled = false; + } + audioSource.Stop(); + } + } + } + else if (eWeaponType == WeaponTypes.Laser) + { + for (int i = 0; i < laserRenderers.Length; i++) + { + laserRenderers[i].enabled = false; + } + audioSource.Stop(); + } + } + lastFinalAimTarget = finalAimTarget; + } + + private void UpdateMenus(bool visible) + { + Events["HideUI"].active = visible; + Events["ShowUI"].active = !visible; + } + + private void OnActionGroupEditorOpened() + { + Events["HideUI"].active = false; + Events["ShowUI"].active = false; + } + + private void OnActionGroupEditorClosed() + { + Events["HideUI"].active = false; + Events["ShowUI"].active = true; + } + + [KSPEvent(guiActiveEditor = true, guiName = "#LOC_BDArmory_HideWeaponGroupUI", active = false)]//Hide Weapon Group UI + public void HideUI() + { + WeaponGroupWindow.HideGUI(); + UpdateMenus(false); + } + + [KSPEvent(guiActiveEditor = true, guiName = "#LOC_BDArmory_SetWeaponGroupUI", active = false)]//Set Weapon Group UI + public void ShowUI() + { + WeaponGroupWindow.ShowGUI(this); + UpdateMenus(true); + } + + void OnGUI() + { + if (weaponState == WeaponStates.Enabled && vessel && !vessel.packed && vessel.isActiveVessel && + BDArmorySettings.DRAW_AIMERS && !aiControlled && !MapView.MapIsEnabled && !pointingAtSelf) + { + float size = 30; + + Vector3 reticlePosition; + if (BDArmorySettings.AIM_ASSIST) + { + if (targetAcquired && (slaved || yawRange < 1 || maxPitch - minPitch < 1)) + { + reticlePosition = pointingAtPosition + fixedLeadOffset; + + if (!slaved) + { + BDGUIUtils.DrawLineBetweenWorldPositions(pointingAtPosition, reticlePosition, 2, + new Color(0, 1, 0, 0.6f)); + } + + BDGUIUtils.DrawTextureOnWorldPos(pointingAtPosition, BDArmorySetup.Instance.greenDotTexture, + new Vector2(6, 6), 0); + + if (atprAcquired) + { + BDGUIUtils.DrawTextureOnWorldPos(targetPosition, BDArmorySetup.Instance.openGreenSquare, + new Vector2(20, 20), 0); + } + } + else + { + reticlePosition = bulletPrediction; + } + } + else + { + reticlePosition = pointingAtPosition; + } + + Texture2D texture; + if (Vector3.Angle(pointingAtPosition - transform.position, finalAimTarget - transform.position) < 1f) + { + texture = BDArmorySetup.Instance.greenSpikedPointCircleTexture; + } + else + { + texture = BDArmorySetup.Instance.greenPointCircleTexture; + } + BDGUIUtils.DrawTextureOnWorldPos(reticlePosition, texture, new Vector2(size, size), 0); + + if (BDArmorySettings.DRAW_DEBUG_LINES) + { + if (targetAcquired) + { + BDGUIUtils.DrawLineBetweenWorldPositions(fireTransforms[0].position, targetPosition, 2, + Color.blue); + } + } + } + + if (HighLogic.LoadedSceneIsEditor && BDArmorySetup.showWeaponAlignment) + { + DrawAlignmentIndicator(); + } + +#if DEBUG + if (BDArmorySettings.DRAW_DEBUG_LINES && weaponState == WeaponStates.Enabled && vessel && !vessel.packed && !MapView.MapIsEnabled) + { + BDGUIUtils.MarkPosition(targetPosition, transform, Color.cyan); + BDGUIUtils.DrawLineBetweenWorldPositions(targetPosition, targetPosition + relVelAdj, 2, Color.green); + BDGUIUtils.DrawLineBetweenWorldPositions(targetPosition + relVelAdj, targetPosition + relVelAdj + accAdj, 2, Color.magenta); + BDGUIUtils.DrawLineBetweenWorldPositions(targetPosition + relVelAdj + accAdj, targetPosition + relVelAdj + accAdj + gravAdj, 2, Color.yellow); + BDGUIUtils.MarkPosition(finalAimTarget, transform, Color.cyan, size: 4); + } +#endif + } + + #endregion KSP Events + + //new regions sorting/organizing gun, laser, and rocket specific code into their own sections for ease of maintainence. Everything else is shared code + #region Gun Fire + + private void Fire() + { + if (BDArmorySetup.GameIsPaused) + { + if (audioSource.isPlaying) + { + audioSource.Stop(); + } + return; + } + + float timeGap = (60 / roundsPerMinute) * TimeWarp.CurrentRate; + if (Time.time - timeFired > timeGap + && !isOverheated + && !isReloading + && !pointingAtSelf + && (aiControlled || !Misc.Misc.CheckMouseIsOnGui()) + && WMgrAuthorized()) + { + bool effectsShot = false; + //Transform[] fireTransforms = part.FindModelTransforms("fireTransform"); + for (float iTime = Mathf.Min(Time.time - timeFired - timeGap, TimeWarp.fixedDeltaTime); iTime >= 0; iTime -= timeGap) + for (int i = 0; i < fireTransforms.Length; i++) + { + //if ((BDArmorySettings.INFINITE_AMMO || part.RequestResource(ammoName, requestResourceAmount) > 0)) + if (CanFire()) + { + Transform fireTransform = fireTransforms[i]; + spinningDown = false; + + //recoil + if (hasRecoil) + { + part.rb.AddForceAtPosition((-fireTransform.forward) * (bulletVelocity * bulletMass / 1000 * BDArmorySettings.RECOIL_FACTOR * recoilReduction), + fireTransform.position, ForceMode.Impulse); + } + + if (!effectsShot) + { + //sound + if (oneShotSound) + { + audioSource.Stop(); + audioSource.PlayOneShot(fireSound); + } + else + { + wasFiring = true; + if (!audioSource.isPlaying) + { + audioSource.clip = fireSound; + audioSource.loop = false; + audioSource.time = 0; + audioSource.Play(); + } + else + { + if (audioSource.time >= fireSound.length) + { + audioSource.time = soundRepeatTime; + } + } + } + + //animation + if (hasFireAnimation) + { + float unclampedSpeed = (roundsPerMinute * fireState.length) / 60f; + float lowFramerateFix = 1; + if (roundsPerMinute > 500f) + { + lowFramerateFix = (0.02f / Time.deltaTime); + } + fireAnimSpeed = Mathf.Clamp(unclampedSpeed, 1f * lowFramerateFix, 20f * lowFramerateFix); + fireState.enabled = true; + if (unclampedSpeed == fireAnimSpeed || fireState.normalizedTime > 1) + { + fireState.normalizedTime = 0; + } + fireState.speed = fireAnimSpeed; + fireState.normalizedTime = Mathf.Repeat(fireState.normalizedTime, 1); + + //Debug.Log("fireAnim time: " + fireState.normalizedTime + ", speed; " + fireState.speed); + } + + //muzzle flash + List.Enumerator pEmitter = muzzleFlashEmitters.GetEnumerator(); + while (pEmitter.MoveNext()) + { + if (pEmitter.Current == null) continue; + //KSPParticleEmitter pEmitter = mtf.gameObject.GetComponent(); + if (pEmitter.Current.useWorldSpace && !oneShotWorldParticles) continue; + if (pEmitter.Current.maxEnergy < 0.5f) + { + float twoFrameTime = Mathf.Clamp(Time.deltaTime * 2f, 0.02f, 0.499f); + pEmitter.Current.maxEnergy = twoFrameTime; + pEmitter.Current.minEnergy = twoFrameTime / 3f; + } + pEmitter.Current.Emit(); + } + pEmitter.Dispose(); + + List.Enumerator gpe = gaplessEmitters.GetEnumerator(); + while (gpe.MoveNext()) + { + if (gpe.Current == null) continue; + gpe.Current.EmitParticles(); + } + gpe.Dispose(); + + //shell ejection + if (BDArmorySettings.EJECT_SHELLS) + { + IEnumerator sTf = shellEjectTransforms.AsEnumerable().GetEnumerator(); + while (sTf.MoveNext()) + { + if (sTf.Current == null) continue; + GameObject ejectedShell = shellPool.GetPooledObject(); + ejectedShell.transform.position = sTf.Current.position; + //+(part.rb.velocity*TimeWarp.fixedDeltaTime); + ejectedShell.transform.rotation = sTf.Current.rotation; + ejectedShell.transform.localScale = Vector3.one * shellScale; + ShellCasing shellComponent = ejectedShell.GetComponent(); + shellComponent.initialV = part.rb.velocity; + ejectedShell.SetActive(true); + } + sTf.Dispose(); + } + effectsShot = true; + } + + //firing bullet + GameObject firedBullet = bulletPool.GetPooledObject(); + PooledBullet pBullet = firedBullet.GetComponent(); + + firedBullet.transform.position = fireTransform.position; + + pBullet.caliber = bulletInfo.caliber; + pBullet.bulletVelocity = bulletInfo.bulletVelocity; + pBullet.bulletMass = bulletInfo.bulletMass; + pBullet.explosive = bulletInfo.explosive; + pBullet.apBulletMod = bulletInfo.apBulletMod; + pBullet.bulletDmgMult = bulletDmgMult; + + //A = π x (Ø / 2)^2 + bulletDragArea = Mathf.PI * Mathf.Pow(caliber / 2f, 2f); + + //Bc = m/Cd * A + bulletBallisticCoefficient = bulletMass / ((bulletDragArea / 1000000f) * 0.295f); // mm^2 to m^2 + + //Bc = m/d^2 * i where i = 0.484 + //bulletBallisticCoefficient = bulletMass / Mathf.Pow(caliber / 1000, 2f) * 0.484f; + + pBullet.ballisticCoefficient = bulletBallisticCoefficient; + + pBullet.flightTimeElapsed = iTime; + // measure bullet lifetime in time rather than in distance, because distances get very relative in orbit + pBullet.timeToLiveUntil = Mathf.Max(maxTargetingRange, maxEffectiveDistance) / bulletVelocity * 1.1f + Time.time; + + timeFired = Time.time - iTime; + + Vector3 firedVelocity = + VectorUtils.GaussianDirectionDeviation(fireTransform.forward, maxDeviation / 4) * bulletVelocity; + + pBullet.currentVelocity = (part.rb.velocity + Krakensbane.GetFrameVelocityV3f()) + firedVelocity; // use the real velocity, w/o offloading + firedBullet.transform.position += (part.rb.velocity + Krakensbane.GetFrameVelocityV3f()) * Time.fixedDeltaTime + + pBullet.currentVelocity * iTime; + + pBullet.sourceVessel = vessel; + pBullet.bulletTexturePath = bulletTexturePath; + pBullet.projectileColor = projectileColorC; + pBullet.startColor = startColorC; + pBullet.fadeColor = fadeColor; + tracerIntervalCounter++; + if (tracerIntervalCounter > tracerInterval) + { + tracerIntervalCounter = 0; + pBullet.tracerStartWidth = tracerStartWidth; + pBullet.tracerEndWidth = tracerEndWidth; + pBullet.tracerLength = tracerLength; + } + else + { + pBullet.tracerStartWidth = nonTracerWidth; + pBullet.tracerEndWidth = nonTracerWidth; + pBullet.startColor.a *= 0.25f; + pBullet.projectileColor.a *= 0.25f; + pBullet.tracerLength = tracerLength * 0.4f; + } + pBullet.tracerLuminance = tracerLuminance; + pBullet.tracerDeltaFactor = tracerDeltaFactor; + pBullet.bulletDrop = bulletDrop; + + if (eWeaponType == WeaponTypes.Ballistic && bulletInfo.explosive) //WeaponTypes.Cannon is deprecated + { + if (bulletType == "def") + { + //legacy model, per weapon config + pBullet.bulletType = PooledBullet.PooledBulletTypes.Explosive; + pBullet.explModelPath = explModelPath; + pBullet.explSoundPath = explSoundPath; + pBullet.blastPower = cannonShellPower; + pBullet.blastHeat = cannonShellHeat; + pBullet.radius = cannonShellRadius; + pBullet.airDetonation = airDetonation; + pBullet.detonationRange = detonationRange; + pBullet.maxAirDetonationRange = maxAirDetonationRange; + pBullet.defaultDetonationRange = defaultDetonationRange; + pBullet.proximityDetonation = proximityDetonation; + } + else + { + //use values from bullets.cfg + pBullet.bulletType = PooledBullet.PooledBulletTypes.Explosive; + pBullet.explModelPath = explModelPath; + pBullet.explSoundPath = explSoundPath; + + pBullet.tntMass = bulletInfo.tntMass; + pBullet.blastPower = bulletInfo.blastPower; + pBullet.blastHeat = bulletInfo.blastHeat; + pBullet.radius = bulletInfo.blastRadius; + + pBullet.airDetonation = airDetonation; + pBullet.detonationRange = detonationRange; + pBullet.maxAirDetonationRange = maxAirDetonationRange; + pBullet.defaultDetonationRange = defaultDetonationRange; + pBullet.proximityDetonation = proximityDetonation; + } + } + else + { + pBullet.bulletType = PooledBullet.PooledBulletTypes.Standard; + pBullet.airDetonation = false; + } + switch (bulletDragType) + { + case BulletDragTypes.None: + pBullet.dragType = PooledBullet.BulletDragTypes.None; + break; + + case BulletDragTypes.AnalyticEstimate: + pBullet.dragType = PooledBullet.BulletDragTypes.AnalyticEstimate; + break; + + case BulletDragTypes.NumericalIntegration: + pBullet.dragType = PooledBullet.BulletDragTypes.NumericalIntegration; + break; + } + + pBullet.bullet = BulletInfo.bullets[bulletType]; + pBullet.gameObject.SetActive(true); + + //heat + heat += heatPerShot; + //EC + DrainECPerShot(); + RoundsRemaining++; + } + else + { + spinningDown = true; + if (!oneShotSound && wasFiring) + { + audioSource.Stop(); + wasFiring = false; + audioSource2.PlayOneShot(overheatSound); + } + } + } + + if (useRippleFire) + { + StartCoroutine(IncrementRippleIndex(initialFireDelay * TimeWarp.CurrentRate)); + } + } + else + { + spinningDown = true; + } + } + + bool CanFire() + { + if (ECPerShot != 0) + { + double chargeAvailable = part.RequestResource("ElectricCharge", ECPerShot, ResourceFlowMode.ALL_VESSEL); + if (chargeAvailable < ECPerShot * 0.95f) + { + ScreenMessages.PostScreenMessage("Weapon Requires EC", 5.0f, ScreenMessageStyle.UPPER_CENTER); + return false; + } + } + if (!hasGunner) + { + ScreenMessages.PostScreenMessage("Weapon Requires Gunner", 5.0f, ScreenMessageStyle.UPPER_CENTER); + return false; + } + if (BDArmorySettings.INFINITE_AMMO) + { + return true; + } + else if (part.RequestResource(ammoName, requestResourceAmount) > 0) + { + return true; + } + + return false; + } + + void DrainECPerShot() + { + if (ECPerShot == 0) return; + //double drainAmount = ECPerShot * TimeWarp.fixedDeltaTime; + double drainAmount = ECPerShot; + double chargeAvailable = part.RequestResource("ElectricCharge", drainAmount, ResourceFlowMode.ALL_VESSEL); + } + + #endregion + + #region Laser Fire + + private bool FireLaser() + { + float chargeAmount = requestResourceAmount * TimeWarp.fixedDeltaTime; + + if (!pointingAtSelf && !Misc.Misc.CheckMouseIsOnGui() && WMgrAuthorized() && !isOverheated && + (part.RequestResource(ammoName, chargeAmount) >= chargeAmount || BDArmorySettings.INFINITE_AMMO)) + { + if (!audioSource.isPlaying) + { + audioSource.PlayOneShot(chargeSound); + audioSource.Play(); + audioSource.loop = true; + } + for (int i = 0; i < fireTransforms.Length; i++) + { + Transform tf = fireTransforms[i]; + + LineRenderer lr = laserRenderers[i]; + + Vector3 rayDirection = tf.forward; + + Vector3 targetDirection = Vector3.zero; //autoTrack enhancer + Vector3 targetDirectionLR = tf.forward; + + if (((visualTargetVessel != null && visualTargetVessel.loaded) || slaved) + && Vector3.Angle(rayDirection, targetDirection) < 1) + { + //targetDirection = targetPosition + (relativeVelocity * Time.fixedDeltaTime) * 2 - tf.position; + targetDirection = targetPosition - tf.position; + rayDirection = targetDirection; + targetDirectionLR = targetDirection.normalized; + } + + Ray ray = new Ray(tf.position, rayDirection); + lr.useWorldSpace = false; + lr.SetPosition(0, Vector3.zero); + RaycastHit hit; + + if (Physics.Raycast(ray, out hit, maxTargetingRange, 9076737)) // this is getting offset slightly zzz + { + lr.useWorldSpace = true; + laserPoint = hit.point + (targetVelocity * Time.fixedDeltaTime); //already adding targetvel to the laser offset in line 1457 + //laserPoint = hit.point; + lr.SetPosition(0, tf.position + (part.rb.velocity * Time.fixedDeltaTime)); + lr.SetPosition(1, laserPoint); + + KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards(); + Part p = eva ? eva.part : hit.collider.gameObject.GetComponentInParent(); + + if (p && p.vessel && p.vessel != vessel) + { + float distance = 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. + p.AddDamage(laserDamage / (1 + Mathf.PI * Mathf.Pow(tanAngle * distance, 2)) * + TimeWarp.fixedDeltaTime + * 0.425f); + + if (BDArmorySettings.INSTAKILL) p.Destroy(); + } + + if (Time.time - timeFired > 6 / 120 && BDArmorySettings.BULLET_HITS) + { + BulletHitFX.CreateBulletHit(p, hit.point, hit, hit.normal, false, 0, 0); + } + } + else + { + laserPoint = lr.transform.InverseTransformPoint((targetDirectionLR * maxTargetingRange) + tf.position); + lr.SetPosition(1, laserPoint); + } + } + heat += heatPerShot * TimeWarp.CurrentRate; + return true; + } + else + { + return false; + } + } + + void SetupLaserSpecifics() + { + chargeSound = GameDatabase.Instance.GetAudioClip(chargeSoundPath); + if (HighLogic.LoadedSceneIsFlight) + { + audioSource.clip = fireSound; + } + + laserRenderers = new LineRenderer[fireTransforms.Length]; + + for (int i = 0; i < fireTransforms.Length; i++) + { + Transform tf = fireTransforms[i]; + laserRenderers[i] = tf.gameObject.AddComponent(); + Color laserColor = Misc.Misc.ParseColor255(projectileColor); + laserColor.a = laserColor.a / 2; + laserRenderers[i].material = new Material(Shader.Find("KSP/Particles/Alpha Blended")); + laserRenderers[i].material.SetColor("_TintColor", laserColor); + laserRenderers[i].material.mainTexture = GameDatabase.Instance.GetTexture("BDArmory/Textures/laser", false); + laserRenderers[i].shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; //= false; + laserRenderers[i].receiveShadows = false; + laserRenderers[i].startWidth = tracerStartWidth; + laserRenderers[i].endWidth = tracerEndWidth; + laserRenderers[i].positionCount = 2; + laserRenderers[i].SetPosition(0, Vector3.zero); + laserRenderers[i].SetPosition(1, Vector3.zero); + laserRenderers[i].useWorldSpace = false; + laserRenderers[i].enabled = false; + } + } + + #endregion + + #region Rocket Fire + // this is the extent of RocketLauncher code that differs from ModuleWeapon + public void FireRocket() //#11, #673 + { + int rocketsLeft; + + float timeGap = (60 / roundsPerMinute) * TimeWarp.CurrentRate; + if (Time.time - timeFired > timeGap && !isReloading || !pointingAtSelf && (aiControlled || !Misc.Misc.CheckMouseIsOnGui()) && WMgrAuthorized()) + {// fixes rocket ripple code for proper rippling + for (float iTime = Mathf.Min(Time.time - timeFired - timeGap, TimeWarp.fixedDeltaTime); iTime >= 0; iTime -= timeGap) + { + if (CanFire()) + { + if (BDArmorySettings.INFINITE_AMMO) + { + rocketsLeft = 1; + } + else + { + if (!externalAmmo) + { + PartResource rocketResource = GetRocketResource(); + rocketsLeft = (int)rocketResource.amount; + } + else + { + vessel.GetConnectedResourceTotals(AmmoID, out double ammoCurrent, out double ammoMax); + rocketsLeft = Mathf.Clamp((int)(RoundsPerMag - RoundsRemaining), 0, Mathf.Clamp((int)ammoCurrent, 0, RoundsPerMag)); + } + } + if (rocketsLeft >= 1) + { + if (rocketPod) + { + Transform currentRocketTfm = rockets[rocketsLeft - 1]; + GameObject rocketObj = GameDatabase.Instance.GetModel(rocketModelPath); + rocketObj = (GameObject)Instantiate(rocketObj, currentRocketTfm.position, currentRocketTfm.parent.rotation); + rocketObj.transform.rotation = currentRocketTfm.parent.rotation; + rocketObj.transform.localScale = part.rescaleFactor * Vector3.one; + currentRocketTfm.localScale = Vector3.zero; + Rocket rocket = rocketObj.AddComponent(); + rocket.explModelPath = explModelPath; + rocket.explSoundPath = explSoundPath; + rocket.spawnTransform = currentRocketTfm; + rocket.mass = rocketMass; + rocket.blastForce = blastForce; + rocket.blastHeat = blastHeat; + rocket.blastRadius = blastRadius; + rocket.thrust = thrust; + rocket.thrustTime = thrustTime; + rocket.proximityDetonation = proximityDetonation; + rocket.detonationRange = detonationRange; + rocket.maxAirDetonationRange = maxAirDetonationRange; + rocket.randomThrustDeviation = thrustDeviation; + rocket.sourceVessel = vessel; + rocketObj.SetActive(true); + rocketObj.transform.SetParent(currentRocketTfm.parent); + rocket.parentRB = part.rb; + + if (!BDArmorySettings.INFINITE_AMMO) + { + if (externalAmmo) + { + part.RequestResource(ammoName, 1); + } + else + { + GetRocketResource().amount--; + } + } + if (!BeltFed) + { + RoundsRemaining++; + } + UpdateRocketScales(); + } + else + { + if (!isOverheated) + { + for (int i = 0; i < fireTransforms.Length; i++) + { + Transform currentRocketTfm = fireTransforms[i]; + GameObject rocketObj = GameDatabase.Instance.GetModel(rocketModelPath); + rocketObj = (GameObject)Instantiate(rocketObj, currentRocketTfm.position, currentRocketTfm.rotation); + rocketObj.transform.rotation = currentRocketTfm.rotation; + Rocket rocket = rocketObj.AddComponent(); + rocket.explModelPath = explModelPath; + rocket.explSoundPath = explSoundPath; + rocket.spawnTransform = currentRocketTfm; + rocket.mass = rocketMass; + rocket.blastForce = blastForce; + rocket.blastHeat = blastHeat; + rocket.blastRadius = blastRadius; + rocket.thrust = thrust; + rocket.thrustTime = thrustTime; + rocket.proximityDetonation = proximityDetonation; + rocket.detonationRange = detonationRange; + rocket.maxAirDetonationRange = maxAirDetonationRange; + rocket.randomThrustDeviation = thrustDeviation; + rocket.sourceVessel = vessel; + rocketObj.SetActive(true); + rocketObj.transform.SetParent(currentRocketTfm); + rocket.parentRB = part.rb; + if (!BDArmorySettings.INFINITE_AMMO) + { + part.RequestResource(ammoName, 1); + } + heat += heatPerShot; + if (!BeltFed) + { + RoundsRemaining++; + } + } + } + } + audioSource.PlayOneShot(GameDatabase.Instance.GetAudioClip(launchSoundPath)); //change to firesound + timeFired = Time.time - iTime; + } + } + } + } + if (useRippleFire) + { + StartCoroutine(IncrementRippleIndex(initialFireDelay * TimeWarp.CurrentRate)); + } + } + void MakeRocketArray() + { + Transform rocketsTransform = part.FindModelTransform("rockets");// important to keep this seperate from the fireTransformName transform + int numOfRockets = rocketsTransform.childCount; // due to rockets.Rocket_n being inconsistantly aligned + rockets = new Transform[numOfRockets]; // (and subsequently messing up the aim() vestors) + if (rocketPod) // and this overwriting the previous fireTransFormName -> fireTransForms + { + RoundsPerMag = numOfRockets; + } + for (int i = 0; i < numOfRockets; i++) + { + string rocketName = rocketsTransform.GetChild(i).name; + int rocketIndex = int.Parse(rocketName.Substring(7)) - 1; + rockets[rocketIndex] = rocketsTransform.GetChild(i); + } + if (!descendingOrder) Array.Reverse(rockets); + } + + void UpdateRocketScales() + { + double rocketQty = 0; + + if (!externalAmmo) + { + PartResource rocketResource = GetRocketResource(); + rocketQty = rocketResource.amount; + rocketsMax = rocketResource.maxAmount; + } + else + { + rocketQty = (RoundsPerMag - RoundsRemaining); + rocketsMax = RoundsPerMag; + } + var rocketsLeft = Math.Floor(rocketQty); + + for (int i = 0; i < rocketsMax; i++) + { + if (i < rocketsLeft) rockets[i].localScale = Vector3.one; + else rockets[i].localScale = Vector3.zero; + } + + } + public PartResource GetRocketResource() + { + IEnumerator res = part.Resources.GetEnumerator(); + while (res.MoveNext()) + { + if (res.Current == null) continue; + if (res.Current.resourceName == ammoName) return res.Current; + } + res.Dispose(); + return null; + } + #endregion + + //more code organization + #region Weapon Setup + + public void EnableWeapon() + { + if (weaponState == WeaponStates.Enabled || weaponState == WeaponStates.PoweringUp) + { + return; + } + + StopShutdownStartupRoutines(); + + startupRoutine = StartCoroutine(StartupRoutine()); + } + + public void DisableWeapon() + { + if (weaponState == WeaponStates.Disabled || weaponState == WeaponStates.PoweringDown) + { + return; + } + + StopShutdownStartupRoutines(); + + shutdownRoutine = StartCoroutine(ShutdownRoutine()); + } + + void ParseWeaponType() + { + weaponType = weaponType.ToLower(); + + switch (weaponType) + { + case "ballistic": + eWeaponType = WeaponTypes.Ballistic; + break; + + case "rocket": + eWeaponType = WeaponTypes.Rocket; + break; + + case "laser": + eWeaponType = WeaponTypes.Laser; + break; + + case "cannon": + // Note: this type is deprecated. behavior is duplicated with Ballistic and bulletInfo.explosive = true + // Type remains for backward compatability for now. + eWeaponType = WeaponTypes.Ballistic; + break; + } + } + + #endregion + + #region Audio + + void UpdateVolume() + { + if (audioSource) + { + audioSource.volume = BDArmorySettings.BDARMORY_WEAPONS_VOLUME; + } + if (audioSource2) + { + audioSource2.volume = BDArmorySettings.BDARMORY_WEAPONS_VOLUME; + } + if (lowpassFilter) + { + lowpassFilter.cutoffFrequency = BDArmorySettings.IVA_LOWPASS_FREQ; + } + } + + void SetupAudio() + { + fireSound = GameDatabase.Instance.GetAudioClip(fireSoundPath); + overheatSound = GameDatabase.Instance.GetAudioClip(overheatSoundPath); + if (!audioSource) + { + audioSource = gameObject.AddComponent(); + audioSource.bypassListenerEffects = true; + audioSource.minDistance = .3f; + audioSource.maxDistance = 1000; + audioSource.priority = 10; + audioSource.dopplerLevel = 0; + audioSource.spatialBlend = 1; + } + + if (!audioSource2) + { + audioSource2 = gameObject.AddComponent(); + audioSource2.bypassListenerEffects = true; + audioSource2.minDistance = .3f; + audioSource2.maxDistance = 1000; + audioSource2.dopplerLevel = 0; + audioSource2.priority = 10; + audioSource2.spatialBlend = 1; + } + + if (reloadAudioPath != string.Empty) + { + reloadAudioClip = (AudioClip)GameDatabase.Instance.GetAudioClip(reloadAudioPath); + } + if (reloadCompletePath != string.Empty) + { + reloadCompleteAudioClip = (AudioClip)GameDatabase.Instance.GetAudioClip(reloadCompletePath); + } + + if (!lowpassFilter && gameObject.GetComponents().Length == 0) + { + lowpassFilter = gameObject.AddComponent(); + lowpassFilter.cutoffFrequency = BDArmorySettings.IVA_LOWPASS_FREQ; + lowpassFilter.lowpassResonanceQ = 1f; + } + + UpdateVolume(); + } + + #endregion Audio + + #region Targeting + + void Aim() + { + //AI control + if (aiControlled && !slaved) + { + if (!targetAcquired) + { + autoFire = false; + return; + } + } + + if (!slaved && !aiControlled && (yawRange > 0 || maxPitch - minPitch > 0)) + { + //MouseControl + Vector3 mouseAim = new Vector3(Input.mousePosition.x / Screen.width, Input.mousePosition.y / Screen.height, + 0); + Ray ray = FlightCamera.fetch.mainCamera.ViewportPointToRay(mouseAim); + RaycastHit hit; + + if (Physics.Raycast(ray, out hit, maxTargetingRange, 9076737)) + { + targetPosition = hit.point; + + //aim through self vessel if occluding mouseray + + KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards(); + Part p = eva ? eva.part : hit.collider.gameObject.GetComponentInParent(); + + if (p && p.vessel && p.vessel == vessel) + { + targetPosition = ray.direction * maxTargetingRange + + FlightCamera.fetch.mainCamera.transform.position; + } + } + else + { + targetPosition = (ray.direction * (maxTargetingRange + (FlightCamera.fetch.Distance * 0.75f))) + + FlightCamera.fetch.mainCamera.transform.position; + if (visualTargetVessel != null && visualTargetVessel.loaded) + { + targetPosition = ray.direction * + Vector3.Distance(visualTargetVessel.transform.position, + FlightCamera.fetch.mainCamera.transform.position) + + FlightCamera.fetch.mainCamera.transform.position; + } + } + } + + //aim assist + Vector3 finalTarget = targetPosition; + Vector3 originalTarget = targetPosition; + Vector3 pointingDirection = fireTransforms[0].forward; + targetDistance = Vector3.Distance(finalTarget, fireTransforms[0].position); + + if ((BDArmorySettings.AIM_ASSIST || aiControlled) && eWeaponType != WeaponTypes.Laser) + { + float effectiveVelocity = bulletVelocity; + Vector3 relativeVelocity = targetVelocity - part.rb.velocity; + Quaternion.FromToRotation(targetAccelerationPrevious, targetAcceleration).ToAngleAxis(out float accelDAngle, out Vector3 accelDAxis); + Vector3 leadTarget = targetPosition; + + Vector3 RocketVelocity = part.rb.velocity + Krakensbane.GetFrameVelocityV3f(); + + int iterations = 6; + while (--iterations >= 0) + { + finalTarget = targetPosition; + float time; + if (eWeaponType == WeaponTypes.Ballistic) + { + time = (leadTarget - fireTransforms[0].position).magnitude / effectiveVelocity - (Time.fixedDeltaTime * 1.5f); + } + else + { + float a = thrust / rocketMass; + float d = (leadTarget - fireTransforms[0].position).magnitude; + // rocket vel is linear accel, so we dont have vel or time, so time is how long it would take to accel to target dist + // velocity = t*a+(t*a*(1/2t-0.5)); have a(ccel), so + //time = (-a+(sqrt a(a+8v))) / 2a + time = ((float)Math.Sqrt(a * (a + (8 * d))) - a) / (2 * a) - (Time.fixedDeltaTime * 1.5f); + RocketVelocity += (thrust / rocketMass) * time * pointingDirection; + } + + if (targetAcquired) + { + finalTarget += relativeVelocity * time; +#if DEBUG + relVelAdj = relativeVelocity * time; + var vc = finalTarget; +#endif + var accelDExtAngle = accelDAngle * time / 3; + var extrapolatedAcceleration = + Quaternion.AngleAxis(accelDExtAngle, accelDAxis) + * targetAcceleration + * Mathf.Cos(accelDExtAngle * Mathf.Deg2Rad * 2.222f); + finalTarget += 0.5f * extrapolatedAcceleration * time * time; +#if DEBUG + accAdj = (finalTarget - vc); +#endif + } + else if (Misc.Misc.GetRadarAltitudeAtPos(targetPosition) < 2000) + { + //this vessel velocity compensation against stationary + finalTarget += (-(part.rb.velocity + Krakensbane.GetFrameVelocityV3f()) * time); + } + + leadTarget = finalTarget; + + if (bulletDrop || eWeaponType == WeaponTypes.Rocket) + { +#if DEBUG + var vc = finalTarget; +#endif + Vector3 up = (VectorUtils.GetUpDirection(finalTarget) + 2 * VectorUtils.GetUpDirection(fireTransforms[0].position)).normalized; + float gAccel = ((float)FlightGlobals.getGeeForceAtPosition(finalTarget).magnitude + + (float)FlightGlobals.getGeeForceAtPosition(fireTransforms[0].position).magnitude * 2) / 3; + Vector3 intermediateTarget = finalTarget + (0.5f * gAccel * time * time * up); + + var avGrav = (FlightGlobals.getGeeForceAtPosition(finalTarget) + 2 * FlightGlobals.getGeeForceAtPosition(fireTransforms[0].position)) / 3; + effectiveVelocity = bulletVelocity + * (float)Vector3d.Dot((intermediateTarget - fireTransforms[0].position).normalized, (finalTarget - fireTransforms[0].position).normalized) + + Vector3.Project(avGrav, finalTarget - fireTransforms[0].position).magnitude * time / 2 * (Vector3.Dot(avGrav, finalTarget - fireTransforms[0].position) < 0 ? -1 : 1); + if (eWeaponType == WeaponTypes.Rocket) + { + effectiveVelocity = RocketVelocity.magnitude + * (float)Vector3d.Dot((intermediateTarget - fireTransforms[0].position).normalized, (finalTarget - fireTransforms[0].position).normalized) + + Vector3.Project(avGrav, finalTarget - fireTransforms[0].position).magnitude * time / 2 * (Vector3.Dot(avGrav, finalTarget - fireTransforms[0].position) < 0 ? -1 : 1); + } + finalTarget = intermediateTarget; +#if DEBUG + gravAdj = (finalTarget - vc); +#endif + } + } + targetDistance = Vector3.Distance(finalTarget, fireTransforms[0].position); + fixedLeadOffset = originalTarget - finalTarget; //for aiming fixed guns to moving target + + //airdetonation + if (airDetonation) + { + if (targetAcquired && airDetonationTiming) + { + //detonationRange = BlastPhysicsUtils.CalculateBlastRange(bulletInfo.tntMass); //this returns 0, use detonationRange GUI tweakable instead + defaultDetonationRange = targetDistance;// adds variable time fuze if/when proximity fuzes fail + + } + else + { + //detonationRange = defaultDetonationRange; + defaultDetonationRange = maxAirDetonationRange; //airburst at max range + } + } + }//removed the detonationange += UnityEngine.random, that gets called every frame and just causes the prox fuze range to wander + finalAimTarget = finalTarget; + + //final turret aiming + if (slaved && !targetAcquired) return; + if (turret) + { + bool origSmooth = turret.smoothRotation; + if (aiControlled || slaved) + { + turret.smoothRotation = false; + } + turret.AimToTarget(finalTarget); + turret.smoothRotation = origSmooth; + } + } + + public void RunTrajectorySimulation() + { + if (((BDArmorySettings.AIM_ASSIST || aiControlled) && eWeaponType == WeaponTypes.Rocket) || + BDArmorySettings.AIM_ASSIST && BDArmorySettings.DRAW_AIMERS && + (BDArmorySettings.DRAW_DEBUG_LINES || (vessel && vessel.isActiveVessel && !aiControlled && !MapView.MapIsEnabled && !pointingAtSelf && eWeaponType != WeaponTypes.Rocket))) + { + Transform fireTransform = fireTransforms[0]; + + if (eWeaponType == WeaponTypes.Rocket && rocketPod) + { + fireTransform = rockets[0].parent; // support for legacy RLs + } + + if (eWeaponType == WeaponTypes.Laser && + BDArmorySettings.AIM_ASSIST && BDArmorySettings.DRAW_AIMERS) + { + Ray ray = new Ray(fireTransform.position, fireTransform.forward); + RaycastHit rayHit; + if (Physics.Raycast(ray, out rayHit, maxTargetingRange, 9076737)) + { + bulletPrediction = rayHit.point; + } + else + { + bulletPrediction = ray.GetPoint(maxTargetingRange); + } + pointingAtPosition = ray.GetPoint(maxTargetingRange); + } + else if (eWeaponType == WeaponTypes.Rocket || (eWeaponType == WeaponTypes.Ballistic && BDArmorySettings.AIM_ASSIST && BDArmorySettings.DRAW_AIMERS)) + { + float simTime = 0; + Vector3 pointingDirection = fireTransform.forward; + float simDeltaTime = 0.155f; + Vector3 simVelocity = part.rb.velocity + Krakensbane.GetFrameVelocityV3f() + (bulletVelocity * fireTransform.forward); + Vector3 simCurrPos = fireTransform.position + ((part.rb.velocity + Krakensbane.GetFrameVelocityV3f()) * Time.fixedDeltaTime); + Vector3 simPrevPos = simCurrPos; + Vector3 simStartPos = simCurrPos; + if (eWeaponType == WeaponTypes.Rocket) + { + simVelocity = part.rb.velocity + Krakensbane.GetFrameVelocityV3f(); + simCurrPos = fireTransform.position + ((part.rb.velocity + Krakensbane.GetFrameVelocityV3f()) * Time.fixedDeltaTime); + simPrevPos = fireTransform.position + ((part.rb.velocity + Krakensbane.GetFrameVelocityV3f()) * Time.fixedDeltaTime); + simStartPos = fireTransform.position + ((part.rb.velocity + Krakensbane.GetFrameVelocityV3f()) * Time.fixedDeltaTime); + } + bool simulating = true; + + List pointPositions = new List(); + pointPositions.Add(simCurrPos); + + float atmosMultiplier = Mathf.Clamp01(2.5f * (float)FlightGlobals.getAtmDensity(vessel.staticPressurekPa, vessel.externalTemperature, vessel.mainBody)); + + while (simulating) + { + RaycastHit hit; + + if (eWeaponType == WeaponTypes.Rocket) + { + if (simTime > thrustTime) + { + simDeltaTime = 0.1f; + } + + if (simTime > 0.04f) + { + simDeltaTime = 0.02f; + if (simTime < thrustTime) + { + simVelocity += thrust / rocketMass * simDeltaTime * pointingDirection; + } + + //rotation (aero stabilize) + pointingDirection = Vector3.RotateTowards(pointingDirection, + simVelocity + Krakensbane.GetFrameVelocity(), + atmosMultiplier * (0.5f * (simTime)) * 50 * simDeltaTime * Mathf.Deg2Rad, 0); + } + } + + if (bulletDrop || eWeaponType == WeaponTypes.Rocket) simVelocity += FlightGlobals.getGeeForceAtPosition(simCurrPos) * simDeltaTime; + simCurrPos += simVelocity * simDeltaTime; + pointPositions.Add(simCurrPos); + + if (Physics.Raycast(simPrevPos, simCurrPos - simPrevPos, out hit, + Vector3.Distance(simPrevPos, simCurrPos), 9076737)) + { + Vessel hitVessel = null; + try + { + KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards(); + hitVessel = (eva ? eva.part : hit.collider.gameObject.GetComponentInParent()).vessel; + } + catch (NullReferenceException) + { + } + + if (hitVessel == null || (hitVessel != null && hitVessel != vessel)) + { + bulletPrediction = hit.point; + simulating = false; + } + } + + simPrevPos = simCurrPos; + if (visualTargetVessel != null && visualTargetVessel.loaded && !visualTargetVessel.Landed && + (simStartPos - simCurrPos).sqrMagnitude > targetDistance * targetDistance) + { + bulletPrediction = simStartPos + (simCurrPos - simStartPos).normalized * targetDistance; + simulating = false; + } + + if ((simStartPos - simCurrPos).sqrMagnitude > maxTargetingRange * maxTargetingRange) + { + bulletPrediction = simStartPos + ((simCurrPos - simStartPos).normalized * maxTargetingRange); + simulating = false; + } + simTime += simDeltaTime; + } + predictedFlightTime = simTime; + + if (BDArmorySettings.DRAW_DEBUG_LINES && BDArmorySettings.DRAW_AIMERS) + { + Vector3[] pointsArray = pointPositions.ToArray(); + if (gameObject.GetComponent() == null) + { + LineRenderer lr = gameObject.AddComponent(); + lr.startWidth = .1f; + lr.endWidth = .1f; + lr.positionCount = pointsArray.Length; + for (int i = 0; i < pointsArray.Length; i++) + { + lr.SetPosition(i, pointsArray[i]); + } + } + else + { + LineRenderer lr = gameObject.GetComponent(); + lr.enabled = true; + lr.positionCount = pointsArray.Length; + for (int i = 0; i < pointsArray.Length; i++) + { + lr.SetPosition(i, pointsArray[i]); + } + } + } + } + } + } + + void CheckAIAutofire() + { + //autofiring with AI + if (targetAcquired && aiControlled) + { + Transform fireTransform = fireTransforms[0]; + + Vector3 targetRelPos = (finalAimTarget) - fireTransform.position; + Vector3 aimDirection = fireTransform.forward; + float targetCosAngle = Vector3.Dot(aimDirection, targetRelPos.normalized); + + Vector3 targetDiffVec = finalAimTarget - lastFinalAimTarget; + Vector3 projectedTargetPos = targetDiffVec; + //projectedTargetPos /= TimeWarp.fixedDeltaTime; + //projectedTargetPos *= TimeWarp.fixedDeltaTime; + projectedTargetPos *= 2; //project where the target will be in 2 timesteps + projectedTargetPos += finalAimTarget; + + targetDiffVec.Normalize(); + Vector3 lastTargetRelPos = (lastFinalAimTarget) - fireTransform.position; + + if (BDATargetManager.CheckSafeToFireGuns(weaponManager, aimDirection, 1000, 0.999848f) //~1 degree of unsafe angle + && targetCosAngle >= maxAutoFireCosAngle) //check if directly on target + { + autoFire = true; + } + else + { + autoFire = false; + } + } + else + { + autoFire = false; + } + + //disable autofire after burst length + if (autoFire && Time.time - autoFireTimer > autoFireLength) + { + autoFire = false; + visualTargetVessel = null; + } + } + + public Vector3 GetLeadOffset() + { + return fixedLeadOffset; + } + + bool WMgrAuthorized() + { + MissileFire manager = BDArmorySetup.Instance.ActiveWeaponManager; + if (manager != null && manager.vessel == vessel) + { + if (manager.hasSingleFired) return false; + else return true; + } + else + { + return true; + } + } + + void CheckWeaponSafety() + { + pointingAtSelf = false; + + // While I'm not saying vessels larger than 500m are impossible, let's be practical here + const float maxCheckRange = 500f; + float checkRange = Mathf.Min(targetAcquired ? targetDistance : maxTargetingRange, maxCheckRange); + + for (int i = 0; i < fireTransforms.Length; i++) + { + Ray ray = new Ray(fireTransforms[i].position, fireTransforms[i].forward); + RaycastHit hit; + + if (Physics.Raycast(ray, out hit, maxTargetingRange, 9076737)) + { + KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards(); + Part p = eva ? eva.part : hit.collider.gameObject.GetComponentInParent(); + if (p && p.vessel && p.vessel == vessel) + { + pointingAtSelf = true; + break; + } + } + + pointingAtPosition = fireTransforms[i].position + (ray.direction * targetDistance); + } + } + + IEnumerator AimAndFireAtEndOfFrame() + { + if (eWeaponType != WeaponTypes.Laser) yield return new WaitForEndOfFrame(); + + UpdateTargetVessel(); + updateAcceleration(targetVelocity, targetPosition); + relativeVelocity = targetVelocity - vessel.rb_velocity; + + RunTrajectorySimulation(); + Aim(); + CheckWeaponSafety(); + CheckAIAutofire(); + + if (finalFire) + { + if (eWeaponType == WeaponTypes.Laser) + { + if (FireLaser()) + { + for (int i = 0; i < laserRenderers.Length; i++) + { + laserRenderers[i].enabled = true; + } + } + else + { + for (int i = 0; i < laserRenderers.Length; i++) + { + laserRenderers[i].enabled = false; + } + audioSource.Stop(); + } + } + else + { + if (!BurstFire && useRippleFire && weaponManager.gunRippleIndex != rippleIndex) + { + //timeFired = Time.time + (initialFireDelay - (60f / roundsPerMinute)) * TimeWarp.CurrentRate; + finalFire = false; + } + else + { + finalFire = true; + } + if (eWeaponType == WeaponTypes.Ballistic) + { + if (finalFire) + Fire(); + } + if (eWeaponType == WeaponTypes.Rocket) + { + if (finalFire) + FireRocket(); + } + } + if (BurstFire && (RoundsRemaining < RoundsPerMag)) + { + finalFire = true; + } + else + { + finalFire = false; + RoundsRemaining = 0; + } + } + + yield break; + } + + void DrawAlignmentIndicator() + { + if (fireTransforms == null || fireTransforms[0] == null) return; + + Transform refTransform = EditorLogic.RootPart.GetReferenceTransform(); + + if (!refTransform) return; + + Vector3 fwdPos = fireTransforms[0].position + (5 * fireTransforms[0].forward); + BDGUIUtils.DrawLineBetweenWorldPositions(fireTransforms[0].position, fwdPos, 4, Color.green); + + Vector3 referenceDirection = refTransform.up; + Vector3 refUp = -refTransform.forward; + Vector3 refRight = refTransform.right; + + Vector3 refFwdPos = fireTransforms[0].position + (5 * referenceDirection); + BDGUIUtils.DrawLineBetweenWorldPositions(fireTransforms[0].position, refFwdPos, 2, Color.white); + + BDGUIUtils.DrawLineBetweenWorldPositions(fwdPos, refFwdPos, 2, XKCDColors.Orange); + + Vector2 guiPos; + if (BDGUIUtils.WorldToGUIPos(fwdPos, out guiPos)) + { + Rect angleRect = new Rect(guiPos.x, guiPos.y, 100, 200); + + Vector3 pitchVector = (5 * Vector3.ProjectOnPlane(fireTransforms[0].forward, refRight)); + Vector3 yawVector = (5 * Vector3.ProjectOnPlane(fireTransforms[0].forward, refUp)); + + BDGUIUtils.DrawLineBetweenWorldPositions(fireTransforms[0].position + pitchVector, fwdPos, 3, + Color.white); + BDGUIUtils.DrawLineBetweenWorldPositions(fireTransforms[0].position + yawVector, fwdPos, 3, Color.white); + + float pitch = Vector3.Angle(pitchVector, referenceDirection); + float yaw = Vector3.Angle(yawVector, referenceDirection); + + string convergeDistance; + + Vector3 projAxis = Vector3.Project(refTransform.position - fireTransforms[0].transform.position, + refRight); + float xDist = projAxis.magnitude; + float convergeAngle = 90 - Vector3.Angle(yawVector, refTransform.up); + if (Vector3.Dot(fireTransforms[0].forward, projAxis) > 0) + { + convergeDistance = "Converge: " + + Mathf.Round((xDist * Mathf.Tan(convergeAngle * Mathf.Deg2Rad))).ToString() + "m"; + } + else + { + convergeDistance = "Diverging"; + } + + string xAngle = "X: " + Vector3.Angle(fireTransforms[0].forward, pitchVector).ToString("0.00"); + string yAngle = "Y: " + Vector3.Angle(fireTransforms[0].forward, yawVector).ToString("0.00"); + + GUI.Label(angleRect, xAngle + "\n" + yAngle + "\n" + convergeDistance); + } + } + + #endregion Targeting + + #region Updates + + void UpdateHeat() + { + heat = Mathf.Clamp(heat - heatLoss * TimeWarp.fixedDeltaTime, 0, Mathf.Infinity); + if (heat > maxHeat && !isOverheated) + { + isOverheated = true; + autoFire = false; + audioSource.Stop(); + wasFiring = false; + audioSource2.PlayOneShot(overheatSound); + weaponManager.ResetGuardInterval(); + } + if (heat < maxHeat / 3 && isOverheated) //reset on cooldown + { + isOverheated = false; + } + } + + void ReloadWeapon() + { + if (isReloading) + { + ReloadTimer = Mathf.Clamp((ReloadTimer + 1 * TimeWarp.fixedDeltaTime / ReloadTime), 0, 1); + } + if (RoundsRemaining >= RoundsPerMag && !isReloading) + { + isReloading = true; + autoFire = false; + audioSource.Stop(); + wasFiring = false; + weaponManager.ResetGuardInterval(); + showReloadMeter = true; + } + if (ReloadTimer >= 1 && isReloading) + { + RoundsRemaining = 0; + gauge.UpdateReloadMeter(1); + showReloadMeter = false; + isReloading = false; + ReloadTimer = 0; + if (eWeaponType == WeaponTypes.Rocket) + { + UpdateRocketScales(); + } + } + } + + void UpdateTargetVessel() + { + targetAcquired = false; + slaved = false; + bool atprWasAcquired = atprAcquired; + atprAcquired = false; + + if (weaponManager) + { + //legacy or visual range guard targeting + if (aiControlled && weaponManager && visualTargetVessel && + (visualTargetVessel.transform.position - transform.position).sqrMagnitude < weaponManager.guardRange * weaponManager.guardRange) + { + targetPosition = visualTargetVessel.CoM; + targetVelocity = visualTargetVessel.rb_velocity; + targetAcquired = true; + return; + } + + if (weaponManager.slavingTurrets && turret) + { + slaved = true; + targetPosition = weaponManager.slavedPosition; + targetVelocity = weaponManager.slavedTarget.vessel?.rb_velocity ?? (weaponManager.slavedVelocity - Krakensbane.GetFrameVelocityV3f()); + targetAcquired = true; + return; + } + + if (weaponManager.vesselRadarData && weaponManager.vesselRadarData.locked) + { + TargetSignatureData targetData = weaponManager.vesselRadarData.lockedTargetData.targetData; + targetVelocity = targetData.velocity - Krakensbane.GetFrameVelocityV3f(); + targetPosition = targetData.predictedPosition; + targetAcceleration = targetData.acceleration; + if (targetData.vessel) + { + targetVelocity = targetData.vessel?.rb_velocity ?? targetVelocity; + targetPosition = targetData.vessel.CoM; + } + targetAcquired = true; + return; + } + + //auto proxy tracking + if (vessel.isActiveVessel && autoProxyTrackRange > 0) + { + if (aptrTicker < 20) + { + aptrTicker++; + + if (atprWasAcquired) + { + targetAcquired = true; + atprAcquired = true; + } + } + else + { + aptrTicker = 0; + Vessel tgt = null; + float closestSqrDist = autoProxyTrackRange * autoProxyTrackRange; + List.Enumerator v = BDATargetManager.LoadedVessels.GetEnumerator(); + while (v.MoveNext()) + { + if (v.Current == null || !v.Current.loaded) continue; + if (!v.Current.IsControllable) continue; + if (v.Current == vessel) continue; + Vector3 targetVector = v.Current.transform.position - part.transform.position; + if (Vector3.Dot(targetVector, fireTransforms[0].forward) < 0) continue; + float sqrDist = (v.Current.transform.position - part.transform.position).sqrMagnitude; + if (sqrDist > closestSqrDist) continue; + if (Vector3.Angle(targetVector, fireTransforms[0].forward) > 20) continue; + tgt = v.Current; + closestSqrDist = sqrDist; + } + v.Dispose(); + + if (tgt == null) return; + targetAcquired = true; + atprAcquired = true; + targetPosition = tgt.CoM; + targetVelocity = tgt.rb_velocity; + } + } + } + } + + /// + /// Update target acceleration based on previous velocity. + /// Position is used to clamp acceleration for splashed targets, as ksp produces excessive bobbing. + /// + void updateAcceleration(Vector3 target_rb_velocity, Vector3 position) + { + targetAccelerationPrevious = targetAcceleration; + targetAcceleration = (target_rb_velocity - Krakensbane.GetLastCorrection() - targetVelocityPrevious) / Time.fixedDeltaTime; + float altitude = (float)FlightGlobals.currentMainBody.GetAltitude(position); + if (altitude < 12 && altitude > -10) + targetAcceleration = Vector3.ProjectOnPlane(targetAcceleration, VectorUtils.GetUpDirection(position)); + targetVelocityPrevious = target_rb_velocity; + } + + void UpdateGUIWeaponState() + { + guiStatusString = weaponState.ToString(); + } + + IEnumerator StartupRoutine() + { + weaponState = WeaponStates.PoweringUp; + UpdateGUIWeaponState(); + + if (hasDeployAnim && deployState) + { + deployState.enabled = true; + deployState.speed = 1; + while (deployState.normalizedTime < 1) //wait for animation here + { + yield return null; + } + deployState.normalizedTime = 1; + deployState.speed = 0; + deployState.enabled = false; + } + + weaponState = WeaponStates.Enabled; + UpdateGUIWeaponState(); + BDArmorySetup.Instance.UpdateCursorState(); + } + + IEnumerator ShutdownRoutine() + { + weaponState = WeaponStates.PoweringDown; + UpdateGUIWeaponState(); + BDArmorySetup.Instance.UpdateCursorState(); + if (turret) + { + yield return new WaitForSeconds(0.2f); + + while (!turret.ReturnTurret()) //wait till turret has returned + { + yield return new WaitForFixedUpdate(); + } + } + + if (hasDeployAnim) + { + deployState.enabled = true; + deployState.speed = -1; + while (deployState.normalizedTime > 0) + { + yield return null; + } + deployState.normalizedTime = 0; + deployState.speed = 0; + deployState.enabled = false; + } + + weaponState = WeaponStates.Disabled; + UpdateGUIWeaponState(); + } + + void StopShutdownStartupRoutines() + { + if (shutdownRoutine != null) + { + StopCoroutine(shutdownRoutine); + shutdownRoutine = null; + } + + if (startupRoutine != null) + { + StopCoroutine(startupRoutine); + startupRoutine = null; + } + } + + #endregion Updates + + #region Bullets + + void ParseBulletDragType() + { + bulletDragTypeName = bulletDragTypeName.ToLower(); + + switch (bulletDragTypeName) + { + case "none": + bulletDragType = BulletDragTypes.None; + break; + + case "numericalintegration": + bulletDragType = BulletDragTypes.NumericalIntegration; + break; + + case "analyticestimate": + bulletDragType = BulletDragTypes.AnalyticEstimate; + break; + } + } + + void SetupBulletPool() + { + GameObject templateBullet = new GameObject("Bullet"); + templateBullet.AddComponent(); + templateBullet.SetActive(false); + bulletPool = ObjectPool.CreateObjectPool(templateBullet, 100, true, true); + } + + void SetupShellPool() + { + GameObject templateShell = + (GameObject)Instantiate(GameDatabase.Instance.GetModel("BDArmory/Models/shell/model")); + templateShell.SetActive(false); + templateShell.AddComponent(); + shellPool = ObjectPool.CreateObjectPool(templateShell, 50, true, true); + } + + void SetupBullet() + { + bulletInfo = BulletInfo.bullets[bulletType]; + if (bulletType != "def") + { + //use values from bullets.cfg if not the Part Module defaults are used + caliber = bulletInfo.caliber; + bulletVelocity = bulletInfo.bulletVelocity; + bulletMass = bulletInfo.bulletMass; + bulletDragTypeName = bulletInfo.bulletDragTypeName; + cannonShellHeat = bulletInfo.blastHeat; + cannonShellPower = bulletInfo.blastPower; + cannonShellRadius = bulletInfo.blastRadius; + } + ParseBulletDragType(); + } + + protected void SetInitialDetonationDistance() + { + if (this.detonationRange == -1) + { + if (eWeaponType == WeaponTypes.Ballistic && (bulletInfo.tntMass != 0 && (proximityDetonation || airDetonation))) + { + detonationRange = (BlastPhysicsUtils.CalculateBlastRange(bulletInfo.tntMass) * 0.66f); + } + else if (eWeaponType == WeaponTypes.Rocket && blastForce != 0) + { + detonationRange = (BlastPhysicsUtils.CalculateBlastRange(blastForce) * 0.66f); + //should really update rockets to use tntmass instead. + } + else + { + detonationRange = 0f; + proximityDetonation = false; + } + } + if (BDArmorySettings.DRAW_DEBUG_LABELS) + { + Debug.Log("[BDArmory]: DetonationDistance = : " + detonationRange); + } + } + + #endregion Bullets + + #region RMB Info + + public override string GetInfo() + { + BulletInfo binfo = BulletInfo.bullets[bulletType]; + StringBuilder output = new StringBuilder(); + output.Append(Environment.NewLine); + output.AppendLine($"Weapon Type: {weaponType}"); + + if (weaponType == "laser") + { + output.AppendLine($"Laser damage: {laserDamage}"); + } + else + { + output.AppendLine($"Rounds Per Minute: {roundsPerMinute * (fireTransforms?.Length ?? 1)}"); + output.AppendLine($"Ammunition: {ammoName}"); + if (weaponType == "ballistic") + { + output.AppendLine($"Bullet type: {bulletType}"); + output.AppendLine($"Bullet mass: {Math.Round(binfo.bulletMass, 2)} kg"); + output.AppendLine($"Muzzle velocity: {Math.Round(binfo.bulletVelocity, 2)} m/s"); + } + output.AppendLine($"Max Range: {maxEffectiveDistance} m"); + if (BurstFire) + { + output.AppendLine($"Burst Fire Weapon"); + output.AppendLine($" - Rounds Per Burst: {RoundsPerMag}"); + } + if (weaponType == "ballistic") + { + if (!BeltFed) + { + output.AppendLine($"Magazine-Fed"); + output.AppendLine($" - Rounds Per Magazine: {RoundsPerMag}"); + output.AppendLine($" - Reload Time: {ReloadTime}"); + } + output.AppendLine($"Explosive: {binfo.explosive}"); + if (binfo.explosive) + { + output.AppendLine($"Blast:"); + output.AppendLine($"- tnt mass: {Math.Round((binfo.tntMass > 0 ? binfo.tntMass : binfo.blastPower), 2)} kg"); + output.AppendLine($"- radius: {Math.Round(BlastPhysicsUtils.CalculateBlastRange(binfo.tntMass), 2)} m"); + output.AppendLine($"Air detonation: {airDetonation}"); + if (airDetonation) + { + output.AppendLine($"- auto timing: {airDetonationTiming}"); + output.AppendLine($"- max range: {maxAirDetonationRange} m"); + } + } + } + if (weaponType == "rocket") + { + if (externalAmmo) + { + output.AppendLine($"Uses External Ammo"); + } + if(!BeltFed) + { + output.AppendLine($" Reloadable"); + output.AppendLine($" - Shots before Reload: {RoundsPerMag}"); + output.AppendLine($" - Reload Time: {ReloadTime}"); + } + //output.AppendLine($"Rocket mass: {rocketMass} kg"); + //output.AppendLine($"Thrust: {thrust}kn"); mass and thrust don't really tell us the important bit, so lets replace that with accel + output.AppendLine($"Acceleration: {thrust / rocketMass}m/s2"); + output.AppendLine($"Blast:"); + output.AppendLine($"- radius: {blastRadius}"); + output.AppendLine($"- power: {blastForce}"); + output.AppendLine($"Proximity Fuzed: {proximityDetonation}"); + if (proximityDetonation) + { + output.AppendLine($"- fuze radius: {(blastRadius * .66f)} m"); + } + } + if (crewserved) + { + output.AppendLine($"Crew-served Weapon - Requires onboard Kerbal"); + } + } + return output.ToString(); + } + + #endregion RMB Info + } + + #region UI //borrowing code from ModularMissile GUI + + [KSPAddon(KSPAddon.Startup.EditorAny, false)] + public class WeaponGroupWindow : MonoBehaviour + { + internal static EventVoid OnActionGroupEditorOpened = new EventVoid("OnActionGroupEditorOpened"); + internal static EventVoid OnActionGroupEditorClosed = new EventVoid("OnActionGroupEditorClosed"); + + private static GUIStyle unchanged; + private static GUIStyle changed; + private static GUIStyle greyed; + private static GUIStyle overfull; + + private static WeaponGroupWindow instance; + private static Vector3 mousePos = Vector3.zero; + + private bool ActionGroupMode; + + private Rect guiWindowRect = new Rect(0, 0, 0, 0); + + private ModuleWeapon WPNmodule; + + [KSPField] public int offsetGUIPos = -1; + + private Vector2 scrollPos; + + [KSPField(isPersistant = false, guiActiveEditor = true, guiActive = false, guiName = "#LOC_BDArmory_ShowGroupEditor"), UI_Toggle(enabledText = "#LOC_BDArmory_ShowGroupEditor_enabledText", disabledText = "#LOC_BDArmory_ShowGroupEditor_disabledText")] [NonSerialized] public bool showRFGUI;//Show Group Editor--close Group GUI--open Group GUI + + private bool styleSetup; + + private string txtName = string.Empty; + + public static void HideGUI() + { + if (instance != null && instance.WPNmodule != null) + { + instance.WPNmodule.WeaponName = instance.WPNmodule.shortName; + instance.WPNmodule = null; + instance.UpdateGUIState(); + } + EditorLogic editor = EditorLogic.fetch; + if (editor != null) + editor.Unlock("BD_MN_GUILock"); + } + + public static void ShowGUI(ModuleWeapon WPNmodule) + { + if (instance != null) + { + instance.WPNmodule = WPNmodule; + instance.UpdateGUIState(); + } + } + + private void UpdateGUIState() + { + enabled = WPNmodule != null; + EditorLogic editor = EditorLogic.fetch; + if (!enabled && editor != null) + editor.Unlock("BD_MN_GUILock"); + } + + private IEnumerator CheckActionGroupEditor() + { + while (EditorLogic.fetch == null) + { + yield return null; + } + EditorLogic editor = EditorLogic.fetch; + while (EditorLogic.fetch != null) + { + if (editor.editorScreen == EditorScreen.Actions) + { + if (!ActionGroupMode) + { + HideGUI(); + OnActionGroupEditorOpened.Fire(); + } + EditorActionGroups age = EditorActionGroups.Instance; + if (WPNmodule && !age.GetSelectedParts().Contains(WPNmodule.part)) + { + HideGUI(); + } + ActionGroupMode = true; + } + else + { + if (ActionGroupMode) + { + HideGUI(); + OnActionGroupEditorClosed.Fire(); + } + ActionGroupMode = false; + } + yield return null; } - } - - #endregion Bullets - - #region RMB Info - - public override string GetInfo() - { - BulletInfo binfo = BulletInfo.bullets[bulletType]; - StringBuilder output = new StringBuilder(); - output.Append(Environment.NewLine); - output.AppendLine($"Weapon Type: {weaponType}"); - - if (weaponType == "laser") - { - output.AppendLine($"Laser damage: {laserDamage}"); - } - else - { - output.AppendLine($"Rounds Per Minute: {roundsPerMinute * (fireTransforms?.Length ?? 1)}"); - output.AppendLine($"Ammunition: {ammoName}"); - output.AppendLine($"Bullet type: {bulletType}"); - output.AppendLine($"Bullet mass: {Math.Round(binfo.bulletMass, 2)} kg"); - output.AppendLine($"Muzzle velocity: {Math.Round(binfo.bulletVelocity, 2)} m/s"); - output.AppendLine($"Max Range: {maxEffectiveDistance} m"); - if (weaponType == "cannon" || weaponType == "ballistic") - { - output.AppendLine($"Explosive: {binfo.explosive}"); - if (binfo.explosive) - { - output.AppendLine($"Blast:"); - output.AppendLine($"- tnt mass: {Math.Round((binfo.tntMass > 0 ? binfo.tntMass : binfo.blastPower), 2)} kg"); - output.AppendLine($"- radius: {Math.Round(BlastPhysicsUtils.CalculateBlastRange(binfo.tntMass), 2)} m"); - output.AppendLine($"Air detonation: {airDetonation}"); - if (airDetonation) - { - output.AppendLine($"- auto timing: {airDetonationTiming}"); - output.AppendLine($"- max range: {maxAirDetonationRange} m"); - } - } - } - } - return output.ToString(); - } - - #endregion RMB Info - } - - #region UI //borrowing code from ModularMissile GUI - - [KSPAddon(KSPAddon.Startup.EditorAny, false)] - public class WeaponGroupWindow : MonoBehaviour - { - internal static EventVoid OnActionGroupEditorOpened = new EventVoid("OnActionGroupEditorOpened"); - internal static EventVoid OnActionGroupEditorClosed = new EventVoid("OnActionGroupEditorClosed"); - - private static GUIStyle unchanged; - private static GUIStyle changed; - private static GUIStyle greyed; - private static GUIStyle overfull; - - private static WeaponGroupWindow instance; - private static Vector3 mousePos = Vector3.zero; - - private bool ActionGroupMode; - - private Rect guiWindowRect = new Rect(0, 0, 0, 0); - - private ModuleWeapon WPNmodule; - - [KSPField] public int offsetGUIPos = -1; - - private Vector2 scrollPos; - - [KSPField(isPersistant = false, guiActiveEditor = true, guiActive = false, guiName = "#LOC_BDArmory_ShowGroupEditor"), UI_Toggle(enabledText = "#LOC_BDArmory_ShowGroupEditor_enabledText", disabledText = "#LOC_BDArmory_ShowGroupEditor_disabledText")] [NonSerialized] public bool showRFGUI;//Show Group Editor--close Group GUI--open Group GUI - - private bool styleSetup; - - private string txtName = string.Empty; - - public static void HideGUI() - { - if (instance != null && instance.WPNmodule != null) - { - instance.WPNmodule.WeaponName = instance.WPNmodule.shortName; - instance.WPNmodule = null; - instance.UpdateGUIState(); - } - EditorLogic editor = EditorLogic.fetch; - if (editor != null) - editor.Unlock("BD_MN_GUILock"); - } - - public static void ShowGUI(ModuleWeapon WPNmodule) - { - if (instance != null) - { - instance.WPNmodule = WPNmodule; - instance.UpdateGUIState(); - } - } - - private void UpdateGUIState() - { - enabled = WPNmodule != null; - EditorLogic editor = EditorLogic.fetch; - if (!enabled && editor != null) - editor.Unlock("BD_MN_GUILock"); - } - - private IEnumerator CheckActionGroupEditor() - { - while (EditorLogic.fetch == null) - { - yield return null; - } - EditorLogic editor = EditorLogic.fetch; - while (EditorLogic.fetch != null) - { - if (editor.editorScreen == EditorScreen.Actions) - { - if (!ActionGroupMode) - { - HideGUI(); - OnActionGroupEditorOpened.Fire(); - } - EditorActionGroups age = EditorActionGroups.Instance; - if (WPNmodule && !age.GetSelectedParts().Contains(WPNmodule.part)) - { - HideGUI(); - } - ActionGroupMode = true; - } - else - { - if (ActionGroupMode) - { - HideGUI(); - OnActionGroupEditorClosed.Fire(); - } - ActionGroupMode = false; - } - yield return null; - } - } - - private void Awake() - { - enabled = false; - instance = this; - } - - private void OnDestroy() - { - instance = null; - } - - public void OnGUI() - { - if (!styleSetup) - { - styleSetup = true; - Styles.InitStyles(); - } - - EditorLogic editor = EditorLogic.fetch; - if (!HighLogic.LoadedSceneIsEditor || !editor) - { - return; - } - bool cursorInGUI = false; // nicked the locking code from Ferram - mousePos = Input.mousePosition; //Mouse location; based on Kerbal Engineer Redux code - mousePos.y = Screen.height - mousePos.y; - - int posMult = 0; - if (offsetGUIPos != -1) - { - posMult = offsetGUIPos; - } - if (ActionGroupMode) - { - if (guiWindowRect.width == 0) - { - guiWindowRect = new Rect(430 * posMult, 365, 438, 50); - } - new Rect(guiWindowRect.xMin + 440, mousePos.y - 5, 300, 20); - } - else - { - if (guiWindowRect.width == 0) - { - //guiWindowRect = new Rect(Screen.width - 8 - 430 * (posMult + 1), 365, 438, (Screen.height - 365)); - guiWindowRect = new Rect(Screen.width - 8 - 430 * (posMult + 1), 365, 438, 50); - } - new Rect(guiWindowRect.xMin - (230 - 8), mousePos.y - 5, 220, 20); - } - cursorInGUI = guiWindowRect.Contains(mousePos); - if (cursorInGUI) - { - editor.Lock(false, false, false, "BD_MN_GUILock"); - //if (EditorTooltip.Instance != null) - // EditorTooltip.Instance.HideToolTip(); - } - else - { - editor.Unlock("BD_MN_GUILock"); - } - guiWindowRect = GUILayout.Window(GetInstanceID(), guiWindowRect, GUIWindow, "Weapon Group GUI", Styles.styleEditorPanel); - } - - public void GUIWindow(int windowID) - { - InitializeStyles(); - - GUILayout.BeginVertical(); - GUILayout.Space(20); - - GUILayout.BeginHorizontal(); - - GUILayout.Label("Add to Weapon Group: "); - - txtName = GUILayout.TextField(txtName); - - if (GUILayout.Button("Save & Close")) - { - string newName = string.IsNullOrEmpty(txtName.Trim()) ? WPNmodule.OriginalShortName : txtName.Trim(); - - WPNmodule.WeaponName = newName; - WPNmodule.shortName = newName; - instance.WPNmodule.HideUI(); - } - - GUILayout.EndHorizontal(); - - scrollPos = GUILayout.BeginScrollView(scrollPos); - - GUILayout.EndScrollView(); - - GUILayout.EndVertical(); - - GUI.DragWindow(); - BDGUIUtils.RepositionWindow(ref guiWindowRect); - } - - private static void InitializeStyles() - { - if (unchanged == null) - { - if (GUI.skin == null) - { - unchanged = new GUIStyle(); - changed = new GUIStyle(); - greyed = new GUIStyle(); - overfull = new GUIStyle(); - } - else - { - unchanged = new GUIStyle(GUI.skin.textField); - changed = new GUIStyle(GUI.skin.textField); - greyed = new GUIStyle(GUI.skin.textField); - overfull = new GUIStyle(GUI.skin.label); - } - - unchanged.normal.textColor = Color.white; - unchanged.active.textColor = Color.white; - unchanged.focused.textColor = Color.white; - unchanged.hover.textColor = Color.white; - - changed.normal.textColor = Color.yellow; - changed.active.textColor = Color.yellow; - changed.focused.textColor = Color.yellow; - changed.hover.textColor = Color.yellow; - - greyed.normal.textColor = Color.gray; - - overfull.normal.textColor = Color.red; - } - } - } - - #endregion UI //borrowing code from ModularMissile GUI + } + + private void Awake() + { + enabled = false; + instance = this; + } + + private void OnDestroy() + { + instance = null; + } + + public void OnGUI() + { + if (!styleSetup) + { + styleSetup = true; + Styles.InitStyles(); + } + + EditorLogic editor = EditorLogic.fetch; + if (!HighLogic.LoadedSceneIsEditor || !editor) + { + return; + } + bool cursorInGUI = false; // nicked the locking code from Ferram + mousePos = Input.mousePosition; //Mouse location; based on Kerbal Engineer Redux code + mousePos.y = Screen.height - mousePos.y; + + int posMult = 0; + if (offsetGUIPos != -1) + { + posMult = offsetGUIPos; + } + if (ActionGroupMode) + { + if (guiWindowRect.width == 0) + { + guiWindowRect = new Rect(430 * posMult, 365, 438, 50); + } + new Rect(guiWindowRect.xMin + 440, mousePos.y - 5, 300, 20); + } + else + { + if (guiWindowRect.width == 0) + { + //guiWindowRect = new Rect(Screen.width - 8 - 430 * (posMult + 1), 365, 438, (Screen.height - 365)); + guiWindowRect = new Rect(Screen.width - 8 - 430 * (posMult + 1), 365, 438, 50); + } + new Rect(guiWindowRect.xMin - (230 - 8), mousePos.y - 5, 220, 20); + } + cursorInGUI = guiWindowRect.Contains(mousePos); + if (cursorInGUI) + { + editor.Lock(false, false, false, "BD_MN_GUILock"); + //if (EditorTooltip.Instance != null) + // EditorTooltip.Instance.HideToolTip(); + } + else + { + editor.Unlock("BD_MN_GUILock"); + } + guiWindowRect = GUILayout.Window(GetInstanceID(), guiWindowRect, GUIWindow, "Weapon Group GUI", Styles.styleEditorPanel); + } + + public void GUIWindow(int windowID) + { + InitializeStyles(); + + GUILayout.BeginVertical(); + GUILayout.Space(20); + + GUILayout.BeginHorizontal(); + + GUILayout.Label("Add to Weapon Group: "); + + txtName = GUILayout.TextField(txtName); + + if (GUILayout.Button("Save & Close")) + { + string newName = string.IsNullOrEmpty(txtName.Trim()) ? WPNmodule.OriginalShortName : txtName.Trim(); + + WPNmodule.WeaponName = newName; + WPNmodule.shortName = newName; + instance.WPNmodule.HideUI(); + } + + GUILayout.EndHorizontal(); + + scrollPos = GUILayout.BeginScrollView(scrollPos); + + GUILayout.EndScrollView(); + + GUILayout.EndVertical(); + + GUI.DragWindow(); + if (BDArmorySettings.STRICT_WINDOW_BOUNDARIES) + { + BDGUIUtils.RepositionWindow(ref guiWindowRect); + } + + } + + private static void InitializeStyles() + { + if (unchanged == null) + { + if (GUI.skin == null) + { + unchanged = new GUIStyle(); + changed = new GUIStyle(); + greyed = new GUIStyle(); + overfull = new GUIStyle(); + } + else + { + unchanged = new GUIStyle(GUI.skin.textField); + changed = new GUIStyle(GUI.skin.textField); + greyed = new GUIStyle(GUI.skin.textField); + overfull = new GUIStyle(GUI.skin.label); + } + + unchanged.normal.textColor = Color.white; + unchanged.active.textColor = Color.white; + unchanged.focused.textColor = Color.white; + unchanged.hover.textColor = Color.white; + + changed.normal.textColor = Color.yellow; + changed.active.textColor = Color.yellow; + changed.focused.textColor = Color.yellow; + changed.hover.textColor = Color.yellow; + + greyed.normal.textColor = Color.gray; + + overfull.normal.textColor = Color.red; + } + } + } + + #endregion UI //borrowing code from ModularMissile GUI } diff --git a/BDArmory/Modules/ModuleWingCommander.cs b/BDArmory/Modules/ModuleWingCommander.cs index 45afe714e..6dc176d09 100644 --- a/BDArmory/Modules/ModuleWingCommander.cs +++ b/BDArmory/Modules/ModuleWingCommander.cs @@ -5,6 +5,7 @@ using BDArmory.Misc; using BDArmory.Parts; using BDArmory.UI; +using BDArmory.Core; using UniLinq; using UnityEngine; using KSP.Localization; @@ -378,7 +379,10 @@ void WingmenWindow(int windowID) height += ((commandButtonLine - 1) * (buttonHeight + buttonGap)); BDArmorySetup.WindowRectWingCommander.height = height; GUI.DragWindow(BDArmorySetup.WindowRectWingCommander); - BDGUIUtils.RepositionWindow(ref BDArmorySetup.WindowRectWingCommander); + if (BDArmorySettings.STRICT_WINDOW_BOUNDARIES) + { + BDGUIUtils.RepositionWindow(ref BDArmorySetup.WindowRectWingCommander); + } } void WingmanButton(int index, out float buttonEndY) diff --git a/BDArmory/Modules/RadarWarningReceiver.cs b/BDArmory/Modules/RadarWarningReceiver.cs index de5c7e8bb..1683c5225 100644 --- a/BDArmory/Modules/RadarWarningReceiver.cs +++ b/BDArmory/Modules/RadarWarningReceiver.cs @@ -425,9 +425,11 @@ internal void WindowRwr(int windowID) BDArmorySetup.ResizeRwrWindow(BDArmorySettings.RWR_WINDOW_SCALE); } } - // End Resizing code. - - BDGUIUtils.RepositionWindow(ref BDArmorySetup.WindowRectRwr); + // End Resizing code. + if (BDArmorySettings.STRICT_WINDOW_BOUNDARIES) + { + BDGUIUtils.RepositionWindow(ref BDArmorySetup.WindowRectRwr); + } } internal static void UpdateRWRScale(float diff) diff --git a/BDArmory/Modules/RocketLauncher.cs b/BDArmory/Modules/RocketLauncher.cs index 2d580b69e..385be0fd0 100644 --- a/BDArmory/Modules/RocketLauncher.cs +++ b/BDArmory/Modules/RocketLauncher.cs @@ -13,1068 +13,340 @@ namespace BDArmory.Modules { - public class RocketLauncher : EngageableWeapon, IBDWeapon - { - public bool hasRocket = true; - - [KSPField(isPersistant = false)] public string rocketType; - - [KSPField(isPersistant = false)] public string rocketModelPath; - - [KSPField(isPersistant = false)] public float rocketMass; - - [KSPField(isPersistant = false)] public float thrust; - - [KSPField(isPersistant = false)] public float thrustTime; - - [KSPField(isPersistant = false)] public float blastRadius; - - [KSPField(isPersistant = false)] public float blastForce; - - [KSPField] public float blastHeat = -1; - - [KSPField(isPersistant = false)] public bool descendingOrder = true; - - [KSPField(isPersistant = false)] public string explModelPath = "BDArmory/Models/explosion/explosion"; - - [KSPField(isPersistant = false)] public string explSoundPath = "BDArmory/Sounds/explode1"; - - [KSPField] public float thrustDeviation = 0.10f; - - [KSPField] public float maxTargetingRange = 8000; - float currentTgtRange = 8000; - float predictedFlightTime = 1; - - public bool drawAimer; - - Vector3 rocketPrediction = Vector3.zero; - Texture2D aimerTexture; - - Transform[] rockets; - - public AudioSource sfAudioSource; - private BDStagingAreaGauge gauge; - - //animation - [KSPField] public string deployAnimationName; - [KSPField] public float deployAnimationSpeed = 1; - AnimationState deployAnimState; - bool hasDeployAnimation; - public bool deployed; - Coroutine deployAnimRoutine; - - public bool readyToFire = true; - - public Vessel legacyGuardTarget = null; - public float lastAutoFiredTime; - public float autoRippleRate = 0; - public float autoFireStartTime = 0; - public float autoFireDuration = 0; - - //turret - [KSPField] public int turretID = 0; - public ModuleTurret turret; - Vector3 trajectoryOffset = Vector3.zero; - public MissileFire weaponManager; - bool targetInTurretView = true; - - public float yawRange - { - get { return turret ? turret.yawRange : 0; } - } - - public float maxPitch - { - get { return turret ? turret.maxPitch : 0; } - } - - public float minPitch - { - get { return turret ? turret.minPitch : 0; } - } - - Vector3 targetPosition; - public Vector3? FiringSolutionVector => targetPosition.IsZero() ? (Vector3?)null : (targetPosition - rockets[0].parent.transform.position).normalized; - - double lastRocketsLeft; - double rocketsMax; - - //weapon interface - public Part GetPart() - { - return part; - } - - public WeaponClasses GetWeaponClass() - { - return WeaponClasses.Rocket; - } - - public string GetSubLabel() - { - return string.Empty; - } - - public string GetMissileType() - { - return string.Empty; - } - - [KSPAction("Fire")] - public void AGFire(KSPActionParam param) - { - FireRocket(); - } - - [KSPEvent(guiActive = true, guiName = "#LOC_BDArmory_Fire", active = true)]//Fire - public void GuiFire() - { - FireRocket(); - } - - [KSPEvent(guiActive = true, guiName = "#LOC_BDArmory_Jettison", active = true, guiActiveEditor = false)]//Jettison - public void Jettison() - { - if (turret) - { - return; - } - - part.decouple(0); - if (BDArmorySetup.Instance.ActiveWeaponManager != null) - BDArmorySetup.Instance.ActiveWeaponManager.UpdateList(); - } - - [KSPEvent(guiActive = false, guiName = "#LOC_BDArmory_ToggleTurret", guiActiveEditor = false)]//Toggle Turret - public void ToggleTurret() - { - if (deployed) - { - DisableTurret(); - } - else - { - EnableTurret(); - } - } - - public void EnableTurret() - { - deployed = true; - drawAimer = true; - hasReturned = false; - - if (returnRoutine != null) - { - StopCoroutine(returnRoutine); - returnRoutine = null; - } - - if (hasDeployAnimation) - { - if (deployAnimRoutine != null) - { - StopCoroutine(deployAnimRoutine); - } - deployAnimRoutine = StartCoroutine(DeployAnimRoutine(true)); - } - else - { - readyToFire = true; - } - } - - public void DisableTurret() - { - deployed = false; - readyToFire = false; - drawAimer = false; - hasReturned = false; - targetInTurretView = false; - - if (returnRoutine != null) - { - StopCoroutine(returnRoutine); - } - returnRoutine = StartCoroutine(ReturnRoutine()); - - if (hasDeployAnimation) - { - if (deployAnimRoutine != null) - { - StopCoroutine(deployAnimRoutine); - } - - deployAnimRoutine = StartCoroutine(DeployAnimRoutine(false)); - } - } - - bool hasReturned = true; - Coroutine returnRoutine; - - IEnumerator ReturnRoutine() - { - if (deployed) - { - hasReturned = false; - yield break; - } - - yield return new WaitForSeconds(0.25f); - - while (!turret.ReturnTurret()) - { - yield return new WaitForFixedUpdate(); - } - - hasReturned = true; - } - - void SetupAudio() - { - sfAudioSource = gameObject.AddComponent(); - sfAudioSource.minDistance = 1; - sfAudioSource.maxDistance = 2000; - sfAudioSource.dopplerLevel = 0; - sfAudioSource.priority = 230; - sfAudioSource.spatialBlend = 1; - - UpdateVolume(); - BDArmorySetup.OnVolumeChange += UpdateVolume; - } - - void UpdateVolume() - { - if (sfAudioSource) - { - sfAudioSource.volume = BDArmorySettings.BDARMORY_WEAPONS_VOLUME; - } - } - - public void Start() - { - // extension for feature_engagementenvelope - InitializeEngagementRange(0, maxTargetingRange); - - SetupAudio(); - - if (HighLogic.LoadedSceneIsFlight) - { - part.force_activate(); - - aimerTexture = BDArmorySetup.Instance.greenPointCircleTexture; - // GameDatabase.Instance.GetTexture("BDArmory/Textures/grayCircle", false); - - MakeRocketArray(); - UpdateRocketScales(); - - if (shortName == string.Empty) - { - shortName = part.partInfo.title; - } - - UpdateAudio(); - BDArmorySetup.OnVolumeChange += UpdateAudio; - - gauge = (BDStagingAreaGauge)part.AddModule("BDStagingAreaGauge"); - gauge.AmmoName = rocketType; - gauge.AudioSource = sfAudioSource; - } - - if (HighLogic.LoadedSceneIsFlight || HighLogic.LoadedSceneIsEditor) - { - List.Enumerator turr = part.FindModulesImplementing().GetEnumerator(); - while (turr.MoveNext()) - { - if (turr.Current == null) continue; - if (turr.Current.turretID != turretID) continue; - turret = turr.Current; - targetInTurretView = false; - break; - } - turr.Dispose(); - - if (turret) - { - Events["GuiFire"].guiActive = false; - Events["Jettison"].guiActive = false; - Actions["AGFire"].active = false; - - if (HighLogic.LoadedSceneIsFlight) - { - Events["ToggleTurret"].guiActive = true; - } - } - - if (!string.IsNullOrEmpty(deployAnimationName)) - { - deployAnimState = Misc.Misc.SetUpSingleAnimation(deployAnimationName, part); - hasDeployAnimation = true; - - readyToFire = false; - } - } - - blastForce = BlastPhysicsUtils.CalculateExplosiveMass(blastRadius); - } - - IEnumerator DeployAnimRoutine(bool forward) - { - readyToFire = false; - BDArmorySetup.Instance.UpdateCursorState(); - - if (forward) - { - while (deployAnimState.normalizedTime < 1) - { - deployAnimState.speed = deployAnimationSpeed; - yield return null; - } - - deployAnimState.normalizedTime = 1; - } - else - { - while (!hasReturned) - { - deployAnimState.speed = 0; - yield return null; - } - - while (deployAnimState.normalizedTime > 0) - { - deployAnimState.speed = -deployAnimationSpeed; - yield return null; - } - - deployAnimState.normalizedTime = 0; - } - - deployAnimState.speed = 0; - - readyToFire = deployed; - BDArmorySetup.Instance.UpdateCursorState(); - } - - void UpdateAudio() - { - if (sfAudioSource) - { - sfAudioSource.volume = BDArmorySettings.BDARMORY_WEAPONS_VOLUME; - } - } - - void OnDestroy() - { - BDArmorySetup.OnVolumeChange -= UpdateAudio; - } - - public void FixedUpdate() - { - if (!HighLogic.LoadedSceneIsFlight || !vessel.IsControllable) - { - return; - } - - SimulateTrajectory(); - - currentTgtRange = maxTargetingRange; - - if (deployed && readyToFire && (turret || weaponManager?.AI?.pilotEnabled == true)) - { - Aim(); - } - else - { - targetPosition = Vector3.zero; - } - } - - public void Update() - { - if (HighLogic.LoadedSceneIsFlight && !vessel.packed) - { - if (readyToFire && deployed) - { - if (returnRoutine != null) - { - StopCoroutine(returnRoutine); - returnRoutine = null; - } - - if (weaponManager && weaponManager.guardMode && weaponManager.selectedWeaponString == GetShortName()) - { - if (Time.time - autoFireStartTime < autoFireDuration) - { - float fireInterval = 0.5f; - if (autoRippleRate > 0) fireInterval = 60f / autoRippleRate; - if (Time.time - lastAutoFiredTime > fireInterval) - { - FireRocket(); - lastAutoFiredTime = Time.time; - } - } - } - else if ((!weaponManager || - (weaponManager.selectedWeaponString == GetShortName() && !weaponManager.guardMode))) - { - if (BDInputUtils.GetKeyDown(BDInputSettingsFields.WEAP_FIRE_KEY) && - (vessel.isActiveVessel || BDArmorySettings.REMOTE_SHOOTING)) - { - FireRocket(); - } - } - } - - if (GetRocketResource().amount != lastRocketsLeft) - { - UpdateRocketScales(); - lastRocketsLeft = GetRocketResource().amount; - } - - if (vessel.isActiveVessel) - { - gauge.UpdateAmmoMeter((float)(lastRocketsLeft / rocketsMax)); - } - } - } - - bool mouseAiming; - - void Aim() - { - mouseAiming = false; - if (weaponManager && (weaponManager.slavingTurrets || weaponManager.guardMode || weaponManager.AI?.pilotEnabled == true)) - { - SlavedAim(); - } - else - { - if (vessel.isActiveVessel || BDArmorySettings.REMOTE_SHOOTING) - { - MouseAim(); - } - } - } - - void SlavedAim() - { - Vector3 targetVel; - Vector3 targetAccel; - if (weaponManager.slavingTurrets) - { - targetPosition = weaponManager.slavedPosition; - targetVel = weaponManager.slavedVelocity; - targetAccel = weaponManager.slavedAcceleration; - - //targetPosition -= vessel.Velocity * predictedFlightTime; - } - else if (legacyGuardTarget) - { - targetPosition = legacyGuardTarget.CoM; - targetVel = legacyGuardTarget.Velocity(); - targetAccel = legacyGuardTarget.acceleration; - } - else - { - targetInTurretView = false; - return; - } - - currentTgtRange = Vector3.Distance(targetPosition, rockets[0].parent.transform.position); - - targetPosition += trajectoryOffset; - targetPosition += targetVel * predictedFlightTime; - targetPosition += 0.5f * targetAccel * predictedFlightTime * predictedFlightTime; - - turret.AimToTarget(targetPosition); - targetInTurretView = turret.TargetInRange(targetPosition, 2, maxTargetingRange); - } - - void MouseAim() - { - mouseAiming = true; - Vector3 targetPosition; - // float maxTargetingRange = 8000; - - float targetDistance; - - //MouseControl - Vector3 mouseAim = new Vector3(Input.mousePosition.x / Screen.width, Input.mousePosition.y / Screen.height, 0); - Ray ray = FlightCamera.fetch.mainCamera.ViewportPointToRay(mouseAim); - RaycastHit hit; - if (Physics.Raycast(ray, out hit, maxTargetingRange, 557057)) - { - targetPosition = hit.point; - - //aim through self vessel if occluding mouseray - Part p = hit.collider.gameObject.GetComponentInParent(); - if (p && p.vessel && p.vessel == vessel) - { - targetPosition = ray.direction * maxTargetingRange + FlightCamera.fetch.mainCamera.transform.position; - } - - targetDistance = Vector3.Distance(hit.point, rockets[0].parent.position); - } - else - { - targetPosition = (ray.direction * (maxTargetingRange + (FlightCamera.fetch.Distance * 0.75f))) + - FlightCamera.fetch.mainCamera.transform.position; - targetDistance = maxTargetingRange; - } - - currentTgtRange = targetDistance; - - targetPosition += trajectoryOffset; - - turret.AimToTarget(targetPosition); - targetInTurretView = turret.TargetInRange(targetPosition, 2, maxTargetingRange); - } - - public void FireRocket() - { - if (!readyToFire) return; - if (!targetInTurretView) return; - - PartResource rocketResource = GetRocketResource(); - - if (rocketResource == null) - { - Debug.Log(part.name + " doesn't carry the rocket resource it was meant to"); - return; - } - - int rocketsLeft = (int)Math.Floor(rocketResource.amount); - - if (BDArmorySettings.INFINITE_AMMO && rocketsLeft < 1) - rocketsLeft = 1; - - if (rocketsLeft >= 1) - { - Transform currentRocketTfm = rockets[rocketsLeft - 1]; - - GameObject rocketObj = GameDatabase.Instance.GetModel(rocketModelPath); - rocketObj = - (GameObject)Instantiate(rocketObj, currentRocketTfm.position, currentRocketTfm.parent.rotation); - rocketObj.transform.rotation = currentRocketTfm.parent.rotation; - rocketObj.transform.localScale = part.rescaleFactor * Vector3.one; - currentRocketTfm.localScale = Vector3.zero; - Rocket rocket = rocketObj.AddComponent(); - rocket.explModelPath = explModelPath; - rocket.explSoundPath = explSoundPath; - rocket.spawnTransform = currentRocketTfm; - rocket.mass = rocketMass; - rocket.blastForce = blastForce; - rocket.blastHeat = blastHeat; - rocket.blastRadius = blastRadius; - rocket.thrust = thrust; - rocket.thrustTime = thrustTime; - rocket.randomThrustDeviation = thrustDeviation; - - rocket.sourceVessel = vessel; - rocketObj.SetActive(true); - rocketObj.transform.SetParent(currentRocketTfm.parent); - rocket.parentRB = part.rb; - - sfAudioSource.PlayOneShot(GameDatabase.Instance.GetAudioClip("BDArmory/Sounds/launch")); - if (!BDArmorySettings.INFINITE_AMMO) - rocketResource.amount--; - - lastRocketsLeft = rocketResource.amount; - } - } - - void SimulateTrajectory() - { - if ((BDArmorySettings.AIM_ASSIST && BDArmorySettings.DRAW_AIMERS && drawAimer && vessel.isActiveVessel) || - (weaponManager && weaponManager.guardMode && weaponManager.selectedWeaponString == GetShortName())) - { - float simTime = 0; - Transform fireTransform = rockets[0].parent; - Vector3 pointingDirection = fireTransform.forward; - Vector3 simVelocity = part.rb.velocity; - Vector3 simCurrPos = fireTransform.position + (part.rb.velocity * Time.fixedDeltaTime); - Vector3 simPrevPos = fireTransform.position + (part.rb.velocity * Time.fixedDeltaTime); - Vector3 simStartPos = fireTransform.position + (part.rb.velocity * Time.fixedDeltaTime); - bool simulating = true; - float simDeltaTime = 0.02f; - List pointPositions = new List(); - pointPositions.Add(simCurrPos); - - bool slaved = turret && weaponManager && (weaponManager.slavingTurrets || weaponManager.guardMode); - float atmosMultiplier = - Mathf.Clamp01(2.5f * - (float) - FlightGlobals.getAtmDensity(vessel.staticPressurekPa, vessel.externalTemperature, - vessel.mainBody)); - while (simulating) - { - RaycastHit hit; - - if (simTime > thrustTime) - { - simDeltaTime = 0.1f; - } - - if (simTime > 0.04f) - { - simDeltaTime = 0.02f; - if (simTime < thrustTime) - { - simVelocity += thrust / rocketMass * simDeltaTime * pointingDirection; - } - - //rotation (aero stabilize) - pointingDirection = Vector3.RotateTowards(pointingDirection, - simVelocity + Krakensbane.GetFrameVelocity(), - atmosMultiplier * (0.5f * (simTime)) * 50 * simDeltaTime * Mathf.Deg2Rad, 0); - } - - //gravity - simVelocity += FlightGlobals.getGeeForceAtPosition(simCurrPos) * simDeltaTime; - - simCurrPos += simVelocity * simDeltaTime; - pointPositions.Add(simCurrPos); - if (!mouseAiming && !slaved) - { - if (simTime > 0.1f && Physics.Raycast(simPrevPos, simCurrPos - simPrevPos, out hit, - Vector3.Distance(simPrevPos, simCurrPos), 9076737)) - { - rocketPrediction = hit.point; - simulating = false; - break; - } - else if (FlightGlobals.getAltitudeAtPos(simCurrPos) < 0) - { - rocketPrediction = simCurrPos; - simulating = false; - break; - } - } - - simPrevPos = simCurrPos; - - if ((simStartPos - simCurrPos).sqrMagnitude > currentTgtRange * currentTgtRange) - { - rocketPrediction = simStartPos + (simCurrPos - simStartPos).normalized * currentTgtRange; - //rocketPrediction = simCurrPos; - simulating = false; - } - simTime += simDeltaTime; - } - - Vector3 pointingPos = fireTransform.position + (fireTransform.forward * currentTgtRange); - trajectoryOffset = pointingPos - rocketPrediction; - predictedFlightTime = simTime; - - if (BDArmorySettings.DRAW_DEBUG_LINES && BDArmorySettings.DRAW_AIMERS) - { - Vector3[] pointsArray = pointPositions.ToArray(); - if (gameObject.GetComponent() == null) - { - LineRenderer lr = gameObject.AddComponent(); - lr.startWidth = .1f; - lr.endWidth = .1f; - lr.positionCount = pointsArray.Length; - for (int i = 0; i < pointsArray.Length; i++) - { - lr.SetPosition(i, pointsArray[i]); - } - } - else - { - LineRenderer lr = gameObject.GetComponent(); - lr.enabled = true; - lr.positionCount = pointsArray.Length; - for (int i = 0; i < pointsArray.Length; i++) - { - lr.SetPosition(i, pointsArray[i]); - } - } - } - else - { - if (gameObject.GetComponent() != null) - { - gameObject.GetComponent().enabled = false; - } - } - } - - //for straight aimer - else if (BDArmorySettings.DRAW_AIMERS && drawAimer && vessel.isActiveVessel) - { - RaycastHit hit; - float distance = 2500; - if (Physics.Raycast(transform.position, transform.forward, out hit, distance, 9076737)) - { - rocketPrediction = hit.point; - } - else - { - rocketPrediction = transform.position + (transform.forward * distance); - } - } - } - - void OnGUI() - { - if (drawAimer && vessel.isActiveVessel && BDArmorySettings.DRAW_AIMERS && !MapView.MapIsEnabled) - { - float size = 30; - - Vector3 aimPosition = FlightCamera.fetch.mainCamera.WorldToViewportPoint(rocketPrediction); - - Rect drawRect = new Rect(aimPosition.x * Screen.width - (0.5f * size), - (1 - aimPosition.y) * Screen.height - (0.5f * size), size, size); - float cameraAngle = Vector3.Angle(FlightCamera.fetch.GetCameraTransform().forward, - rocketPrediction - FlightCamera.fetch.mainCamera.transform.position); - if (cameraAngle < 90) GUI.DrawTexture(drawRect, aimerTexture); - } - } - - void MakeRocketArray() - { - Transform rocketsTransform = part.FindModelTransform("rockets"); - int numOfRockets = rocketsTransform.childCount; - rockets = new Transform[numOfRockets]; - - for (int i = 0; i < numOfRockets; i++) - { - string rocketName = rocketsTransform.GetChild(i).name; - int rocketIndex = int.Parse(rocketName.Substring(7)) - 1; - rockets[rocketIndex] = rocketsTransform.GetChild(i); - } - - if (!descendingOrder) Array.Reverse(rockets); - } - - public PartResource GetRocketResource() - { - using (var res = part.Resources.GetEnumerator()) - while (res.MoveNext()) - { - if (res.Current == null) continue; - if (res.Current.resourceName == rocketType) return res.Current; - } - return null; - } - - void UpdateRocketScales() - { - // This is a backup method only, rockets will be rescaled to zero on firing, - // so this will be called only on start and if something weird happens. - PartResource rocketResource = GetRocketResource(); - var rocketsLeft = Math.Floor(rocketResource.amount); - rocketsMax = rocketResource.maxAmount; - for (int i = 0; i < rocketsMax; i++) - { - if (i < rocketsLeft) rockets[i].localScale = Vector3.one; - else rockets[i].localScale = Vector3.zero; - } - } - - // RMB info in editor - public override string GetInfo() - { - StringBuilder output = new StringBuilder(); - output.Append(Environment.NewLine); - output.AppendLine("Weapon Type: Rocket Launcher"); - output.AppendLine($"Rocket Type: {rocketType}"); - output.AppendLine($"Max Range: {maxTargetingRange} m"); - - output.AppendLine($"Blast:"); - output.AppendLine($"- radius: {blastRadius}"); - output.AppendLine($"- power: {blastForce}"); - output.AppendLine($"- heat: {blastHeat}"); - - return output.ToString(); - } - } - - public class Rocket : MonoBehaviour - { - public Transform spawnTransform; - public Vessel sourceVessel; - public float mass; - public float thrust; - public float thrustTime; - public float blastRadius; - public float blastForce; - public float blastHeat; - public string explModelPath; - public string explSoundPath; - - public float randomThrustDeviation = 0.05f; - - public Rigidbody parentRB; - - float startTime; - public AudioSource audioSource; - - Vector3 prevPosition; - Vector3 currPosition; - - float stayTime = 0.04f; - float lifeTime = 10; - - //bool isThrusting = true; - - Rigidbody rb; - - KSPParticleEmitter[] pEmitters; - - float randThrustSeed; - - void Start() - { - BDArmorySetup.numberOfParticleEmitters++; - - rb = gameObject.AddComponent(); - pEmitters = gameObject.GetComponentsInChildren(); - - IEnumerator pe = pEmitters.AsEnumerable().GetEnumerator(); - while (pe.MoveNext()) - { - if (pe.Current == null) continue; - if (FlightGlobals.getStaticPressure(transform.position) == 0 && pe.Current.useWorldSpace) - { - pe.Current.emit = false; - } - else if (pe.Current.useWorldSpace) - { - BDAGaplessParticleEmitter gpe = pe.Current.gameObject.AddComponent(); - gpe.rb = rb; - gpe.emit = true; - } - else - { - EffectBehaviour.AddParticleEmitter(pe.Current); - } - } - pe.Dispose(); - - prevPosition = transform.position; - currPosition = transform.position; - startTime = Time.time; - - rb.mass = mass; - rb.isKinematic = true; - //rigidbody.velocity = startVelocity; - if (!FlightGlobals.RefFrameIsRotating) rb.useGravity = false; - - rb.useGravity = false; - - randThrustSeed = UnityEngine.Random.Range(0f, 100f); - - SetupAudio(); - } - - void FixedUpdate() - { - //floating origin and velocity offloading corrections - if (!FloatingOrigin.Offset.IsZero() || !Krakensbane.GetFrameVelocity().IsZero()) - { - transform.position -= FloatingOrigin.OffsetNonKrakensbane; - prevPosition -= FloatingOrigin.OffsetNonKrakensbane; - } - - if (Time.time - startTime < stayTime && transform.parent != null) - { - transform.rotation = transform.parent.rotation; - transform.position = spawnTransform.position; - //+(transform.parent.rigidbody.velocity*Time.fixedDeltaTime); - } - else - { - if (transform.parent != null && parentRB) - { - transform.parent = null; - rb.isKinematic = false; - rb.velocity = parentRB.velocity + Krakensbane.GetFrameVelocityV3f(); - } - } - - if (rb && !rb.isKinematic) - { - //physics - if (FlightGlobals.RefFrameIsRotating) - { - rb.velocity += FlightGlobals.getGeeForceAtPosition(transform.position) * Time.fixedDeltaTime; - } - - //guidance and attitude stabilisation scales to atmospheric density. - float atmosMultiplier = - Mathf.Clamp01(2.5f * - (float) - FlightGlobals.getAtmDensity(FlightGlobals.getStaticPressure(transform.position), - FlightGlobals.getExternalTemperature(), FlightGlobals.currentMainBody)); - - //model transform. always points prograde - transform.rotation = Quaternion.RotateTowards(transform.rotation, - Quaternion.LookRotation(rb.velocity + Krakensbane.GetFrameVelocity(), transform.up), - atmosMultiplier * (0.5f * (Time.time - startTime)) * 50 * Time.fixedDeltaTime); - - if (Time.time - startTime < thrustTime && Time.time - startTime > stayTime) - { - float random = randomThrustDeviation * (1 - (Mathf.PerlinNoise(4 * Time.time, randThrustSeed) * 2)); - float random2 = randomThrustDeviation * (1 - (Mathf.PerlinNoise(randThrustSeed, 4 * Time.time) * 2)); - rb.AddRelativeForce(new Vector3(random, random2, thrust)); - } - } - - if (Time.time - startTime > thrustTime) - { - //isThrusting = false; - IEnumerator pEmitter = pEmitters.AsEnumerable().GetEnumerator(); - while (pEmitter.MoveNext()) - { - if (pEmitter.Current == null) continue; - if (pEmitter.Current.useWorldSpace) - { - pEmitter.Current.minSize = Mathf.MoveTowards(pEmitter.Current.minSize, 0.1f, 0.05f); - pEmitter.Current.maxSize = Mathf.MoveTowards(pEmitter.Current.maxSize, 0.2f, 0.05f); - } - else - { - pEmitter.Current.minSize = Mathf.MoveTowards(pEmitter.Current.minSize, 0, 0.1f); - pEmitter.Current.maxSize = Mathf.MoveTowards(pEmitter.Current.maxSize, 0, 0.1f); - if (pEmitter.Current.maxSize == 0) - { - pEmitter.Current.emit = false; - } - } - } - pEmitter.Dispose(); - } - - if (Time.time - startTime > 0.1f + stayTime) - { - currPosition = transform.position; - float dist = (currPosition - prevPosition).magnitude; - Ray ray = new Ray(prevPosition, currPosition - prevPosition); - RaycastHit hit; - KerbalEVA hitEVA = null; - //if (Physics.Raycast(ray, out hit, dist, 2228224)) - //{ - // try - // { - // hitEVA = hit.collider.gameObject.GetComponentUpwards(); - // if (hitEVA != null) - // Debug.Log("[BDArmory]:Hit on kerbal confirmed!"); - // } - // catch (NullReferenceException) - // { - // Debug.Log("[BDArmory]:Whoops ran amok of the exception handler"); - // } - - // if (hitEVA && hitEVA.part.vessel != sourceVessel) - // { - // Detonate(hit.point); - // } - //} - - if (!hitEVA) - { - if (Physics.Raycast(ray, out hit, dist, 9076737)) - { - Part hitPart = null; - try - { - KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards(); - hitPart = eva ? eva.part : hit.collider.gameObject.GetComponentInParent(); - } - catch (NullReferenceException) - { - } - - if (hitPart == null || (hitPart != null && hitPart.vessel != sourceVessel)) - { - Detonate(hit.point); - } - } - else if (FlightGlobals.getAltitudeAtPos(transform.position) < 0) - { - Detonate(transform.position); - } - } - } - else if (FlightGlobals.getAltitudeAtPos(currPosition) <= 0) - { - Detonate(currPosition); - } - prevPosition = currPosition; - - if (Time.time - startTime > lifeTime) - { - Detonate(transform.position); - } - } - - void Update() - { - if (HighLogic.LoadedSceneIsFlight) - { - if (BDArmorySetup.GameIsPaused) - { - if (audioSource.isPlaying) - { - audioSource.Stop(); - } - } - else - { - if (!audioSource.isPlaying) - { - audioSource.Play(); - } - } - } - } - - void Detonate(Vector3 pos) - { - BDArmorySetup.numberOfParticleEmitters--; - - ExplosionFx.CreateExplosion(pos, BlastPhysicsUtils.CalculateExplosiveMass(blastRadius), - explModelPath, explSoundPath, true); - - IEnumerator emitter = pEmitters.AsEnumerable().GetEnumerator(); - while (emitter.MoveNext()) - { - if (emitter.Current == null) continue; - if (!emitter.Current.useWorldSpace) continue; - emitter.Current.gameObject.AddComponent(); - emitter.Current.transform.parent = null; - } - emitter.Dispose(); - Destroy(gameObject); //destroy rocket on collision - } - - void SetupAudio() - { - audioSource = gameObject.AddComponent(); - audioSource.loop = true; - audioSource.minDistance = 1; - audioSource.maxDistance = 2000; - audioSource.dopplerLevel = 0.5f; - audioSource.volume = 0.9f * BDArmorySettings.BDARMORY_WEAPONS_VOLUME; - audioSource.pitch = 1f; - audioSource.priority = 255; - audioSource.spatialBlend = 1; - - audioSource.clip = GameDatabase.Instance.GetAudioClip("BDArmory/Sounds/rocketLoop"); - - UpdateVolume(); - BDArmorySetup.OnVolumeChange += UpdateVolume; - } - - void UpdateVolume() - { - if (audioSource) - { - audioSource.volume = BDArmorySettings.BDARMORY_WEAPONS_VOLUME; - } - } - } + public class RocketLauncher : EngageableWeapon + { + + } + public class Rocket : MonoBehaviour + { + public Transform spawnTransform; + public Vessel sourceVessel; + public float mass; + public float thrust; + public float thrustTime; + public float blastRadius; + public float blastForce; + public float blastHeat; + public bool proximityDetonation; + public float maxAirDetonationRange; + public float detonationRange; + public string explModelPath; + public string explSoundPath; + + public float randomThrustDeviation = 0.05f; + + public Rigidbody parentRB; + + float startTime; + public AudioSource audioSource; + + Vector3 prevPosition; + Vector3 currPosition; + Vector3 startPosition; + + float stayTime = 0.04f; + float lifeTime = 10; + + //bool isThrusting = true; + + Rigidbody rb; + + KSPParticleEmitter[] pEmitters; + + float randThrustSeed; + + void Start() + { + BDArmorySetup.numberOfParticleEmitters++; + + rb = gameObject.AddComponent(); + pEmitters = gameObject.GetComponentsInChildren(); + + IEnumerator pe = pEmitters.AsEnumerable().GetEnumerator(); + while (pe.MoveNext()) + { + if (pe.Current == null) continue; + if (FlightGlobals.getStaticPressure(transform.position) == 0 && pe.Current.useWorldSpace) + { + pe.Current.emit = false; + } + else if (pe.Current.useWorldSpace) + { + BDAGaplessParticleEmitter gpe = pe.Current.gameObject.AddComponent(); + gpe.rb = rb; + gpe.emit = true; + } + else + { + EffectBehaviour.AddParticleEmitter(pe.Current); + } + } + pe.Dispose(); + + prevPosition = transform.position; + currPosition = transform.position; + startPosition = transform.position; + startTime = Time.time; + + rb.mass = mass; + rb.isKinematic = true; + //rigidbody.velocity = startVelocity; + if (!FlightGlobals.RefFrameIsRotating) rb.useGravity = false; + + rb.useGravity = false; + + randThrustSeed = UnityEngine.Random.Range(0f, 100f); + + SetupAudio(); + } + + void FixedUpdate() + { + //floating origin and velocity offloading corrections + if (!FloatingOrigin.Offset.IsZero() || !Krakensbane.GetFrameVelocity().IsZero()) + { + transform.position -= FloatingOrigin.OffsetNonKrakensbane; + prevPosition -= FloatingOrigin.OffsetNonKrakensbane; + } + float distanceFromStart = Vector3.Distance(transform.position, startPosition); + + if (Time.time - startTime < stayTime && transform.parent != null) + { + transform.rotation = transform.parent.rotation; + transform.position = spawnTransform.position; + //+(transform.parent.rigidbody.velocity*Time.fixedDeltaTime); + } + else + { + if (transform.parent != null && parentRB) + { + transform.parent = null; + rb.isKinematic = false; + rb.velocity = parentRB.velocity + Krakensbane.GetFrameVelocityV3f(); + } + } + + if (rb && !rb.isKinematic) + { + //physics + if (FlightGlobals.RefFrameIsRotating) + { + rb.velocity += FlightGlobals.getGeeForceAtPosition(transform.position) * Time.fixedDeltaTime; + } + + //guidance and attitude stabilisation scales to atmospheric density. + float atmosMultiplier = + Mathf.Clamp01(2.5f * + (float) + FlightGlobals.getAtmDensity(FlightGlobals.getStaticPressure(transform.position), + FlightGlobals.getExternalTemperature(), FlightGlobals.currentMainBody)); + + //model transform. always points prograde + transform.rotation = Quaternion.RotateTowards(transform.rotation, + Quaternion.LookRotation(rb.velocity + Krakensbane.GetFrameVelocity(), transform.up), + atmosMultiplier * (0.5f * (Time.time - startTime)) * 50 * Time.fixedDeltaTime); + + + if (Time.time - startTime < thrustTime && Time.time - startTime > stayTime) + { + float random = randomThrustDeviation * (1 - (Mathf.PerlinNoise(4 * Time.time, randThrustSeed) * 2)); + float random2 = randomThrustDeviation * (1 - (Mathf.PerlinNoise(randThrustSeed, 4 * Time.time) * 2)); + rb.AddRelativeForce(new Vector3(random, random2, thrust)); + } + } + + + if (Time.time - startTime > thrustTime) + { + //isThrusting = false; + IEnumerator pEmitter = pEmitters.AsEnumerable().GetEnumerator(); + while (pEmitter.MoveNext()) + { + if (pEmitter.Current == null) continue; + if (pEmitter.Current.useWorldSpace) + { + pEmitter.Current.minSize = Mathf.MoveTowards(pEmitter.Current.minSize, 0.1f, 0.05f); + pEmitter.Current.maxSize = Mathf.MoveTowards(pEmitter.Current.maxSize, 0.2f, 0.05f); + } + else + { + pEmitter.Current.minSize = Mathf.MoveTowards(pEmitter.Current.minSize, 0, 0.1f); + pEmitter.Current.maxSize = Mathf.MoveTowards(pEmitter.Current.maxSize, 0, 0.1f); + if (pEmitter.Current.maxSize == 0) + { + pEmitter.Current.emit = false; + } + } + } + pEmitter.Dispose(); + } + + if (Time.time - startTime > 0.1f + stayTime) + { + currPosition = transform.position; + float dist = (currPosition - prevPosition).magnitude; + Ray ray = new Ray(prevPosition, currPosition - prevPosition); + RaycastHit hit; + KerbalEVA hitEVA = null; + //if (Physics.Raycast(ray, out hit, dist, 2228224)) + //{ + // try + // { + // hitEVA = hit.collider.gameObject.GetComponentUpwards(); + // if (hitEVA != null) + // Debug.Log("[BDArmory]:Hit on kerbal confirmed!"); + // } + // catch (NullReferenceException) + // { + // Debug.Log("[BDArmory]:Whoops ran amok of the exception handler"); + // } + + // if (hitEVA && hitEVA.part.vessel != sourceVessel) + // { + // Detonate(hit.point); + // } + //} + + if (!hitEVA) + { + if (Physics.Raycast(ray, out hit, dist, 9076737)) + { + Part hitPart = null; + try + { + KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards(); + hitPart = eva ? eva.part : hit.collider.gameObject.GetComponentInParent(); + } + catch (NullReferenceException) + { + } + + + if (hitPart == null || (hitPart != null && hitPart.vessel != sourceVessel)) + { + Detonate(hit.point); + } + } + else if (FlightGlobals.getAltitudeAtPos(transform.position) < 0) + { + Detonate(transform.position); + } + } + } + else if (FlightGlobals.getAltitudeAtPos(currPosition) <= 0) + { + Detonate(currPosition); + } + prevPosition = currPosition; + + if (Time.time - startTime > lifeTime) // life's 10s, quite a long time for faster rockets + { + Detonate(transform.position); + } + if (distanceFromStart >= maxAirDetonationRange)//rockets are performance intensive, lets cull those that have flown too far away + { + Detonate(transform.position); + } + if (ProximityAirDetonation(distanceFromStart)) + { + Detonate(transform.position); + } + } + private bool ProximityAirDetonation(float distanceFromStart) + { + bool detonate = false; + + if (distanceFromStart <= blastRadius) return false; + + if (proximityDetonation) + { + using (var hitsEnu = Physics.OverlapSphere(transform.position, detonationRange, 557057).AsEnumerable().GetEnumerator()) + { + while (hitsEnu.MoveNext()) + { + if (hitsEnu.Current == null) continue; + try + { + Part partHit = hitsEnu.Current.GetComponentInParent(); + if (partHit?.vessel != sourceVessel) + { + if (BDArmorySettings.DRAW_DEBUG_LABELS) + Debug.Log("[BDArmory]: Bullet proximity sphere hit | Distance overlap = " + detonationRange + "| Part name = " + partHit.name); + return detonate = true; + } + } + catch + { + } + } + } + } + return detonate; + } + void Update() + { + if (HighLogic.LoadedSceneIsFlight) + { + if (BDArmorySetup.GameIsPaused) + { + if (audioSource.isPlaying) + { + audioSource.Stop(); + } + } + else + { + if (!audioSource.isPlaying) + { + audioSource.Play(); + } + } + } + } + void Detonate(Vector3 pos) + { + BDArmorySetup.numberOfParticleEmitters--; + + ExplosionFx.CreateExplosion(pos, BlastPhysicsUtils.CalculateExplosiveMass(blastRadius), + explModelPath, explSoundPath, true); + + IEnumerator emitter = pEmitters.AsEnumerable().GetEnumerator(); + while (emitter.MoveNext()) + { + if (emitter.Current == null) continue; + if (!emitter.Current.useWorldSpace) continue; + emitter.Current.gameObject.AddComponent(); + emitter.Current.transform.parent = null; + } + emitter.Dispose(); + Destroy(gameObject); //destroy rocket on collision + } + + + void SetupAudio() + { + audioSource = gameObject.AddComponent(); + audioSource.loop = true; + audioSource.minDistance = 1; + audioSource.maxDistance = 2000; + audioSource.dopplerLevel = 0.5f; + audioSource.volume = 0.9f * BDArmorySettings.BDARMORY_WEAPONS_VOLUME; + audioSource.pitch = 1f; + audioSource.priority = 255; + audioSource.spatialBlend = 1; + + audioSource.clip = GameDatabase.Instance.GetAudioClip("BDArmory/Sounds/rocketLoop"); + + UpdateVolume(); + BDArmorySetup.OnVolumeChange += UpdateVolume; + } + + void UpdateVolume() + { + if (audioSource) + { + audioSource.volume = BDArmorySettings.BDARMORY_WEAPONS_VOLUME; + } + } + } } diff --git a/BDArmory/Radar/VesselRadarData.cs b/BDArmory/Radar/VesselRadarData.cs index 04f011de1..0a1932ddb 100644 --- a/BDArmory/Radar/VesselRadarData.cs +++ b/BDArmory/Radar/VesselRadarData.cs @@ -1018,9 +1018,11 @@ private void WindowRadar(int windowID) BDArmorySetup.ResizeRadarWindow(BDArmorySettings.RADAR_WINDOW_SCALE); } } - // End Resizing code. - - BDGUIUtils.RepositionWindow(ref BDArmorySetup.WindowRectRadar); + // End Resizing code. + if (BDArmorySettings.STRICT_WINDOW_BOUNDARIES) + { + BDGUIUtils.RepositionWindow(ref BDArmorySetup.WindowRectRadar); + } } internal static void UpdateRadarScale(float diff) diff --git a/BDArmory/UI/BDArmorySetup.cs b/BDArmory/UI/BDArmorySetup.cs index 09689ddf9..2b1003f1c 100644 --- a/BDArmory/UI/BDArmorySetup.cs +++ b/BDArmory/UI/BDArmorySetup.cs @@ -431,17 +431,20 @@ void Start() BulletInfo.Load(); } - private void CheckIfWindowsSettingsAreWithinScreen() - { - BDGUIUtils.RepositionWindow(ref WindowRectToolbar); - BDGUIUtils.RepositionWindow(ref WindowRectSettings); - BDGUIUtils.RepositionWindow(ref WindowRectRwr); - BDGUIUtils.RepositionWindow(ref WindowRectVesselSwitcher); - BDGUIUtils.RepositionWindow(ref WindowRectWingCommander); - BDGUIUtils.RepositionWindow(ref WindowRectTargetingCam); - } - - void Update() + private void CheckIfWindowsSettingsAreWithinScreen() + { + if (BDArmorySettings.STRICT_WINDOW_BOUNDARIES) + { + BDGUIUtils.RepositionWindow(ref WindowRectToolbar); + BDGUIUtils.RepositionWindow(ref WindowRectSettings); + BDGUIUtils.RepositionWindow(ref WindowRectRwr); + BDGUIUtils.RepositionWindow(ref WindowRectVesselSwitcher); + BDGUIUtils.RepositionWindow(ref WindowRectWingCommander); + BDGUIUtils.RepositionWindow(ref WindowRectTargetingCam); + } + } + + void Update() { if (HighLogic.LoadedSceneIsFlight) { @@ -516,42 +519,31 @@ public void UpdateCursorState() } drawCursor = false; - if (!MapView.MapIsEnabled && !Misc.Misc.CheckMouseIsOnGui() && !PauseMenu.isOpen) - { - if (ActiveWeaponManager.selectedWeapon != null && ActiveWeaponManager.weaponIndex > 0 && - !ActiveWeaponManager.guardMode) - { - if (ActiveWeaponManager.selectedWeapon.GetWeaponClass() == WeaponClasses.Gun || - ActiveWeaponManager.selectedWeapon.GetWeaponClass() == WeaponClasses.DefenseLaser) - { - ModuleWeapon mw = - ActiveWeaponManager.selectedWeapon.GetPart().FindModuleImplementing(); - if (mw.weaponState == ModuleWeapon.WeaponStates.Enabled && mw.maxPitch > 1 && !mw.slaved && - !mw.aiControlled) - { - //Screen.showCursor = false; - Cursor.visible = false; - drawCursor = true; - return; - } - } - else if (ActiveWeaponManager.selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket) - { - RocketLauncher rl = - ActiveWeaponManager.selectedWeapon.GetPart().FindModuleImplementing(); - if (rl.readyToFire && rl.turret) - { - //Screen.showCursor = false; - Cursor.visible = false; - drawCursor = true; - return; - } - } - } - } - - //Screen.showCursor = true; - Cursor.visible = true; + if (!MapView.MapIsEnabled && !Misc.Misc.CheckMouseIsOnGui() && !PauseMenu.isOpen) + + { + if (ActiveWeaponManager.selectedWeapon != null && ActiveWeaponManager.weaponIndex > 0 && + !ActiveWeaponManager.guardMode) + { + if (ActiveWeaponManager.selectedWeapon.GetWeaponClass() == WeaponClasses.Gun || + ActiveWeaponManager.selectedWeapon.GetWeaponClass() == WeaponClasses.DefenseLaser || + ActiveWeaponManager.selectedWeapon.GetWeaponClass() == WeaponClasses.Rocket) + { + ModuleWeapon mw = + ActiveWeaponManager.selectedWeapon.GetPart().FindModuleImplementing(); + if (mw.weaponState == ModuleWeapon.WeaponStates.Enabled && mw.maxPitch > 1 && !mw.slaved && + !mw.aiControlled) + { + //Screen.showCursor = false; + Cursor.visible = false; + drawCursor = true; + return; + } + } + } + } + //Screen.showCursor = true; + Cursor.visible = true; } void VesselChange(Vessel v) @@ -684,7 +676,7 @@ void WindowBDAToolbar(int windowID) line += 0.25f; // Version. - GUI.Label(new Rect(toolWindowWidth - 30 - 28 - 70, 23, 70, 10), Version, waterMarkStyle); + GUI.Label(new Rect(toolWindowWidth - 30 - 28 - 70, 23, 70, 10), Version + " SI", waterMarkStyle); //SETTINGS BUTTON if (!BDKeyBinder.current && @@ -1177,9 +1169,12 @@ void WindowBDAToolbar(int windowID) toolWindowHeight = Mathf.Lerp(toolWindowHeight, contentTop + (line * entryHeight) + 5, 1); WindowRectToolbar.height = toolWindowHeight; - // = new Rect(toolbarWindowRect.position.x, toolbarWindowRect.position.y, toolWindowWidth, toolWindowHeight); - BDGUIUtils.RepositionWindow(ref WindowRectToolbar); - } + // = new Rect(toolbarWindowRect.position.x, toolbarWindowRect.position.y, toolWindowWidth, toolWindowHeight); + if (BDArmorySettings.STRICT_WINDOW_BOUNDARIES) + { + BDGUIUtils.RepositionWindow(ref WindowRectToolbar); + } + } bool validGPSName = true; @@ -1410,9 +1405,11 @@ void WindowSettings(int windowID) GUI.Label(SLeftRect(line), $"{Localizer.Format("#LOC_BDArmory_Settings_MaxBulletHoles")}: ({BDArmorySettings.MAX_NUM_BULLET_DECALS})", leftLabel);//Max Bullet Holes BDArmorySettings.MAX_NUM_BULLET_DECALS = (int)GUI.HorizontalSlider(SRightRect(line), BDArmorySettings.MAX_NUM_BULLET_DECALS, 1f, 999); line++; - line++; + BDArmorySettings.PAINTBALL = GUI.Toggle(SLeftRect(line), BDArmorySettings.PAINTBALL, "Paintball Mode"); + BDArmorySettings.BATTLEDAMAGE = GUI.Toggle(SRightRect(line), BDArmorySettings.BATTLEDAMAGE, "Battle Damage"); + line++; - bool origPm = BDArmorySettings.PEACE_MODE; + bool origPm = BDArmorySettings.PEACE_MODE; BDArmorySettings.PEACE_MODE = GUI.Toggle(SLeftRect(line), BDArmorySettings.PEACE_MODE, Localizer.Format("#LOC_BDArmory_Settings_PeaceMode"));//"Peace Mode" if (BDArmorySettings.PEACE_MODE && !origPm) { @@ -1423,7 +1420,8 @@ void WindowSettings(int windowID) } } line++; - line++; + BDArmorySettings.STRICT_WINDOW_BOUNDARIES = GUI.Toggle(SRightRect(line), BDArmorySettings.STRICT_WINDOW_BOUNDARIES, "Strict Window Boundaries"); + line++; GUI.Label(SLeftRect(line), Localizer.Format("#LOC_BDArmory_Settings_RWRWindowScale") + ": " + (BDArmorySettings.RWR_WINDOW_SCALE * 100).ToString("0") + "%", leftLabel);//RWR Window Scale float rwrScale = BDArmorySettings.RWR_WINDOW_SCALE; @@ -1531,8 +1529,11 @@ void WindowSettings(int windowID) line += 1.5f; settingsHeight = (line * settingsLineHeight); WindowRectSettings.height = settingsHeight; - BDGUIUtils.RepositionWindow(ref WindowRectSettings); - BDGUIUtils.UseMouseEventInRect(WindowRectSettings); + if (BDArmorySettings.STRICT_WINDOW_BOUNDARIES) + { + BDGUIUtils.RepositionWindow(ref WindowRectSettings); + } + BDGUIUtils.UseMouseEventInRect(WindowRectSettings); } internal static void ResizeRwrWindow(float rwrScale) diff --git a/BDArmory/UI/BDTeamSelector.cs b/BDArmory/UI/BDTeamSelector.cs index 53b24facb..7b77ce9e2 100644 --- a/BDArmory/UI/BDTeamSelector.cs +++ b/BDArmory/UI/BDTeamSelector.cs @@ -2,6 +2,7 @@ using BDArmory.Misc; using BDArmory.Modules; using UnityEngine; +using BDArmory.Core; using KSP.Localization; namespace BDArmory.UI @@ -103,7 +104,10 @@ private void TeamSelectorWindow(int id) } height += margin; - BDGUIUtils.RepositionWindow(ref window); + if (BDArmorySettings.STRICT_WINDOW_BOUNDARIES) + { + BDGUIUtils.RepositionWindow(ref window); + } BDGUIUtils.UseMouseEventInRect(window); } diff --git a/BDArmory/UI/LoadedVesselSwitcher.cs b/BDArmory/UI/LoadedVesselSwitcher.cs index 3bbc07f70..17e31b7c4 100644 --- a/BDArmory/UI/LoadedVesselSwitcher.cs +++ b/BDArmory/UI/LoadedVesselSwitcher.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using BDArmory.Misc; using BDArmory.Modules; +using BDArmory.Core; using UnityEngine; using KSP.Localization; @@ -250,7 +251,10 @@ private void WindowVesselSwitcher(int id) height += _margin; _windowHeight = height; - BDGUIUtils.RepositionWindow(ref BDArmorySetup.WindowRectVesselSwitcher); + if (BDArmorySettings.STRICT_WINDOW_BOUNDARIES) + { + BDGUIUtils.RepositionWindow(ref BDArmorySetup.WindowRectVesselSwitcher); + } } private string UpdateVesselStatus(MissileFire wm, GUIStyle vButtonStyle) diff --git a/Changelog 1.3.4-SI.txt b/Changelog 1.3.4-SI.txt new file mode 100644 index 000000000..c50460904 --- /dev/null +++ b/Changelog 1.3.4-SI.txt @@ -0,0 +1,100 @@ +Changes/additions to vanilla BDAc 1.3.4 +======================================= +-Complete overhaul of ModuleWeapon + + *Now supports magazine-fed weapons that must be occasionally reloaded + *Now supports burst-fire weapons (multiple shots per fire command) + *Now supports crewed weapons that require a kerbal present to fire + *RocketLaucher functionality has been folded into ModuleWeapon, and will need to be reconfigured. A MM patch for legacy rocketlauncher support is included + *RocketLaunchers can now be supplied from external ammo boxes. + *Gyrojet guns now supported + *Rocket class weapons can now use FlaK settings (airDetonation, proximityDetonation, maxAirDetonationRange) + *Can set custom fire sounds for Rockets + + ============================== + *New ModuleWeapon .cfg settings + ============================== + BeltFed = False // enables mag-fed weapons + RoundsPerMag = n // shots per magazine or shots per burst if BurstFire = true + ReloadTime = n // time(sec) needed to reload + BurstFire = true // enables burst fire; use with RoundsPerMag + crewserved = true // weapon part must have CrewCapacity > 0 and Kerbal occupant + externalAmmo = true // rocket class weapons use external ammo boxes + rocketPod = true // default true, used if rocketpod w/ rocket submodels (e.g. Hydra70) + setting rocketPod = true will default RoundsPerMag to the # of rocket submodels + setting rocketpod = false will automatically set externalAmmo = true + RocketPod = true and externalAmmo = true will set BeltFed = false + +-Rework of AI weapon management + + *AI will now use rocket pods + *Slight improvement to bomb selection logic + +-Rework of Pilot AI + + *Steer Limiter now properly labeled steer limiter, not stages number + *AI will now make greater use of Yaw control authority for fine yaw adjustment + + ================ + *New AI settings - require advanced tweakables enabled + ================ + Speed-Adjusted Steer Limiter //secondary, speed-adjusted steer limiter + Adjusted Limiter Speed // speed threshold the adjusted limiter takes effect + Extend Distance // distance AI will extend to regain energy + Missile Evade Distance // distance AI will break to avoid missiles + AI Personality // standard or reckless behavior + +-Gameplay / UI changes + *Weapon Manager GUI reads BDAc 1.3.4. SI, to differentiate this fork of BDA from vanilla + *Fixes weapon ammo count in the Weaponmanager GUI + *GUI windows - RWR, Radar, VesselSwitch,etc. can now be moved off screen + *Bullet damage calc extended + -Bullets down to 5mm caliber will properly deal damage now + -The APBulletMod value in BulletDef.cfg is now implemented, and works as a multiplier. APBM = 1 is standard penetration, 0.5 would halve bullet penetration depth, 1.5 would increase bullet penetration by 150%, etc. + *Updates HP calc - will now give wings and B9 proc parts reasonable Hp values for their size/mass + *HP vaules now increase/decrease by 25 instead of 100 + *Adds PaintBall Mode - Exactly what it says on the tin; nonlethal mode for testing or fun + *Adds FuelTank penetration - LF & LF/O tanks that get shot will start to leak fuel + *Adds Battle damage to parts + -Engine damage will reduce thrust and eventually shutdown the engine + -Wing and ctrl surface damage will reduce lift + -SAS damage will reduce available torque + -Sufficient cockpit/cabin damage will kill kerbals inside them + *Changes to BDA settings Window + *Instakill setting now works properly + *Adds PaintBall setting - toggles PaintBall Mode + *Strict Window Boundaries setting - toggles if GUI windows snap to screen edges + *Battle damage setting - toggles part battle damage consequences + +-Expanded BDArmory settings for BDArmorySettings.cfg + *PAINTBALL = False + *BATTLEDAMAGE = False + *STRICT_WINDOW_BOUNDARIES = FALSE + *SELFSEALING_TANKS = TRUE + +-Updates BDArmory/Localization/UI/en-us.cfg with the new Pilot AI settings + *#LOC_BDArmory_SASteerLimiter = Speed-Adjusted Steer Limiter + *#LOC_BDArmory_LimiterSpeed = Adjusted Limiter Speed + *#LOC_BDArmory_ExtendDist = Extend Distance + *#LOC_BDArmory_EvadeDist = Missile Evade Distance + *#LOC_BDArmory_AIPersonality = AI Personality + *#LOC_BDArmory_Reckless = Reckless + *#LOC_BDArmory_Standard = Standard + +-Adds new FX + -BDArmory/FX/FuelLeakFX/model.mu + -BDArmory/FX/FuelLeakFX/DustParticle.png + +-Adds three new bullethit decals for Paintball mode + -BDArmory/Models/bulletDecal/BulletDecal3.mu + -BDArmory/Models/bulletDecal/PH1.png + -BDArmory/Models/bulletDecal/BulletDecal4.mu + -BDArmory/Models/bulletDecal/PH2.png + -BDArmory/Models/bulletDecal/BulletDecal5.mu + -BDArmory/Models/bulletDecal/PH3.png + + +includes 3 Example weapons showcasing new functionality + -Browning Gyrojet(BDArmory/Parts/browninganm2/browninganm2 - Copy) + -FlaK Burst Cannon (BDArmory/Parts/m1Abrams/m1Abrams - Copy) + -Reloadable RocketPod (BDArmory/Parts/h70Launcher/h70Launcher - Copy) \ No newline at end of file From 669708a8171529457a77bdbfd223aa4e2e29ab35 Mon Sep 17 00:00:00 2001 From: SuicidalInsanity Date: Thu, 6 Aug 2020 23:10:50 -0700 Subject: [PATCH 8/8] Update Changelog 1.3.4-SI.txt --- Changelog 1.3.4-SI.txt | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/Changelog 1.3.4-SI.txt b/Changelog 1.3.4-SI.txt index c50460904..24e4ac0cc 100644 --- a/Changelog 1.3.4-SI.txt +++ b/Changelog 1.3.4-SI.txt @@ -5,10 +5,12 @@ Changes/additions to vanilla BDAc 1.3.4 *Now supports magazine-fed weapons that must be occasionally reloaded *Now supports burst-fire weapons (multiple shots per fire command) *Now supports crewed weapons that require a kerbal present to fire - *RocketLaucher functionality has been folded into ModuleWeapon, and will need to be reconfigured. A MM patch for legacy rocketlauncher support is included + *RocketLaucher functionality has been folded into ModuleWeapon, Rocketlaunchers will need to be reconfigured. A MM patch for legacy rocketlauncher support is included *RocketLaunchers can now be supplied from external ammo boxes. *Gyrojet guns now supported - *Rocket class weapons can now use FlaK settings (airDetonation, proximityDetonation, maxAirDetonationRange) + *Rocket class weapons can now use FlaK settings (airDetonation, proximityDetonation, + +maxAirDetonationRange) *Can set custom fire sounds for Rockets ============================== @@ -38,8 +40,8 @@ Changes/additions to vanilla BDAc 1.3.4 ================ *New AI settings - require advanced tweakables enabled ================ - Speed-Adjusted Steer Limiter //secondary, speed-adjusted steer limiter - Adjusted Limiter Speed // speed threshold the adjusted limiter takes effect + Speed-Adjusted Steer Limiter //secondary, speed-adjusted steer limiter. Should be of use to FAR users + Adjusted Limiter Speed // speed threshold the adjusted limiter takes effect. Also should be of use to FAR users. Extend Distance // distance AI will extend to regain energy Missile Evade Distance // distance AI will break to avoid missiles AI Personality // standard or reckless behavior @@ -50,8 +52,14 @@ Changes/additions to vanilla BDAc 1.3.4 *GUI windows - RWR, Radar, VesselSwitch,etc. can now be moved off screen *Bullet damage calc extended -Bullets down to 5mm caliber will properly deal damage now - -The APBulletMod value in BulletDef.cfg is now implemented, and works as a multiplier. APBM = 1 is standard penetration, 0.5 would halve bullet penetration depth, 1.5 would increase bullet penetration by 150%, etc. - *Updates HP calc - will now give wings and B9 proc parts reasonable Hp values for their size/mass + -The APBulletMod value in BulletDef.cfg is now implemented, and works as a + +multiplier. APBM = 1 is standard penetration, 0.5 would halve bullet penetration depth, 1.5 would + +increase bullet penetration by 150%, etc. + *Updates HP calc - will now give wings and B9 proc parts reasonable Hp values for their + +size/mass. Mainly for HP clacs vis-a-vis FAR dynamic mass adjustment for smooth scaling *HP vaules now increase/decrease by 25 instead of 100 *Adds PaintBall Mode - Exactly what it says on the tin; nonlethal mode for testing or fun *Adds FuelTank penetration - LF & LF/O tanks that get shot will start to leak fuel @@ -63,7 +71,7 @@ Changes/additions to vanilla BDAc 1.3.4 *Changes to BDA settings Window *Instakill setting now works properly *Adds PaintBall setting - toggles PaintBall Mode - *Strict Window Boundaries setting - toggles if GUI windows snap to screen edges + *Strict Window Boundaries setting - allows GUI windows to be moved off-screen. Reenabling will snap any offscreen windows to nearest screen edge. *Battle damage setting - toggles part battle damage consequences -Expanded BDArmory settings for BDArmorySettings.cfg @@ -97,4 +105,4 @@ Changes/additions to vanilla BDAc 1.3.4 includes 3 Example weapons showcasing new functionality -Browning Gyrojet(BDArmory/Parts/browninganm2/browninganm2 - Copy) -FlaK Burst Cannon (BDArmory/Parts/m1Abrams/m1Abrams - Copy) - -Reloadable RocketPod (BDArmory/Parts/h70Launcher/h70Launcher - Copy) \ No newline at end of file + -Reloadable RocketPod (BDArmory/Parts/h70Launcher/h70Launcher - Copy)