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..328aac4 --- /dev/null +++ b/KSPCommunityFixes/BugFixes/StickySplashedFixer.cs @@ -0,0 +1,141 @@ +/* +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(Part), "Die"), + this)); + + patches.Add(new PatchInfo( + PatchMethodType.Postfix, + 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 evaOnLadderOnOtherVessel; + if (__instance.EVALadderVessel != __instance) + { + __instance.situation = __instance.evaController.LadderPart.vessel.situation; + evaOnLadderOnOtherVessel = true; + } + else + { + 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; + } + 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 != evaOnLadderOnOtherVessel) + { + __instance.wasLadder = evaOnLadderOnOtherVessel; + } + + return false; + } + + // 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 Part_die_Prefix(Part __instance, out Vessel __state) + { + __state = __instance.vessel; + } + + static void Part_die_Postfix(Part __instance, Vessel __state) + { + if (__state.IsNotNullOrDestroyed()) + __state.UpdateLandedSplashed(); + } + + static void Part_Decouple_Prefix(Part __instance, out Vessel __state) + { + __state = __instance.vessel; + } + + static void Part_Decouple_Postfix(Part __instance, Vessel __state) + { + if (__state.IsNotNullOrDestroyed()) + __state.UpdateLandedSplashed(); + + // New vessel (on __instance) will run Initialize. + } + } +} 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 @@ +