1- using KSP . UI . Screens ;
1+ using KSP . Localization ;
2+ using KSP . UI . Screens ;
23using System ;
34using System . Collections . Generic ;
45using System . Linq ;
@@ -38,19 +39,10 @@ public static class Core
3839 /// </summary>
3940 public static Dictionary < string , HealthCondition > HealthConditions ;
4041
41- /// <summary>
42- /// Mod-wide random number generator
43- /// </summary>
44- internal static System . Random Rand = new System . Random ( ) ;
45-
4642 static readonly string [ ] prefixes = { "" , "K" , "M" , "G" , "T" } ;
4743
48- static float radStormTypesTotalWeight = 0 ;
49-
5044 static Dictionary < string , Vessel > kerbalVesselsCache = new Dictionary < string , Vessel > ( ) ;
5145
52- static List < float > trainingCaps ;
53-
5446 /// <summary>
5547 /// List of all tracked kerbals
5648 /// </summary>
@@ -63,59 +55,38 @@ public static class Core
6355 {
6456 new StressFactor ( ) ,
6557 new ConfinementFactor ( ) ,
66- new LonelinessFactor ( ) ,
6758 new MicrogravityFactor ( ) ,
68- new EVAFactor ( ) ,
69- new ConditionsFactor ( ) ,
59+ new LonelinessFactor ( ) ,
7060 new IsolationFactor ( ) ,
61+ new EVAFactor ( ) ,
7162 new HomeFactor ( ) ,
72- new KSCFactor ( )
63+ new KSCFactor ( ) ,
64+ new ConditionsFactor ( )
7365 } ;
7466
7567 /// <summary>
7668 /// Keeps data about all resources that provide Shielding. Key is resource id, value is amount of shielding provided by 1 unit
7769 /// </summary>
7870 public static Dictionary < int , double > ShieldingResources { get ; set ; } = new Dictionary < int , double > ( ) ;
7971
72+ /// <summary>
73+ /// List of all possible quirks
74+ /// </summary>
8075 public static List < Quirk > Quirks { get ; set ; } = new List < Quirk > ( ) ;
8176
8277 public static Dictionary < CelestialBody , PlanetHealthConfig > PlanetConfigs { get ; set ; }
8378
84- public static List < RadStormType > RadStormTypes { get ; set ; }
85-
86- public static double SolarCycleDuration { get ; set ; }
87-
88- public static double SolarCycleStartingPhase { get ; set ; }
89-
90- public static double RadStormMinMBTE { get ; set ; }
91-
92- public static double RadStormMaxMBTE { get ; set ; }
93-
94- public static double SolarCyclePhase => ( SolarCycleStartingPhase + Planetarium . GetUniversalTime ( ) / SolarCycleDuration ) % 1 ;
95-
96- public static double RadStormMTBE => RadStormMinMBTE + ( RadStormMaxMBTE - RadStormMinMBTE ) * ( Math . Sin ( 2 * Math . PI * ( SolarCyclePhase + 0.75 ) ) + 1 ) / 2 ;
97-
9879 /// <summary>
9980 /// True if the current scene is Editor (VAB or SPH)
10081 /// </summary>
10182 public static bool IsInEditor => HighLogic . LoadedSceneIsEditor ;
10283
103- /// <summary>
104- /// Max amount of stress reduced by training depending on Astronaut Complex's level
105- /// </summary>
106- public static double TrainingCap => trainingCaps [ ( int ) Math . Round ( ScenarioUpgradeableFacilities . GetFacilityLevel ( SpaceCenterFacility . AstronautComplex ) * 2 ) ] ;
107-
108- /// <summary>
109- /// Current <see cref="LogLevel"/>: either Debug or Important
110- /// </summary>
111- public static LogLevel Level => KerbalHealthGeneralSettings . Instance . DebugMode ? LogLevel . Debug : LogLevel . Important ;
112-
11384 /// <summary>
11485 /// Returns factor with a given id
11586 /// </summary>
11687 /// <param name="id">Factor id</param>
11788 /// <returns></returns>
118- public static HealthFactor GetHealthFactor ( string id ) => Factors . FirstOrDefault ( f => f . Name == id ) ;
89+ public static HealthFactor GetHealthFactor ( string id ) => Factors . Find ( f => f . Name == id ) ;
11990
12091 public static HealthCondition GetHealthCondition ( string s ) => HealthConditions . TryGetValue ( s , out HealthCondition value ) ? value : null ;
12192
@@ -135,6 +106,26 @@ public static PlanetHealthConfig GetPlanetConfig(string name)
135106 return cb != null && PlanetConfigs . TryGetValue ( cb , out PlanetHealthConfig res ) ? res : null ;
136107 }
137108
109+ public static float GetInternalFacilityLevel ( int displayFacilityLevel ) => ( float ) ( displayFacilityLevel - 1 ) / 2 ;
110+
111+ #region RAD STORMS
112+
113+ static float radStormTypesTotalWeight = 0 ;
114+
115+ public static List < RadStormType > RadStormTypes { get ; set ; }
116+
117+ public static float SolarCycleDuration { get ; set ; }
118+
119+ public static float SolarCycleStartingPhase { get ; set ; }
120+
121+ public static float RadStormMinMBTE { get ; set ; }
122+
123+ public static float RadStormMaxMBTE { get ; set ; }
124+
125+ public static float SolarCyclePhase => ( int ) ( SolarCycleStartingPhase + Planetarium . GetUniversalTime ( ) / SolarCycleDuration ) % 1 ;
126+
127+ public static float RadStormMTBE => RadStormMinMBTE + ( RadStormMaxMBTE - RadStormMinMBTE ) * ( float ) ( Math . Sin ( 2 * Math . PI * ( SolarCyclePhase + 0.75 ) ) + 1 ) / 2 ;
128+
138129 public static RadStormType GetRandomRadStormType ( )
139130 {
140131 double d = Rand . NextDouble ( ) * radStormTypesTotalWeight ;
@@ -147,6 +138,10 @@ public static RadStormType GetRandomRadStormType()
147138 return null ;
148139 }
149140
141+ #endregion
142+
143+ #region CREW UTILITIES
144+
150145 public static IList < ProtoCrewMember > GetCrew ( ProtoCrewMember pcm , bool entireVessel )
151146 {
152147 Vessel vessel = pcm . GetVessel ( ) ;
@@ -181,17 +176,18 @@ public static int GetColleaguesCount(ProtoCrewMember pcm) =>
181176 public static Part GetCrewPart ( this ProtoCrewMember pcm ) =>
182177 IsInEditor ? KSPUtil . GetPartByCraftID ( EditorLogic . SortedShipList , ShipConstruction . ShipManifest . GetPartForCrew ( pcm ) . PartID ) : pcm ? . seat ? . part ;
183178
184- public static string GetPartTitle ( string partName ) => PartLoader . getPartInfoByName ( partName ) ? . title ?? partName ;
179+ #endregion
180+
181+ public static string GetPartTitle ( string partName ) =>
182+ partName . StartsWith ( "kerbalEVA" ) ? Localizer . Format ( "#KH_SpacesuitPart" , partName ) : ( PartLoader . getPartInfoByName ( partName ) ? . title ?? partName ) ;
185183
186184 /// <summary>
187185 /// Returns true if the kerbal is in a loaded vessel
188186 /// </summary>
189- public static bool IsUnpacked ( this ProtoCrewMember pcm ) //=> pcm.GetVessel()?.loaded ?? false;
187+ public static bool IsUnpacked ( this ProtoCrewMember pcm )
190188 {
191189 Vessel vessel = pcm . GetVessel ( ) ;
192- if ( vessel == null )
193- return false ;
194- return vessel . loaded && ! vessel . packed ;
190+ return vessel != null && vessel . loaded && ! vessel . packed ;
195191 }
196192
197193 /// <summary>
@@ -234,37 +230,66 @@ public static Vessel GetVessel(this ProtoCrewMember pcm)
234230 return vessel ;
235231 }
236232
233+ public static bool IsPlanet ( this CelestialBody body ) => body ? . orbit ? . referenceBody == Sun . Instance . sun ;
234+
235+ public static CelestialBody GetPlanet ( this CelestialBody body ) => body == null || body . IsPlanet ( ) ? body : body . orbit ? . referenceBody ? . GetPlanet ( ) ;
236+
237237 public static double GetDistanceToSun ( this Vessel v ) =>
238238 v . mainBody == Sun . Instance . sun
239239 ? v . altitude + Sun . Instance . sun . Radius
240240 : ( v . distanceToSun > 0 ? v . distanceToSun : v . mainBody . GetPlanet ( ) . orbit . altitude + Sun . Instance . sun . Radius ) ;
241241
242+ public static float GetScienceMultiplier ( this Vessel vessel , bool ignoreLandedOnHomebody = true )
243+ {
244+ CelestialBody body = vessel ? . mainBody ;
245+ if ( body == null )
246+ {
247+ Log ( $ "Could not get science multiplier for { vessel ? . vesselName } .", LogLevel . Error ) ;
248+ return 0 ;
249+ }
250+ if ( ignoreLandedOnHomebody && body . isHomeWorld && vessel . LandedOrSplashed )
251+ return 0 ;
252+ if ( vessel . Landed )
253+ return body . scienceValues . LandedDataValue ;
254+ if ( vessel . Splashed )
255+ return body . scienceValues . SplashedDataValue ;
256+ if ( ( vessel . situation & Vessel . Situations . FLYING ) != 0 )
257+ return vessel . altitude < body . scienceValues . flyingAltitudeThreshold ? body . scienceValues . FlyingLowDataValue : body . scienceValues . FlyingHighDataValue ;
258+ return vessel . altitude < body . scienceValues . InSpaceHighDataValue ? body . scienceValues . InSpaceLowDataValue : body . scienceValues . InSpaceHighDataValue ;
259+ }
260+
261+ #region TRAINING
262+
263+ static List < float > trainingCaps ;
264+
242265 /// <summary>
243- /// Returns a list of part modules that are used in stress calculations
266+ /// Max amount of stress reduced by training depending on Astronaut Complex's level
244267 /// </summary>
245- public static List < ModuleKerbalHealth > GetTrainableParts ( IList < Part > allParts ) =>
246- allParts . SelectMany ( part => part . FindModulesImplementing < ModuleKerbalHealth > ( ) ) . Where ( mkh => mkh . complexity != 0 ) . ToList ( ) ;
268+ public static float KSCTrainingCap => trainingCaps [ ( int ) Math . Round ( ScenarioUpgradeableFacilities . GetFacilityLevel ( SpaceCenterFacility . AstronautComplex ) * 2 ) ] ;
269+
270+ public const float InFlightTrainingCap = 1 ;
247271
248272 /// <summary>
249- /// Returns a list of *distinct* part modules that are used in training
273+ /// Returns a list of unique part modules that are used in training & stress calculations
250274 /// </summary>
251- public static List < ModuleKerbalHealth > GetTrainablePartTypes ( IList < Part > allParts )
275+ public static List < ModuleKerbalHealth > GetTrainableModules ( this IEnumerable < Part > allParts )
252276 {
253277 List < ModuleKerbalHealth > res = new List < ModuleKerbalHealth > ( ) ;
254- foreach ( ModuleKerbalHealth mkh in GetTrainableParts ( allParts ) )
255- if ( ! res . Any ( mkh2 => mkh . PartName == mkh2 . PartName ) )
256- res . Add ( mkh ) ;
278+ foreach ( Part part in allParts )
279+ foreach ( ModuleKerbalHealth mkh in part . FindModulesImplementing < ModuleKerbalHealth > ( ) . Where ( mkh => mkh . complexity != 0 ) )
280+ {
281+ if ( ! res . Any ( mkh2 => mkh2 . PartName == mkh . PartName ) )
282+ res . Add ( mkh ) ;
283+ break ;
284+ }
257285 return res ;
258286 }
259287
260- public static bool HasTrainableParts ( IEnumerable < Part > allParts ) => allParts . Any ( part => part . FindModulesImplementing < ModuleKerbalHealth > ( ) . Any ( mkh => mkh . complexity != 0 ) ) ;
288+ public static bool AnyTrainableParts ( IEnumerable < Part > allParts ) => allParts . Any ( part => part . FindModulesImplementing < ModuleKerbalHealth > ( ) . Any ( mkh => mkh . complexity != 0 ) ) ;
261289
262- public static float GetInternalFacilityLevel ( int displayFacilityLevel ) => ( float ) ( displayFacilityLevel - 1 ) / 2 ;
290+ #endregion
263291
264- public static bool IsPlanet ( this CelestialBody body ) => body ? . orbit ? . referenceBody == Sun . Instance . sun ;
265-
266- public static CelestialBody GetPlanet ( this CelestialBody body ) =>
267- body == null || body . IsPlanet ( ) ? body : body . orbit ? . referenceBody ? . GetPlanet ( ) ;
292+ #region CONFIG NODE UTILITIES
268293
269294 public static string GetString ( this ConfigNode n , string key , string defaultValue = null )
270295 {
@@ -288,6 +313,15 @@ public static uint GetUInt(this ConfigNode n, string key, uint defaultValue = 0)
288313 public static bool GetBool ( this ConfigNode n , string key , bool defaultValue = false ) =>
289314 bool . TryParse ( n . GetValue ( key ) , out bool res ) ? res : defaultValue ;
290315
316+ #endregion
317+
318+ #region MATH & RNG UTILITIES
319+
320+ /// <summary>
321+ /// Mod-wide random number generator
322+ /// </summary>
323+ internal static System . Random Rand = new System . Random ( ) ;
324+
291325 /// <summary>
292326 /// Returns x*x
293327 /// </summary>
@@ -301,22 +335,24 @@ public static double GetGaussian(double stdDev = 1, double mean = 0) =>
301335
302336 public static double EventChance ( double mtbe , double interval ) => 1 - Math . Exp ( - interval / mtbe ) ;
303337
304- public static bool EventHappens ( double mtbe , double interval ) => mtbe >= 0 && Rand . NextDouble ( ) < EventChance ( mtbe , interval ) ;
338+ public static bool EventHappens ( double mtbe , double interval ) => ( mtbe > 0 && Rand . NextDouble ( ) < EventChance ( mtbe , interval ) ) || mtbe == 0 ;
339+
340+ #endregion
341+
342+ #region NUMBERS AND STRINGS
305343
306344 /// <summary>
307345 /// Returns a string of a value with a mandatory sign (+ or -, unless v = 0)
308346 /// </summary>
309347 /// <param name="value">Value to present as a string</param>
310348 /// <param name="format">String format according to Double.ToString</param>
311- /// <returns></returns>
312349 public static string SignValue ( double value , string format ) => ( value > 0 ? "+" : "" ) + value . ToString ( format ) ;
313350
314351 /// <summary>
315352 /// Converts a number into a string with a multiplicative character (K, M, G or T), if applicable
316353 /// </summary>
317354 /// <param name="value">The value to convert</param>
318355 /// <param name="digits">Number of digits to allow before the prefix (must be 3 or more)</param>
319- /// <returns></returns>
320356 public static string PrefixFormat ( double value , int digits = 3 , bool mandatorySign = false )
321357 {
322358 double v = Math . Abs ( value ) ;
@@ -386,6 +422,8 @@ public static string ParseUT(double time, bool showSeconds = true, int daysTimeL
386422 return res . Trim ( ) ;
387423 }
388424
425+ #endregion
426+
389427 public static void ShowMessage ( string msg , bool unwarpTime )
390428 {
391429 MessageSystem . Instance . AddMessage ( new MessageSystem . Message (
@@ -431,20 +469,12 @@ public static void LoadConfig()
431469
432470 int i = 0 ;
433471 foreach ( ConfigNode n in config . GetNodes ( "PLANET_HEALTH_CONFIG" ) )
434- {
435- PlanetHealthConfig bc = GetPlanetConfig ( n . GetString ( "name" ) ) ;
436- if ( bc != null )
437- {
438- bc . Load ( n ) ;
439- i ++ ;
440- }
441- }
442- Log ( $ "{ i } planet configs out of { PlanetConfigs . Count } bodies loaded.", LogLevel . Important ) ;
472+ GetPlanetConfig ( n . GetString ( "name" ) ) ? . Load ( n ) ;
443473
444- SolarCycleDuration = config . GetDouble ( "solarCycleDuration" , 11 ) * KSPUtil . dateTimeFormatter . Year ;
445- SolarCycleStartingPhase = config . GetDouble ( "solarCycleStartingPhase" ) ;
446- RadStormMinMBTE = config . GetDouble ( "radStormMinMBTE" , 426 ) ;
447- RadStormMaxMBTE = config . GetDouble ( "radStormMaxMBTE" , 6390 ) ;
474+ SolarCycleDuration = config . GetFloat ( "solarCycleDuration" , 11 ) * KSPUtil . dateTimeFormatter . Year ;
475+ SolarCycleStartingPhase = config . GetFloat ( "solarCycleStartingPhase" ) ;
476+ RadStormMinMBTE = config . GetFloat ( "radStormMinMBTE" , 426 ) ;
477+ RadStormMaxMBTE = config . GetFloat ( "radStormMaxMBTE" , 6390 ) ;
448478
449479 RadStormTypes = new List < RadStormType > ( ) ;
450480 i = 0 ;
@@ -457,9 +487,9 @@ public static void LoadConfig()
457487
458488 trainingCaps = new List < float > ( 3 )
459489 {
460- 0.40f ,
461- 0.60f ,
462- 0.75f
490+ 0.30f ,
491+ 0.50f ,
492+ 0.60f
463493 } ;
464494 foreach ( ConfigNode n in config . GetNodes ( "TRAINING_CAPS" ) )
465495 {
@@ -472,6 +502,13 @@ public static void LoadConfig()
472502 ConfigLoaded = true ;
473503 }
474504
505+ #region LOG
506+
507+ /// <summary>
508+ /// Current <see cref="LogLevel"/>: either Debug or Important
509+ /// </summary>
510+ public static LogLevel Level => KerbalHealthGeneralSettings . Instance . DebugMode ? LogLevel . Debug : LogLevel . Important ;
511+
475512 /// <summary>
476513 /// Returns true if current logging allows logging of messages at messageLevel
477514 /// </summary>
@@ -493,5 +530,7 @@ internal static void Log(string message, LogLevel messageLevel = LogLevel.Debug)
493530 Debug . Log ( $ "[KerbalHealth] { message } ") ;
494531 }
495532 }
533+
534+ #endregion
496535 }
497536}
0 commit comments