From 10f274d4b78e0cbfabcad16248deac24f142b5a1 Mon Sep 17 00:00:00 2001 From: NathanKell Date: Fri, 17 Jun 2022 23:34:44 -0700 Subject: [PATCH 1/2] Fix Splashed being sticky (if a vessel decouples all splashed parts, it can never become unsplashed). Also fixes splashed overriding prelaunch. --- GameData/KSPCommunityFixes/Settings.cfg | 5 + .../BugFixes/StickySplashedFixer.cs | 131 ++++++++++++++++++ KSPCommunityFixes/KSPCommunityFixes.csproj | 1 + 3 files changed, 137 insertions(+) create mode 100644 KSPCommunityFixes/BugFixes/StickySplashedFixer.cs diff --git a/GameData/KSPCommunityFixes/Settings.cfg b/GameData/KSPCommunityFixes/Settings.cfg index e1c3dfe..e037ddb 100644 --- a/GameData/KSPCommunityFixes/Settings.cfg +++ b/GameData/KSPCommunityFixes/Settings.cfg @@ -84,6 +84,11 @@ KSP_COMMUNITY_FIXES // it depends on the stock delta V implementation. DeltaVHideWhenDisabled = true + // Fixes an issue where if a vessel starts out splashed, and decouples from its only + // splashed parts, it will never leave the splashed state. + // This also fixes an issue where Splashed overrides Prelaunch as a Situation. + StickySplashedFixer = true + // Fix the asteroid/comet spawner generating non-unique Part.flightId identifiers. This has // a few minor side effects in stock (mainly incorrect science bonuses), but this field is // heavily relied upon by various mods and this can cause major issues for them. diff --git a/KSPCommunityFixes/BugFixes/StickySplashedFixer.cs b/KSPCommunityFixes/BugFixes/StickySplashedFixer.cs new file mode 100644 index 0000000..4a5d8de --- /dev/null +++ b/KSPCommunityFixes/BugFixes/StickySplashedFixer.cs @@ -0,0 +1,131 @@ +/* +The core of the issue is the asteroid/comet spawner passing a random int for the "id" argument of the ProtoVessel.CreatePartNode() method. +This id is affected to the part.flightId field, which must be unique game-wide and should have been generated by calling +ShipConstruction.GetUniqueFlightID(). Failing to produce an unique flightId result in various issues, especially with mods as they often +rely on that id. + +Note that by fixing this, we replace how the asteroid/comet "seed" is generated, which technically affect further calls to UnityEngine.Random(), +but this shouldn't have any effect on the actual randomness/distribution of the generated stuff. +*/ + +using System; +using System.Collections.Generic; +using HarmonyLib; +using KSP.UI.Screens; + +namespace KSPCommunityFixes.BugFixes +{ + class StickySplashedFixer : BasePatch + { + protected override Version VersionMin => new Version(1, 8, 0); + + protected override void ApplyPatches(List patches) + { + patches.Add(new PatchInfo( + PatchMethodType.Prefix, + AccessTools.Method(typeof(Vessel), "updateSituation"), + this)); + + patches.Add(new PatchInfo( + PatchMethodType.Prefix, + AccessTools.Method(typeof(StageManager), "ActivateStage"), + this)); + + patches.Add(new PatchInfo( + PatchMethodType.Postfix, + AccessTools.Method(typeof(StageManager), "ActivateStage"), + this)); + } + + static bool Vessel_updateSituation_Prefix(Vessel __instance) + { + bool flag; + if (__instance.EVALadderVessel != __instance) + { + __instance.situation = __instance.evaController.LadderPart.vessel.situation; + flag = true; + } + else + { + flag = false; + if (__instance.situation == Vessel.Situations.PRELAUNCH) + { + if (__instance.srfSpeed > (__instance.Splashed ? 1 : 2.5) && !__instance.precalc.isEasingGravity && !__instance.vesselSpawning) + __instance.situation = __instance.Splashed ? Vessel.Situations.SPLASHED : Vessel.Situations.LANDED; + } + else if (__instance.Landed) + { + __instance.situation = Vessel.Situations.LANDED; + } + else if (__instance.Splashed) + { + __instance.situation = Vessel.Situations.SPLASHED; + } + else + { + if (__instance.staticPressurekPa > 0.0) + { + __instance.situation = Vessel.Situations.FLYING; + } + else if (__instance.orbit.eccentricity < 1.0 && __instance.orbit.ApR < __instance.mainBody.sphereOfInfluence) + { + if (__instance.orbit.PeA < (__instance.mainBody.atmosphere ? __instance.mainBody.atmosphereDepth : 0)) + { + __instance.situation = Vessel.Situations.SUB_ORBITAL; + } + else + { + __instance.situation = Vessel.Situations.ORBITING; + } + } + else + { + __instance.situation = Vessel.Situations.ESCAPING; + } + } + } + + if (__instance.situation != __instance.lastSituation) + { + GameEvents.onVesselSituationChange.Fire(new GameEvents.HostedFromToAction(__instance, __instance.lastSituation, __instance.situation)); + __instance.lastSituation = __instance.situation; + } + + if (__instance.wasLadder != flag) + { + __instance.wasLadder = flag; + } + + return false; + } + + static List stagedPartList = new List(); + static HashSet vesselsToCheck = new HashSet(); + + static void StageManager_ActivateStage_Prefix(StageManager __instance, out int __state) + { + stagedPartList.AddRange(FlightGlobals.ActiveVessel.Parts); + __state = __instance._currentStage; + } + + static void StageManager_ActivateStage_Postfix(StageManager __instance, int __state) + { + if (__instance._currentStage != __state) + { + for (int i = stagedPartList.Count; i-- > 0;) + { + Vessel v = stagedPartList[i].vessel; + if (vesselsToCheck.Contains(v)) + continue; + + vesselsToCheck.Add(v); + } + + foreach (Vessel v in vesselsToCheck) + v.UpdateLandedSplashed(); + } + stagedPartList.Clear(); + vesselsToCheck.Clear(); + } + } +} diff --git a/KSPCommunityFixes/KSPCommunityFixes.csproj b/KSPCommunityFixes/KSPCommunityFixes.csproj index 3add4fd..0c231bb 100644 --- a/KSPCommunityFixes/KSPCommunityFixes.csproj +++ b/KSPCommunityFixes/KSPCommunityFixes.csproj @@ -91,6 +91,7 @@ + From 0a5e979b60237aa531b6ecc6ef135b0e9cb19738 Mon Sep 17 00:00:00 2001 From: NathanKell Date: Sat, 18 Jun 2022 21:28:30 -0700 Subject: [PATCH 2/2] Redo splashed fix to hit at the part level (in case parts decouple/die regardless of stage) --- .../BugFixes/StickySplashedFixer.cs | 64 +++++++++++-------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/KSPCommunityFixes/BugFixes/StickySplashedFixer.cs b/KSPCommunityFixes/BugFixes/StickySplashedFixer.cs index 4a5d8de..328aac4 100644 --- a/KSPCommunityFixes/BugFixes/StickySplashedFixer.cs +++ b/KSPCommunityFixes/BugFixes/StickySplashedFixer.cs @@ -28,28 +28,39 @@ protected override void ApplyPatches(List patches) patches.Add(new PatchInfo( PatchMethodType.Prefix, - AccessTools.Method(typeof(StageManager), "ActivateStage"), + AccessTools.Method(typeof(Part), "Die"), this)); patches.Add(new PatchInfo( PatchMethodType.Postfix, - AccessTools.Method(typeof(StageManager), "ActivateStage"), + AccessTools.Method(typeof(Part), "Die"), + this)); + + patches.Add(new PatchInfo( + PatchMethodType.Prefix, + AccessTools.Method(typeof(Part), "decouple"), + this)); + + patches.Add(new PatchInfo( + PatchMethodType.Postfix, + AccessTools.Method(typeof(Part), "decouple"), this)); } static bool Vessel_updateSituation_Prefix(Vessel __instance) { - bool flag; + bool evaOnLadderOnOtherVessel; if (__instance.EVALadderVessel != __instance) { __instance.situation = __instance.evaController.LadderPart.vessel.situation; - flag = true; + evaOnLadderOnOtherVessel = true; } else { - flag = false; + evaOnLadderOnOtherVessel = false; if (__instance.situation == Vessel.Situations.PRELAUNCH) { + // Slower speed for leaving prelaunch in water since, well, boats are slow. if (__instance.srfSpeed > (__instance.Splashed ? 1 : 2.5) && !__instance.precalc.isEasingGravity && !__instance.vesselSpawning) __instance.situation = __instance.Splashed ? Vessel.Situations.SPLASHED : Vessel.Situations.LANDED; } @@ -91,41 +102,40 @@ static bool Vessel_updateSituation_Prefix(Vessel __instance) __instance.lastSituation = __instance.situation; } - if (__instance.wasLadder != flag) + if (__instance.wasLadder != evaOnLadderOnOtherVessel) { - __instance.wasLadder = flag; + __instance.wasLadder = evaOnLadderOnOtherVessel; } return false; } - static List stagedPartList = new List(); - static HashSet vesselsToCheck = new HashSet(); + // We could optimize the below by setting up a coroutine that runs later in the frame + // so a vessel isn't processed more than once if, say, multiple parts detach on the + // same frame. But the landed/splash check isn't very expensive so I'm not worried. - static void StageManager_ActivateStage_Prefix(StageManager __instance, out int __state) + static void Part_die_Prefix(Part __instance, out Vessel __state) { - stagedPartList.AddRange(FlightGlobals.ActiveVessel.Parts); - __state = __instance._currentStage; + __state = __instance.vessel; } - static void StageManager_ActivateStage_Postfix(StageManager __instance, int __state) + static void Part_die_Postfix(Part __instance, Vessel __state) { - if (__instance._currentStage != __state) - { - for (int i = stagedPartList.Count; i-- > 0;) - { - Vessel v = stagedPartList[i].vessel; - if (vesselsToCheck.Contains(v)) - continue; + if (__state.IsNotNullOrDestroyed()) + __state.UpdateLandedSplashed(); + } - vesselsToCheck.Add(v); - } + static void Part_Decouple_Prefix(Part __instance, out Vessel __state) + { + __state = __instance.vessel; + } - foreach (Vessel v in vesselsToCheck) - v.UpdateLandedSplashed(); - } - stagedPartList.Clear(); - vesselsToCheck.Clear(); + static void Part_Decouple_Postfix(Part __instance, Vessel __state) + { + if (__state.IsNotNullOrDestroyed()) + __state.UpdateLandedSplashed(); + + // New vessel (on __instance) will run Initialize. } } }