diff --git a/CKAN/SystemHeat-CryoTanks.netkan b/CKAN/SystemHeat-CryoTanks.netkan new file mode 100644 index 0000000..1ef19ee --- /dev/null +++ b/CKAN/SystemHeat-CryoTanks.netkan @@ -0,0 +1,29 @@ +{ + "spec_version": "v1.4", + "identifier": "SystemHeat-CryoTanks", + "name": "System Heat - Cryo Tanks Configuration", + "$kref": "#/ckan/github/post-kerbin-mining-corporation/SystemHeat", + "$vref": "#/ckan/ksp-avc", + "abstract": "This System Heat config package changes zero boiloff cryogenic tanks to need cooling instead of power. WARNING: potentially vessel-breaking.", + "author": "Nertea (Chris Adderley)", + "license": "CC-BY-NC-SA-4.0", + "resources": { + "homepage": "https://forum.kerbalspaceprogram.com/index.php?/topic/193909-1", + "repository": "https://github.com/post-kerbin-mining-corporation/SystemHeat" + }, + "depends": [ + { "name": "ModuleManager" }, + { "name": "SystemHeat" }, + { "name": "CommunityResourcePack" } + ], + "suggests": [ + { "name": "SystemHeatConverters" }, + { "name": "SystemHeatFissionReactors" }, + { "name": "SystemHeatHarvesters" }, + { "name": "NearFuturePropulsion" } + ], + "install": [ { + "find": "SystemHeatBoiloff", + "install_to": "GameData" + } ] +} diff --git a/Extras/SystemHeatBoiloff/CryoTanks/CryoTanksSystemHeat.cfg b/Extras/SystemHeatBoiloff/CryoTanks/CryoTanksSystemHeat.cfg new file mode 100644 index 0000000..272d23b --- /dev/null +++ b/Extras/SystemHeatBoiloff/CryoTanks/CryoTanksSystemHeat.cfg @@ -0,0 +1,39 @@ +@PART[*]:HAS[@MODULE[ModuleCryoTank]]:AFTER[zzz_CryoTanks] +{ + MODULE + { + name = ModuleSystemHeat + volume = #$../mass$ + volume *= 0.5 + moduleID = tank + iconName = Icon_Snow + } + + @MODULE[ModuleCryoTank] + { + + @name = ModuleSystemHeatCryoTank + systemHeatModuleID = tank + + @BOILOFFCONFIG:HAS[#FuelName[LqdHydrogen]] + { + CoolingHeatCost = 0.3 + CryocoolerTemperature = 300 + } + @BOILOFFCONFIG:HAS[#FuelName[LqdMethane]] + { + CoolingHeatCost = 0.15 + CryocoolerTemperature = 400 + } + @BOILOFFCONFIG:HAS[#FuelName[LqdHe3]] + { + CoolingHeatCost = 0.22 + CryocoolerTemperature = 350 + } + @BOILOFFCONFIG:HAS[#FuelName[LqdDeuterium]] + { + CoolingHeatCost = 0.25 + CryocoolerTemperature = 300 + } + } +} \ No newline at end of file diff --git a/Extras/SystemHeatConverters/genericConverters.cfg b/Extras/SystemHeatConverters/genericConverters.cfg index 4aa7ea5..adbbbdc 100644 --- a/Extras/SystemHeatConverters/genericConverters.cfg +++ b/Extras/SystemHeatConverters/genericConverters.cfg @@ -25,10 +25,12 @@ key = 1000 1.0 key = 1300 0.0 } + systemPower = #$INPUT_RESOURCE:HAS[#ResourceName[ElectricCharge]]/Ratio$ //set the heat ouput as proportional to the EC usage. @systemPower *= .90 //lets assume 90% of EC usage turns into heat. !ThermalEfficiency {} !TemperatureModifier {} @GeneratesHeat = false //disable stock heating + } } diff --git a/Extras/SystemHeatFissionReactors/NearFutureElectrical/NearFutureElectricalUSIReactors.cfg b/Extras/SystemHeatFissionReactors/NearFutureElectrical/NearFutureElectricalUSIReactors.cfg new file mode 100644 index 0000000..02a976a --- /dev/null +++ b/Extras/SystemHeatFissionReactors/NearFutureElectrical/NearFutureElectricalUSIReactors.cfg @@ -0,0 +1,772 @@ +// Near Future Electrical USI Reactors compatibility +// Changes USI reactors to behave like NFE reactors +@PART[USI_Nuke_625]:FOR[SystemHeatFissionReactors] +{ + MODULE + { + name = ModuleSystemHeat + volume = 0.25 + moduleID = reactor + iconName = Icon_Nuclear + } + + !MODULE[ModuleUpdateOverride] {} + !MODULE[FissionReactor] {} + !MODULE[FissionGenerator] {} + !MODULE[ModuleCoreHeatNoCatchup] {} + !MODULE[RadioactiveStorageContainer] {} + MODULE + { + name = ModuleUpdateOverride + } + + MODULE + { + name = ModuleSystemHeatFissionReactor + moduleID = reactor + + // -- Heat stuff + // ModuleSystemHeat instance to link to + systemHeatModuleID = reactor + + // Heat kW + HeatGeneration + { + key = 0 0 0 0 + key = 100 136 0 0 + } + + // Above this temp, risky + NominalTemperature = 750 + + // Above this temp, reactor takes damage + CriticalTemperature = 1300 + + // Amount of damage taken by core when over critical temp + // %/s/K, so with value 0.001, at 200 K over CriticalTemp, reactor takes 0.2% damage/s + CoreDamageRate = 0.005 + + // When repairing, amount of core damage to heal (%) + RepairAmountPerKit = 25 + + CurrentPowerPercent = 100 + ThrottleIncreaseRate = 5 + MinimumThrottle = 25 + + // -- Electrical stuff + // Power generated + ElectricalGeneration + { + key = 0 0 + key = 100 36 + } + + // --- Fuel stuff + // Base lifetime calculations off this resource + FuelName = EnrichedUranium + + INPUT_RESOURCE + { + ResourceName = EnrichedUranium + Ratio = 0.000000126785 + FlowMode = NO_FLOW + } + + OUTPUT_RESOURCE + { + ResourceName = DepletedFuel + Ratio = 0.000000126785 + DumpExcess = false + FlowMode = NO_FLOW + } + + EngineerLevelForRepair = 5 + MaxRepairPercent = 75 + MaxTempForRepair = 330 + } + + MODULE + { + name = ModuleSystemHeatFissionFuelContainer + EngineerLevelForTransfer = 3 + ResourceNames = EnrichedUranium, DepletedFuel + } +} + +@PART[USI_Nuke_125_S]:FOR[SystemHeatFissionReactors] +{ + MODULE + { + name = ModuleSystemHeat + volume = 0.5 + moduleID = reactor + iconName = Icon_Nuclear + } + + !MODULE[ModuleUpdateOverride] {} + !MODULE[FissionReactor] {} + !MODULE[FissionGenerator] {} + !MODULE[ModuleCoreHeatNoCatchup] {} + !MODULE[RadioactiveStorageContainer] {} + + MODULE + { + name = ModuleSystemHeatFissionReactor + moduleID = reactor + + // -- Heat stuff + // ModuleSystemHeat instance to link to + systemHeatModuleID = reactor + + // Heat kW + HeatGeneration + { + key = 0 0 0 0 + key = 100 325 0 0 + } + + // Above this temp, risky + NominalTemperature = 775 + + // Above this temp, reactor takes damage + CriticalTemperature = 1300 + + // Amount of damage taken by core when over critical temp + // %/s/K, so with value 0.001, at 200 K over CriticalTemp, reactor takes 0.2% damage/s + CoreDamageRate = 0.01 + + // When repairing, amount of core damage to heal (%) + RepairAmountPerKit = 20 + + CurrentPowerPercent = 100 + ThrottleIncreaseRate = 5 + MinimumThrottle = 25 + + // -- Electrical stuff + // Power generated + ElectricalGeneration + { + key = 0 0 + key = 100 100 + } + + // --- Fuel stuff + // Base lifetime calculations off this resource + FuelName = EnrichedUranium + + INPUT_RESOURCE + { + ResourceName = EnrichedUranium + Ratio = 0.00000034414 + FlowMode = NO_FLOW + } + + OUTPUT_RESOURCE + { + ResourceName = DepletedFuel + Ratio = 0.00000034414 + DumpExcess = false + FlowMode = NO_FLOW + } + } + + MODULE + { + name = ModuleSystemHeatFissionFuelContainer + EngineerLevelForTransfer = 3 + ResourceNames = EnrichedUranium, DepletedFuel + } +} + +@PART[USI_Nuke_125]:FOR[SystemHeatFissionReactors] +{ + MODULE + { + name = ModuleSystemHeat + volume = 1 + moduleID = reactor + iconName = Icon_Nuclear + } + + !MODULE[ModuleUpdateOverride] {} + !MODULE[FissionReactor] {} + !MODULE[FissionGenerator] {} + !MODULE[ModuleCoreHeatNoCatchup] {} + !MODULE[RadioactiveStorageContainer] {} + + MODULE + { + name = ModuleSystemHeatFissionReactor + moduleID = reactor + + // -- Heat stuff + // ModuleSystemHeat instance to link to + systemHeatModuleID = reactor + + // Heat kW + HeatGeneration + { + key = 0 0 0 0 + key = 100 650 0 0 + } + + // Above this temp, risky + NominalTemperature = 800 + + // Above this temp, reactor takes damage + CriticalTemperature = 1300 + + // Amount of damage taken by core when over critical temp + // %/s/K, so with value 0.001, at 200 K over CriticalTemp, reactor takes 0.2% damage/s + CoreDamageRate = 0.01 + + // When repairing, amount of core damage to heal (%) + RepairAmountPerKit = 20 + + CurrentPowerPercent = 100 + ThrottleIncreaseRate = 5 + MinimumThrottle = 25 + + // -- Electrical stuff + // Power generated + ElectricalGeneration + { + key = 0 0 + key = 100 200 + } + + // --- Fuel stuff + // Base lifetime calculations off this resource + FuelName = EnrichedUranium + + INPUT_RESOURCE + { + ResourceName = EnrichedUranium + Ratio = 0.00000065205 + FlowMode = NO_FLOW + } + + OUTPUT_RESOURCE + { + ResourceName = DepletedFuel + Ratio = 0.00000065205 + DumpExcess = false + FlowMode = NO_FLOW + } + } + + MODULE + { + name = ModuleSystemHeatFissionFuelContainer + EngineerLevelForTransfer = 3 + ResourceNames = EnrichedUranium, DepletedFuel + } +} + +@PART[USI_Nuke_250]:FOR[SystemHeatFissionReactors] +{ + MODULE + { + name = ModuleSystemHeat + volume = 2 + moduleID = reactor + iconName = Icon_Nuclear + } + + !MODULE[ModuleUpdateOverride] {} + !MODULE[FissionReactor] {} + !MODULE[FissionGenerator] {} + !MODULE[ModuleCoreHeatNoCatchup] {} + !MODULE[RadioactiveStorageContainer] {} + + + MODULE + { + name = ModuleSystemHeatFissionReactor + moduleID = reactor + + // -- Heat stuff + // ModuleSystemHeat instance to link to + systemHeatModuleID = reactor + + // Heat kW + HeatGeneration + { + key = 0 0 0 0 + key = 100 2900 0 0 + } + + // Above this temp, risky + NominalTemperature = 850 + // Above this temp, reactor takes damage + CriticalTemperature = 1300 + // Amount of damage taken by core when over critical temp + // %/s/K, so with value 0.001, at 200 K over CriticalTemp, reactor takes 0.2% damage/s + CoreDamageRate = 0.01 + + // When repairing, amount of core damage to heal (%) + RepairAmountPerKit = 15 + + CurrentPowerPercent = 100 + ThrottleIncreaseRate = 5 + MinimumThrottle = 25 + + // -- Electrical stuff + // Power generated + ElectricalGeneration + { + key = 0 0 + key = 100 1000 + } + + // --- Fuel stuff + // Base lifetime calculations off this resource + FuelName = EnrichedUranium + + INPUT_RESOURCE + { + ResourceName = EnrichedUranium + Ratio = 0.0000027169 + FlowMode = NO_FLOW + } + + OUTPUT_RESOURCE + { + ResourceName = DepletedFuel + Ratio = 0.0000027169 + DumpExcess = false + FlowMode = NO_FLOW + } + } + MODULE + { + name = ModuleSystemHeatFissionFuelContainer + EngineerLevelForTransfer = 3 + ResourceNames = EnrichedUranium, DepletedFuel + } +} + +@PART[USI_Nuke_375]:FOR[SystemHeatFissionReactors] +{ + MODULE + { + name = ModuleSystemHeat + volume = 4 + moduleID = reactor + iconName = Icon_Nuclear + } + + !MODULE[ModuleUpdateOverride] {} + !MODULE[FissionReactor] {} + !MODULE[FissionGenerator] {} + !MODULE[ModuleCoreHeatNoCatchup] {} + !MODULE[RadioactiveStorageContainer] {} + + MODULE + { + name = ModuleSystemHeatFissionReactor + moduleID = reactor + + // -- Heat stuff + // ModuleSystemHeat instance to link to + systemHeatModuleID = reactor + + // Heat kW + HeatGeneration + { + key = 0 0 0 0 + key = 100 9000 0 0 + } + + // Above this temp, risky + NominalTemperature = 900 + + // Above this temp, reactor takes damage + CriticalTemperature = 1300 + + // Amount of damage taken by core when over critical temp + // %/s/K, so with value 0.001, at 200 K over CriticalTemp, reactor takes 0.2% damage/s + CoreDamageRate = 0.01 + + // When repairing, amount of core damage to heal (%) + RepairAmountPerKit = 10 + + CurrentPowerPercent = 100 + ThrottleIncreaseRate = 5 + MinimumThrottle = 25 + + // -- Electrical stuff + // Power generated + ElectricalGeneration + { + key = 0 0 + key = 100 4000 + } + + // --- Fuel stuff + // Base lifetime calculations off this resource + FuelName = EnrichedUranium + + INPUT_RESOURCE + { + ResourceName = EnrichedUranium + Ratio = 0.000010143 + FlowMode = NO_FLOW + } + + OUTPUT_RESOURCE + { + ResourceName = DepletedFuel + Ratio = 0.000010143 + DumpExcess = false + FlowMode = NO_FLOW + } + } + + MODULE + { + name = ModuleSystemHeatFissionFuelContainer + EngineerLevelForTransfer = 3 + ResourceNames = EnrichedUranium, DepletedFuel + } +} + +@PART[FTT_Service_375_01]:FOR[SystemHeatFissionReactors] +{ + MODULE + { + name = ModuleSystemHeat + volume = 4 + moduleID = reactor + iconName = Icon_Nuclear + } + + !MODULE[ModuleUpdateOverride] {} + !MODULE[FissionReactor] {} + !MODULE[FissionGenerator] {} + !MODULE[ModuleCoreHeatNoCatchup] {} + !MODULE[RadioactiveStorageContainer] {} + + MODULE + { + name = ModuleSystemHeatFissionReactor + moduleID = reactor + + // -- Heat stuff + // ModuleSystemHeat instance to link to + systemHeatModuleID = reactor + + // Heat kW + HeatGeneration + { + key = 0 0 0 0 + key = 100 8000 0 0 + } + + // Above this temp, risky + NominalTemperature = 900 + + // Above this temp, reactor takes damage + CriticalTemperature = 1300 + + // Amount of damage taken by core when over critical temp + // %/s/K, so with value 0.001, at 200 K over CriticalTemp, reactor takes 0.2% damage/s + CoreDamageRate = 0.005 + + // When repairing, amount of core damage to heal (%) + RepairAmountPerKit = 10 + + CurrentPowerPercent = 100 + ThrottleIncreaseRate = 5 + MinimumThrottle = 25 + + // -- Electrical stuff + // Power generated + ElectricalGeneration + { + key = 0 0 + key = 100 4500 + } + + // --- Fuel stuff + // Base lifetime calculations off this resource + FuelName = EnrichedUranium + + INPUT_RESOURCE + { + ResourceName = EnrichedUranium + Ratio = 0.0000089658 + FlowMode = NO_FLOW + } + + OUTPUT_RESOURCE + { + ResourceName = DepletedFuel + Ratio = 0.0000089658 + DumpExcess = false + FlowMode = NO_FLOW + } + } + + MODULE + { + name = ModuleSystemHeatFissionFuelContainer + EngineerLevelForTransfer = 3 + ResourceNames = EnrichedUranium, DepletedFuel + } +} + +@PART[FTT_Reactor_500_01]:FOR[SystemHeatFissionReactors] +{ + MODULE + { + name = ModuleSystemHeat + volume = 8 + moduleID = reactor + iconName = Icon_Nuclear + } + + !MODULE[ModuleUpdateOverride] {} + !MODULE[FissionReactor] {} + !MODULE[FissionGenerator] {} + !MODULE[ModuleCoreHeatNoCatchup] {} + !MODULE[RadioactiveStorageContainer] {} + + MODULE + { + name = ModuleSystemHeatFissionReactor + moduleID = reactor + + // -- Heat stuff + // ModuleSystemHeat instance to link to + systemHeatModuleID = reactor + + // Heat kW + HeatGeneration + { + key = 0 0 0 0 + key = 100 16000 0 0 + } + + // Above this temp, risky + NominalTemperature = 950 + + // Above this temp, reactor takes damage + CriticalTemperature = 1300 + + // Amount of damage taken by core when over critical temp + // %/s/K, so with value 0.001, at 200 K over CriticalTemp, reactor takes 0.2% damage/s + CoreDamageRate = 0.005 + + // When repairing, amount of core damage to heal (%) + RepairAmountPerKit = 10 + + CurrentPowerPercent = 100 + ThrottleIncreaseRate = 5 + MinimumThrottle = 25 + + // -- Electrical stuff + // Power generated + ElectricalGeneration + { + key = 0 0 + key = 100 8000 + } + + // --- Fuel stuff + // Base lifetime calculations off this resource + FuelName = EnrichedUranium + + INPUT_RESOURCE + { + ResourceName = EnrichedUranium + Ratio = 0.0000190184 + FlowMode = NO_FLOW + } + + OUTPUT_RESOURCE + { + ResourceName = DepletedFuel + Ratio = 0.0000190184 + DumpExcess = false + FlowMode = NO_FLOW + } + } + + MODULE + { + name = ModuleSystemHeatFissionFuelContainer + EngineerLevelForTransfer = 3 + ResourceNames = EnrichedUranium, DepletedFuel + } +} + +@PART[Duna_PDU]:FOR[SystemHeatFissionReactors] +{ + MODULE + { + name = ModuleSystemHeat + volume = 2 + moduleID = reactor + iconName = Icon_Nuclear + } + + !MODULE[ModuleUpdateOverride] {} + !MODULE[FissionReactor] {} + !MODULE[FissionGenerator] {} + !MODULE[ModuleCoreHeatNoCatchup] {} + !MODULE[RadioactiveStorageContainer] {} + + MODULE + { + name = ModuleSystemHeatFissionReactor + moduleID = reactor + + // -- Heat stuff + // ModuleSystemHeat instance to link to + systemHeatModuleID = reactor + + // Heat kW + HeatGeneration + { + key = 0 0 0 0 + key = 100 3250 0 0 + } + + // Above this temp, risky + NominalTemperature = 750 + + // Above this temp, reactor takes damage + CriticalTemperature = 1300 + + // Amount of damage taken by core when over critical temp + // %/s/K, so with value 0.001, at 200 K over CriticalTemp, reactor takes 0.2% damage/s + CoreDamageRate = 0.005 + + // When repairing, amount of core damage to heal (%) + RepairAmountPerKit = 15 + + CurrentPowerPercent = 100 + ThrottleIncreaseRate = 5 + MinimumThrottle = 25 + + // -- Electrical stuff + // Power generated + ElectricalGeneration + { + key = 0 0 + key = 100 750 + } + + // --- Fuel stuff + // Base lifetime calculations off this resource + FuelName = EnrichedUranium + + INPUT_RESOURCE + { + ResourceName = EnrichedUranium + Ratio = 0.00000326 + FlowMode = NO_FLOW + } + + OUTPUT_RESOURCE + { + ResourceName = DepletedFuel + Ratio = 0.00000326 + DumpExcess = false + FlowMode = NO_FLOW + } + } + + MODULE + { + name = ModuleSystemHeatFissionFuelContainer + EngineerLevelForTransfer = 3 + ResourceNames = EnrichedUranium, DepletedFuel + } +} + +@PART[Tundra_PDU]:FOR[SystemHeatFissionReactors] +{ + MODULE + { + name = ModuleSystemHeat + volume = 4 + moduleID = reactor + iconName = Icon_Nuclear + } + + !MODULE[ModuleUpdateOverride] {} + !MODULE[FissionReactor] {} + !MODULE[FissionGenerator] {} + !MODULE[ModuleCoreHeatNoCatchup] {} + !MODULE[RadioactiveStorageContainer] {} + + MODULE + { + name = ModuleSystemHeatFissionReactor + moduleID = reactor + + // -- Heat stuff + // ModuleSystemHeat instance to link to + systemHeatModuleID = reactor + + // Heat kW + HeatGeneration + { + key = 0 0 0 0 + key = 100 4700 0 0 + } + + // Above this temp, risky + NominalTemperature = 900 + + // Above this temp, reactor takes damage + CriticalTemperature = 1300 + + // Amount of damage taken by core when over critical temp + // %/s/K, so with value 0.001, at 200 K over CriticalTemp, reactor takes 0.2% damage/s + CoreDamageRate = 0.005 + + // When repairing, amount of core damage to heal (%) + RepairAmountPerKit = 10 + + CurrentPowerPercent = 100 + ThrottleIncreaseRate = 5 + MinimumThrottle = 25 + + // -- Electrical stuff + // Power generated + ElectricalGeneration + { + key = 0 0 + key = 100 1200 + } + + // --- Fuel stuff + // Base lifetime calculations off this resource + FuelName = EnrichedUranium + + INPUT_RESOURCE + { + ResourceName = EnrichedUranium + Ratio = 0.00000652 + FlowMode = NO_FLOW + } + + OUTPUT_RESOURCE + { + ResourceName = DepletedFuel + Ratio = 0.00000652 + DumpExcess = false + FlowMode = NO_FLOW + } + } + + MODULE + { + name = ModuleSystemHeatFissionFuelContainer + EngineerLevelForTransfer = 3 + ResourceNames = EnrichedUranium, DepletedFuel + } +} diff --git a/Extras/SystemHeatHarvesters/genericHarvesters.cfg b/Extras/SystemHeatHarvesters/genericHarvesters.cfg index 9d896ed..6afa681 100644 --- a/Extras/SystemHeatHarvesters/genericHarvesters.cfg +++ b/Extras/SystemHeatHarvesters/genericHarvesters.cfg @@ -11,7 +11,8 @@ !MODULE[ModuleOverheatDisplay]{} - @MODULE[ModuleResourceHarvester]:HAS[@INPUT_RESOURCE:HAS[#ResourceName[ElectricCharge]]]{ //Ive seen some converters that dont use electric charge, those things need thier own patch, as we calculate produced heat based on EC consumption. + @MODULE[ModuleResourceHarvester]:HAS[@INPUT_RESOURCE:HAS[#ResourceName[ElectricCharge]]] { + //Ive seen some converters that dont use electric charge, those things need thier own patch, as we calculate produced heat based on consumption. @name = ModuleSystemHeatHarvester moduleID = #$ConverterName$ //this should be the localization string or the actual name, either way, unique. systemHeatModuleID = harvester @@ -24,10 +25,9 @@ key = 650 0.0 } systemPower = #$INPUT_RESOURCE:HAS[#ResourceName[ElectricCharge]]/Ratio$ - @systemPower *= #$Efficiency$ //set the heat ouput as proportional to the EC usage. // I *think* that all the energy goes into heat. - !Thermalefficiency {} + !ThermalEfficiency {} !TemperatureModifier {} - @generatesHeat = false //disable stock heating + @GeneratesHeat = false //disable stock heating } } diff --git a/Extras/SystemHeatIonEngines/NearFuturePropulsion/NFPSystemHeatVASIMR.cfg b/Extras/SystemHeatIonEngines/NearFuturePropulsion/NFPSystemHeatVASIMR.cfg index ef07445..2e25261 100644 --- a/Extras/SystemHeatIonEngines/NearFuturePropulsion/NFPSystemHeatVASIMR.cfg +++ b/Extras/SystemHeatIonEngines/NearFuturePropulsion/NFPSystemHeatVASIMR.cfg @@ -18,7 +18,7 @@ { name = ModuleSystemHeatEngine // must be unique - moduleID = engine + moduleID = engineA // ModuleSystemHeat to link to systemHeatModuleID = engine @@ -41,7 +41,7 @@ { name = ModuleSystemHeatEngine // must be unique - moduleID = engine + moduleID = engineX // ModuleSystemHeat to link to systemHeatModuleID = engine @@ -81,7 +81,7 @@ { name = ModuleSystemHeatEngine // must be unique - moduleID = engine + moduleID = engineA // ModuleSystemHeat to link to systemHeatModuleID = engine @@ -105,7 +105,7 @@ { name = ModuleSystemHeatEngine // must be unique - moduleID = engine + moduleID = engineX // ModuleSystemHeat to link to systemHeatModuleID = engine @@ -145,7 +145,7 @@ { name = ModuleSystemHeatEngine // must be unique - moduleID = engine + moduleID = engineA // ModuleSystemHeat to link to systemHeatModuleID = engine @@ -169,7 +169,7 @@ { name = ModuleSystemHeatEngine // must be unique - moduleID = engine + moduleID = engineX // ModuleSystemHeat to link to systemHeatModuleID = engine diff --git a/GameData/SystemHeat/Localization/en-us.cfg b/GameData/SystemHeat/Localization/en-us.cfg index 7d124ee..e522ad4 100644 --- a/GameData/SystemHeat/Localization/en-us.cfg +++ b/GameData/SystemHeat/Localization/en-us.cfg @@ -10,8 +10,8 @@ Localization #LOC_SystemHeat_ToolbarPanel_OutgoingFluxTitle = Total Generation #LOC_SystemHeat_ToolbarPanel_IncomingFluxTile = Total Rejection - #LOC_SystemHeat_ToolbarPanel_OutgoingFluxValue = <<1>> kW - #LOC_SystemHeat_ToolbarPanel_IncomingFluxValue = <<1>> kW + #LOC_SystemHeat_ToolbarPanel_OutgoingFluxValue = <<1>>W + #LOC_SystemHeat_ToolbarPanel_IncomingFluxValue = <<1>>W #LOC_SystemHeat_ToolbarPanel_LoopCountTitle = Loop Count #LOC_SystemHeat_ToolbarPanel_LoopCountValue = <<1>> @@ -38,13 +38,13 @@ Localization #LOC_SystemHeat_ToolbarPanel_LoopTemperatureTitle = • Temperature #LOC_SystemHeat_ToolbarPanel_LoopTemperatureValue = <<1>>/<<2>> K #LOC_SystemHeat_ToolbarPanel_LoopFluxTitle = • Net Flux - #LOC_SystemHeat_ToolbarPanel_LoopFluxValue = <<1>><<2>> kW + #LOC_SystemHeat_ToolbarPanel_LoopFluxValue = <<1>><<2>>W // OVERLAY PANEL // ------------ - #LOC_SystemHeat_OverlayPanel_UpperText = Temperature Output <<1>> K \nWaste Heat <<2>> kW - #LOC_SystemHeat_OverlayPanel_UpperTextNoTemp = Waste Heat <<1>> kW - #LOC_SystemHeat_OverlayPanel_LowerText = Loop Status\n Temperature <<1>>/<<2>> K \n Net Flux <<3>> kW \n Volume <<4>> m³ + #LOC_SystemHeat_OverlayPanel_UpperText = Temperature Output <<1>> K \nWaste Heat <<2>>W + #LOC_SystemHeat_OverlayPanel_UpperTextNoTemp = Waste Heat <<1>>W + #LOC_SystemHeat_OverlayPanel_LowerText = Loop Status\n Temperature <<1>>/<<2>> K \n Net Flux <<3>>W \n Volume <<4>> m³ // REACTOR TOOLBAR // ------------ @@ -52,7 +52,7 @@ Localization #LOC_SystemHeat_ReactorPanel_NoReactors = No reactors detected on vessel #LOC_SystemHeat_ReactorPanel_ReactorOnToggleLabel = ON #LOC_SystemHeat_ReactorPanel_Field_HeatGeneratedTitle = Heat Generated - #LOC_SystemHeat_ReactorPanel_Field_HeatGenerated = <<1>> kW + #LOC_SystemHeat_ReactorPanel_Field_HeatGenerated = <<1>>W #LOC_SystemHeat_ReactorPanel_Field_PowerGeneratedTitle = Power Generated #LOC_SystemHeat_ReactorPanel_Field_PowerGenerated = <<1>> Ec/s #LOC_SystemHeat_ReactorPanel_Field_CoreLifeTitle = Core Life @@ -64,12 +64,13 @@ Localization // ENGINEER'S REPORT // ------------ #LOC_SystemHeat_EngineerReport_LoopFluxTest_ConcernTitle = Loops Need Additional Cooling Capacity - #LOC_SystemHeat_EngineerReport_LoopFluxTest_ConcernDescription = Loop <<1>> has a net heat flux of <<2>> kW! <<3>> + #LOC_SystemHeat_EngineerReport_LoopFluxTest_ConcernDescription = Loop <<1>> has a net heat flux of <<2>>W! <<3>> #LOC_SystemHeat_EngineerReport_LoopTemperatureTest_ConcernTitle = Loop Temperature Too High #LOC_SystemHeat_EngineerReport_LoopTemperatureTest_ConcernDescription = One or more loops has a nominal temperature that is too high for its members! #LOC_SystemHeat_Units_K = K + #LOC_SystemHeat_Units_W = W #LOC_SystemHeat_Units_kW = kW // PART MODULES @@ -88,25 +89,25 @@ Localization // ModuleSystemHeatRadiator /// ================================= #LOC_SystemHeat_ModuleSystemHeatRadiator_DisplayName = Heat Radiator - #LOC_SystemHeat_ModuleSystemHeatRadiator_PartInfo = Cools systems on vessels by pumping coolant through radiating surfaces.\n\nThermal Parameters:\n - Radiates <<1>> kW at <<2>> K\n - Radiates <<3>> kW at <<4>> K\n \n\nLegacy Core Heat Parameters + #LOC_SystemHeat_ModuleSystemHeatRadiator_PartInfo = Cools systems on vessels by pumping coolant through radiating surfaces.\n\nThermal Parameters:\n - Radiates <<1>>W at <<2>> K\n - Radiates <<3>>W at <<4>> K\n \n\nLegacy Core Heat Parameters #LOC_SystemHeat_ModuleSystemHeatRadiator_GroupName = Heat Radiator #LOC_SystemHeat_ModuleSystemHeatRadiator_RadiatorStatus_Title = Radiative Flux - #LOC_SystemHeat_ModuleSystemHeatRadiator_RadiatorStatus_Running = <<1>> kW + #LOC_SystemHeat_ModuleSystemHeatRadiator_RadiatorStatus_Running = <<1>>W #LOC_SystemHeat_ModuleSystemHeatRadiator_RadiatorEfficiency_Title = Radiator Efficiency #LOC_SystemHeat_ModuleSystemHeatRadiator_RadiatorEfficiency_Running = <<1>>% #LOC_SystemHeat_ModuleSystemHeatRadiator_RadiatorEfficiency_Offline = Offline #LOC_SystemHeat_ModuleSystemHeatRadiator_ConvectionStatus_Title = Convective Flux - #LOC_SystemHeat_ModuleSystemHeatRadiator_ConvectionStatus_Running = <<1>> kW + #LOC_SystemHeat_ModuleSystemHeatRadiator_ConvectionStatus_Running = <<1>>W // ModuleSystemHeatEngine /// ================================= #LOC_SystemHeat_ModuleSystemHeatEngine_DisplayName = Engine Heat - #LOC_SystemHeat_ModuleSystemHeatEngine_PartInfo = Thermal Parameters:\n - Waste Heat: <<1>> kW\n -Outlet Temperature: <<2>> K\n -Max. Temperature <<3>> K + #LOC_SystemHeat_ModuleSystemHeatEngine_PartInfo = Thermal Parameters:\n - Waste Heat: <<1>>W\n -Outlet Temperature: <<2>> K\n -Max. Temperature <<3>> K #LOC_SystemHeat_ModuleSystemHeatEngine_Field_HeatGeneration = Engine Heat Generation - #LOC_SystemHeat_ModuleSystemHeatEngine_Field_HeatGeneration_Running = <<1>> kW + #LOC_SystemHeat_ModuleSystemHeatEngine_Field_HeatGeneration_Running = <<1>>W #LOC_SystemHeat_ModuleSystemHeatEngine_Field_Temperature = Engine System Temperature #LOC_SystemHeat_ModuleSystemHeatEngine_Field_Temperature_Running = <<1>>/<<2>> K #LOC_SystemHeat_ModuleSystemHeatEngine_Message_Overheat = Engine system maximum temperature of <<1>> K was exceeded on <<2>>! Emergency shutdown! @@ -115,7 +116,7 @@ Localization /// ================================= #LOC_SystemHeat_ModuleSystemHeatFissionReactor_UIGroup_Title = Fission Reactor #LOC_SystemHeat_ModuleSystemHeatFissionReactor_ModuleName = Fission Reactor - #LOC_SystemHeat_ModuleSystemHeatFissionReactor_PartInfo = Electrical Power: <<1>> Ec/s\nResponse Speed: <<8>>%/s\nMinimum Power: <<9>>%\nCore Life: <<2>>\n\nThermal Parameters:\n - Waste Heat: <<3>> kW\n - Outlet Temperature: <<4>> K\n - Optimal Temp.: <<5>> K \n - Damage Temp.: <<6>> K\n - Meltdown Temp.: <<7>> K + #LOC_SystemHeat_ModuleSystemHeatFissionReactor_PartInfo = Electrical Power: <<1>> Ec/s\nResponse Speed: <<8>>%/s\nMinimum Power: <<9>>%\nCore Life: <<2>>\n\nThermal Parameters:\n - Waste Heat: <<3>>W\n - Outlet Temperature: <<4>> K\n - Optimal Temp.: <<5>> K \n - Damage Temp.: <<6>> K\n - Meltdown Temp.: <<7>> K #LOC_SystemHeat_ModuleSystemHeatFissionReactor_Action_TogglePanelAction = Toggle Reactor Panel #LOC_SystemHeat_ModuleSystemHeatFissionReactor_Action_StartActionName = Start Reactor @@ -169,14 +170,14 @@ Localization #LOC_SystemHeat_ModuleSystemHeatFissionReactor_Action_TogglePanelAction = Toggle Reactor Panel - #LOC_SystemHeat_ModuleSystemHeatFissionEngine_PartInfo = Generates thrust through nuclear fission. \n\nElectrical Power <<1>> Ec/s\nResponse Speed: <<8>>%/s\nMinimum Power: <<9>>%\nCore Life: <<2>>\n\nThermal Parameters:\n - Waste Heat: <<3>> kW\n - Exhaust Cooling: <<10>>%\n - Outlet Temperature: <<4>> K\n\n - Optimal Temp.: <<5>> K \n - Damage Temp.: <<6>> K\n - Meltdown Temp.: <<7>> K + #LOC_SystemHeat_ModuleSystemHeatFissionEngine_PartInfo = Generates thrust through nuclear fission. \n\nElectrical Power <<1>> Ec/s\nResponse Speed: <<8>>%/s\nMinimum Power: <<9>>%\nCore Life: <<2>>\n\nThermal Parameters:\n - Waste Heat: <<3>>W\n - Exhaust Cooling: <<10>>%\n - Outlet Temperature: <<4>> K\n\n - Optimal Temp.: <<5>> K \n - Damage Temp.: <<6>> K\n - Meltdown Temp.: <<7>> K #LOC_SystemHeat_ModuleSystemHeatFissionReactor_Action_TogglePanelAction = Toggle Reactor Panel - #LOC_SystemHeat_ModuleSystemHeatFissionEngine_PartInfo_NoPower = Generates thrust through nuclear fission. \n\nResponse Speed: <<7>>%/s\nMinimum Power: <<8>>%\nCore Life: <<1>>\n\nThermal Parameters:\n - Waste Heat: <<2>> kW\n - Exhaust Cooling: <<9>>%\n - Outlet Temperature: <<3>> K\n - Optimal Temp.: <<>> K \n - Damage Temp.: <<5>> K\n - Meltdown Temp.: <<6>> K + #LOC_SystemHeat_ModuleSystemHeatFissionEngine_PartInfo_NoPower = Generates thrust through nuclear fission. \n\nResponse Speed: <<7>>%/s\nMinimum Power: <<8>>%\nCore Life: <<1>>\n\nThermal Parameters:\n - Waste Heat: <<2>>W\n - Exhaust Cooling: <<9>>%\n - Outlet Temperature: <<3>> K\n - Optimal Temp.: <<>> K \n - Damage Temp.: <<5>> K\n - Meltdown Temp.: <<6>> K #LOC_SystemHeat_ModuleSystemHeatFissionReactor_Action_TogglePanelAction = Toggle Reactor Panel #LOC_SystemHeat_ModuleSystemHeatFissionEngine_Field_CurrentExhaustCooling = Exhaust Cooling - #LOC_SystemHeat_ModuleSystemHeatFissionEngine_Field_CurrentExhaustCooling_Running = <<1>> kW + #LOC_SystemHeat_ModuleSystemHeatFissionEngine_Field_CurrentExhaustCooling_Running = <<1>>W #LOC_SystemHeat_ModuleSystemHeatFissionEngine_Field_CurrentExhaustCooling_EngineOff = Engine Off #LOC_SystemHeat_ModuleSystemHeatFissionEngine_Message_Shutdown = Reactor shutdown on <<1>>, engine shutdown... @@ -209,12 +210,12 @@ Localization /// ================================= #LOC_SystemHeat_ModuleSystemHeatSink_UIGroup_Title = Heat Sink #LOC_SystemHeat_ModuleSystemHeatSink_ModuleName = Heat Sink - #LOC_SystemHeat_ModuleSystemHeatSink_PartInfo = Stores or releases heat on demand. \n\nThermal Parameters:\n - Max. Heat Stored: <<1>> MJ \n - Heat Storage Rate: <<2>> kW + #LOC_SystemHeat_ModuleSystemHeatSink_PartInfo = Stores or releases heat on demand. \n\nThermal Parameters:\n - Max. Heat Stored: <<1>> MJ \n - Heat Storage Rate: <<2>>W #LOC_SystemHeat_ModuleSystemHeatSink_Field_HeatStorageDumpRate = Dump Rate #LOC_SystemHeat_ModuleSystemHeatSink_Field_SystemHeatGeneration = Storage Accepted #LOC_SystemHeat_ModuleSystemHeatSink_Field_SystemHeatGeneration_Dump = Storage Dumping - #LOC_SystemHeat_ModuleSystemHeatSink_Field_SystemHeatGeneration_Storing = <<1>>/<<2>> kW + #LOC_SystemHeat_ModuleSystemHeatSink_Field_SystemHeatGeneration_Storing = <<1>>/<<2>>W #LOC_SystemHeat_ModuleSystemHeatSink_Field_SystemTemperature = Storage Temperature #LOC_SystemHeat_ModuleSystemHeatSink_Field_SystemTemperature_Running = <<1>> K @@ -228,20 +229,20 @@ Localization /// ModuleSystemHeatExchanger /// ================================= #LOC_SystemHeat_ModuleSystemHeatExchanger_ModuleName = Heat Exchanger - #LOC_SystemHeat_ModuleSystemHeatExchanger_UIGroup_Title = Heat Exchanger - #LOC_SystemHeat_ModuleSystemHeatExchanger_PartInfo = Transfers heat from one loop to another, optionally modifying the second loop's temperature. \n\nPower Used\n - ΔT of <<1>> K needs <<2>> Ec/s\n - ΔT of <<3>> K needs <<4>> Ec/s\n\nWaste Heat Produced\n - ΔT of <<1>> K generates <<5>> kW\n - ΔT of <<3>> K generates <<6>> kW + #LOC_SystemHeat_ModuleSystemHeatExchanger_UIGroup_Title = Heat Exchanger, optionally modifying the second loop's temperature. \n\nPower Used\n - ΔT of <<1>> K needs <<2>> Ec/s\n - ΔT of <<3>> K needs <<4>> Ec/s\n\nWaste Heat Produced\n - ΔT of <<1>> K generates <<5>>W\n - ΔT of <<3>> K generates <<6>>W + #LOC_SystemHeat_ModuleSystemHeatExchanger_PartInfo = Transfers heat from one loop to another #LOC_SystemHeat_ModuleSystemHeatExchanger_Field_Direction = Transfer Direction #LOC_SystemHeat_ModuleSystemHeatExchanger_Field_Direction_String = Loop <<1>> -> Loop <<2>> #LOC_SystemHeat_ModuleSystemHeatExchanger_Field_Status = Exchanger Status - #LOC_SystemHeat_ModuleSystemHeatExchanger_Field_Status_Active = Output Temperature: <<1>> K\nOutput Flux: <<2>> kW + #LOC_SystemHeat_ModuleSystemHeatExchanger_Field_Status_Active = Output Temperature: <<1>> K\nOutput Flux: <<2>>W #LOC_SystemHeat_ModuleSystemHeatExchanger_Field_Status_NoPower = Not enough ElectricCharge! #LOC_SystemHeat_ModuleSystemHeatExchanger_Field_Status_TooHot = Destination loop overheated! #LOC_SystemHeat_ModuleSystemHeatExchanger_Field_Status_Disabled = Disabled #LOC_SystemHeat_ModuleSystemHeatExchanger_Field_MaxHeatTransfer = Max Heat Transfer #LOC_SystemHeat_ModuleSystemHeatExchanger_Field_TemperatureAdjust = Temperature Adjustment #LOC_SystemHeat_ModuleSystemHeatExchanger_Field_PowerCost = Power Usage - #LOC_SystemHeat_ModuleSystemHeatExchanger_Field_PowerCost_Active = <<1>> kW + #LOC_SystemHeat_ModuleSystemHeatExchanger_Field_PowerCost_Active = <<1>>W #LOC_SystemHeat_ModuleSystemHeatExchanger_Event_EnableExchanger = Enable Exchanger #LOC_SystemHeat_ModuleSystemHeatExchanger_Event_DisableExchanger = Disable Exchanger @@ -254,7 +255,7 @@ Localization /// ModuleSystemHeatBaseConverterAdapter /// ================================= - #LOC_SystemHeat_ModuleSystemHeatBaseConverterAdapter_PartInfo = \n\nThermal Parameters:\n - Heat Generated: <<1>> kW\n - Outlet Temperature: <<2>> K\n - Max. Temperature: <<3>> K + #LOC_SystemHeat_ModuleSystemHeatBaseConverterAdapter_PartInfo = \n\nThermal Parameters:\n - Heat Generated: <<1>>W\n - Outlet Temperature: <<2>> K\n - Max. Temperature: <<3>> K #LOC_SystemHeat_ModuleSystemHeatBaseConverterAdapter_ModuleName = Taco Module #LOC_SystemHeat_ModuleSystemHeatBaseConverterAdapter_Message_Shutdown = Converter overheated on <<1>>! Emergency shutdown! @@ -262,7 +263,7 @@ Localization /// ModuleSystemHeatConverter /// ================================= - #LOC_SystemHeat_ModuleSystemHeatConverter_PartInfoAdd = \n\nThermal Parameters:\n - Heat Generated: <<1>> kW\n - Outlet Temperature: <<2>> K\n - Max. Temperature: <<3>> K + #LOC_SystemHeat_ModuleSystemHeatConverter_PartInfoAdd = \n\nThermal Parameters:\n - Heat Generated: <<1>>W\n - Outlet Temperature: <<2>> K\n - Max. Temperature: <<3>> K #LOC_SystemHeat_ModuleSystemHeatConverter_Field_Efficiency = Efficiency [<<1>>] #LOC_SystemHeat_ModuleSystemHeatConverter_Field_Efficiency_Value = <<1>>% @@ -272,13 +273,44 @@ Localization /// ModuleSystemHeatHarvester /// ================================= - #LOC_SystemHeat_ModuleSystemHeatHarvester_PartInfoAdd = \n\nThermal Parameters:\n - Heat Generated: <<1>> kW\n - Outlet Temperature: <<2>> K\n - Max. Temperature: <<3>> K + #LOC_SystemHeat_ModuleSystemHeatHarvester_PartInfoAdd = \n\nThermal Parameters:\n - Heat Generated: <<1>>W\n - Outlet Temperature: <<2>> K\n - Max. Temperature: <<3>> K #LOC_SystemHeat_ModuleSystemHeatHarvester_Field_Efficiency = Efficiency #LOC_SystemHeat_ModuleSystemHeatHarvester_Field_Efficiency_Value = <<1>>% #LOC_SystemHeat_ModuleSystemHeatHarvester_Field_SimulateEditor = Editor Simulate [<<1>>] #LOC_SystemHeat_ModuleSystemHeatHarvester_Message_Shutdown = Harvester overheated on <<1>>! Emergency shutdown! + /// ModuleSystemCryoTank + /// ================================= + + #LOC_SystemHeat_ModuleSystemHeatCryoTank_ModuleName = Cryogenic Tank + #LOC_SystemHeat_ModuleSystemHeatCryoTank_PartInfoUncooled = Cryogenic fuels evaporate over time\n + #LOC_SystemHeat_ModuleSystemHeatCryoTank_PartInfoCooled = Cryogenic fuels evaporate over time if uncooled<<1>> + #LOC_SystemHeat_ModuleSystemHeatCryoTank_PartInfoBoiloff = \n<<1>>:\n - Boiloff Rate: <<2>>%/hr\n - Heat Generated: <<3>>W per 1000 units\n - Max. Temperature: <<4>> K\n + #LOC_SystemHeat_ModuleSystemHeatCryoTank_PartInfoBoiloffOutput = \n- Generates <<1>>: +<<2>>%/hr + + #LOC_SystemHeat_ModuleSystemHeatCryoTank_UIGroup_Title = Cryogenic Tank + + #LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_BoiloffStatus = Boiloff + #LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_BoiloffStatus_NoFuel = No Fuel + #LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_BoiloffStatus_Insulated = Stable + #LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_BoiloffStatus_Boiloff = Losing <<1>> u/<<2>> + + #LOC_SystemHeat_ModuleSystemHeatCryoTank_TimeInterval_Second_Abbrev = s + #LOC_SystemHeat_ModuleSystemHeatCryoTank_TimeInterval_Hour_Abbrev = hr + #LOC_SystemHeat_ModuleSystemHeatCryoTank_TimeInterval_Minute_Abbrev = min + + #LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_CoolingStatus = Cooling + #LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_CoolingStatus_Editor = Waste Heat: <<1>>W + #LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_CoolingStatus_NoFuel = No Fuel + #LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_CoolingStatus_Disabled = Disabled + #LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_CoolingStatus_Uncooled = Uncooled + #LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_CoolingStatus_Cooling = Waste Heat: <<1>>W + + #LOC_SystemHeat_ModuleSystemHeatCryoTank_Event_Enable = Enable Cooling + #LOC_SystemHeat_ModuleSystemHeatCryoTank_Event_Disable = Disable Cooling + + // PARTS // ------------ #LOC_SystemHeat_manufacturer_pkmc_title = Post-Kebin Mining Corporation diff --git a/GameData/SystemHeat/Plugin/SystemHeat.dll b/GameData/SystemHeat/Plugin/SystemHeat.dll index 297b0d7..fb44574 100644 Binary files a/GameData/SystemHeat/Plugin/SystemHeat.dll and b/GameData/SystemHeat/Plugin/SystemHeat.dll differ diff --git a/GameData/SystemHeat/Settings.cfg b/GameData/SystemHeat/Settings.cfg index 07153a2..9fdf295 100644 --- a/GameData/SystemHeat/Settings.cfg +++ b/GameData/SystemHeat/Settings.cfg @@ -8,10 +8,10 @@ SYSTEMHEAT OverlayPadding = 0.4 // Debug Settings - DebugUI = true - DebugOverlay = true + DebugUI = false + DebugOverlay = false DebugSimulation = false - DebugModules = true + DebugModules = false DebugPartUI = false diff --git a/GameData/SystemHeat/Versioning/SystemHeat.version b/GameData/SystemHeat/Versioning/SystemHeat.version index 01b90ff..9b30533 100644 --- a/GameData/SystemHeat/Versioning/SystemHeat.version +++ b/GameData/SystemHeat/Versioning/SystemHeat.version @@ -5,15 +5,15 @@ "VERSION": { "MAJOR":0, - "MINOR":5, - "PATCH":6, + "MINOR":6, + "PATCH":0, "BUILD":0 }, "KSP_VERSION": { "MAJOR":1, "MINOR":12, - "PATCH":2 + "PATCH":3 }, "KSP_VERSION_MIN":{ "MAJOR":1, diff --git a/README.md b/README.md index bc9b85d..bbd102b 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Please note that these extras may break your ships in flight as they will make e * **SystemHeatConverters**: Converts converters to use SystemHeat * **SystemHeatFissionReactors**: Converts nuclear reactors to use SystemHeat * **SystemHeatFissionEngines**: When installed, nuclear engines will have integrated reactors +* **SystemHeatCryoTanks**: When installed, tanks configured by CryoTanks will not use power and instead require cooling ## Contributing diff --git a/SystemHeat/SystemHeat/HeatLoop.cs b/SystemHeat/SystemHeat/HeatLoop.cs index 3be8208..623377d 100644 --- a/SystemHeat/SystemHeat/HeatLoop.cs +++ b/SystemHeat/SystemHeat/HeatLoop.cs @@ -114,11 +114,8 @@ public void RemoveHeatModule(ModuleSystemHeat heatModule) Volume -= heatModule.volume; modules.Remove(heatModule); - // Recalculate the nominal temperature NominalTemperature = CalculateNominalTemperature(); - - } public void ResetTemperatures() @@ -126,7 +123,8 @@ public void ResetTemperatures() Temperature = GetEnvironmentTemperature(); for (int i = 0; i < modules.Count; i++) { - modules[i].currentLoopTemperature = GetEnvironmentTemperature(); + if (modules[i].moduleUsed) + modules[i].currentLoopTemperature = GetEnvironmentTemperature(); } } @@ -170,7 +168,8 @@ protected float CalculateNetFlux() float currentNetFlux = 0f; for (int i = 0; i < modules.Count; i++) { - currentNetFlux += modules[i].totalSystemFlux; + if (modules[i].moduleUsed) + currentNetFlux += modules[i].totalSystemFlux; } return currentNetFlux; } @@ -183,7 +182,8 @@ protected float CalculatePositiveFlux() float currentNetFlux = 0f; for (int i = 0; i < modules.Count; i++) { - currentNetFlux = modules[i].totalSystemFlux > 0 ? modules[i].totalSystemFlux + currentNetFlux : currentNetFlux; + if (modules[i].moduleUsed) + currentNetFlux = modules[i].totalSystemFlux > 0 ? modules[i].totalSystemFlux + currentNetFlux : currentNetFlux; } return currentNetFlux; } @@ -246,7 +246,7 @@ void SimulateIteration(float simTimeStep) { Temperature += deltaTemperatureIdeal; } - + else { // do nothing @@ -258,7 +258,8 @@ void SimulateIteration(float simTimeStep) // Propagate to all modules for (int i = 0; i < modules.Count; i++) { - modules[i].UpdateSimulationValues(NominalTemperature, Temperature, NetFlux); + if (modules[i].moduleUsed) + modules[i].UpdateSimulationValues(NominalTemperature, Temperature, NetFlux); } } protected void AllocateFlux(float totalFlux) @@ -296,6 +297,7 @@ protected void AllocateFlux(float totalFlux) } void SimulateConvection(float simTimeStep) { + ConvectionTemperature = simulator.AtmoSim.ExternalTemperature; ConvectionFlux = SystemHeatSettings.ConvectionBaseCoefficient * simulator.AtmoSim.ConvectiveCoefficient * simTimeStep; } @@ -335,7 +337,8 @@ protected float CalculateLoopVolume() float total = 0f; for (int i = 0; i < modules.Count; i++) { - total += modules[i].volume; + if (modules[i].moduleUsed) + total += modules[i].volume; } return total; } @@ -350,7 +353,7 @@ protected float CalculateNominalTemperature() for (int i = 0; i < modules.Count; i++) { - if (!modules[i].ignoreTemperature && modules[i].volume >= 0f && modules[i].totalSystemFlux >= 0f) + if (modules[i].moduleUsed && !modules[i].ignoreTemperature && modules[i].volume >= 0f && modules[i].totalSystemFlux >= 0f) { temp += modules[i].systemNominalTemperature * modules[i].volume; totalVolume += modules[i].volume; @@ -362,5 +365,19 @@ protected float CalculateNominalTemperature() else return GetEnvironmentTemperature(); } + + public int GetActiveModuleCount() + { + int count = 0; + for (int i = 0; i < modules.Count; i++) + { + if (modules[i].moduleUsed) + { + count++; + } + } + return count; + } + } } diff --git a/SystemHeat/SystemHeat/Modules/BoiloffFuel.cs b/SystemHeat/SystemHeat/Modules/BoiloffFuel.cs new file mode 100644 index 0000000..615bb5a --- /dev/null +++ b/SystemHeat/SystemHeat/Modules/BoiloffFuel.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; + +namespace SystemHeat +{ + /// + /// Represents a fuel resource on a part that boils off + /// + [System.Serializable] + public class BoiloffFuel + { + /// + /// Fuel name + /// + public string fuelName; + + /// + /// Boiloff rate in %/hr + /// + public float boiloffRate; + + /// + /// Boiloff temp + /// + public float cryoTemperature; + + /// + /// Cooling cost in kW/1000u + /// + public float coolingHeatCost; + + public PartResource resource; + public List outputs; + public float boiloffRateSeconds = 0f; + + public bool fuelPresent = false; + int id = -1; + Part part; + + /// + /// Constructor + /// + /// + /// + public BoiloffFuel(ConfigNode node, Part p) + { + part = p; + node.TryGetValue("FuelName", ref fuelName); + node.TryGetValue("BoiloffRate", ref boiloffRate); + node.TryGetValue("CryocoolerTemperature", ref cryoTemperature); + node.TryGetValue("CoolingHeatCost", ref coolingHeatCost); + + + ConfigNode[] outNodes = node.GetNodes("OUTPUT_RESOURCE"); + + if (outNodes.Length > 0 || outputs == null) + outputs = new List(); + { + for (int i = 0; i < outNodes.Length; i++) + { + ResourceRatio r = new ResourceRatio(); + r.Load(outNodes[i]); + outputs.Add(r); + } + } + } + + /// + /// Initialize the fuel + /// + public void Initialize() + { + id = PartResourceLibrary.Instance.GetDefinition(fuelName).id; + resource = part.Resources.Get(id); + boiloffRateSeconds = boiloffRate / 100f / 3600f; + fuelPresent = true; + } + + /// + /// Returns the max amount of the fuel on the part + /// + /// + public double FuelAmountMax() + { + if (fuelPresent) + return resource.maxAmount; + return 0d; + } + + /// + /// Returns the amount of the fuel on the part + /// + /// + public double FuelAmount() + { + if (fuelPresent) + return resource.amount; + return 0d; + } + + /// + /// Returns the heating cost to cool the fuel + /// + /// + public float FuelCoolingHeatCost() + { + if (fuelPresent) + return coolingHeatCost; + return 0f; + } + + /// + /// Returns the cryo temp of the fuel + /// + /// + public float FuelCryocoolerTemperature() + { + if (fuelPresent) + return cryoTemperature; + return -1f; + } + + + + /// + /// Returns the cost to cool fuel not considering current contents + /// + /// + public float FuelConfiguredCoolingHeatCost() + { + return coolingHeatCost; + } + + /// + /// Appplies boiloff, given a time interval and a scale + /// + /// + /// + public void Boiloff(double seconds, double scale) + { + if (fuelPresent) + { + double toBoil = resource.amount * (1.0 - Math.Pow(1.0 - boiloffRateSeconds, seconds)) * scale; + + double boilResult = part.RequestResource(resource.info.id, toBoil, ResourceFlowMode.NO_FLOW); + + /// Generate outputs + if (outputs.Count > 0) + { + for (int i = 0; i < outputs.Count; i++) + { + part.RequestResource(outputs[i].ResourceName, -boilResult * outputs[i].Ratio, outputs[i].FlowMode); + } + } + } + } + } + +} diff --git a/SystemHeat/SystemHeat/Modules/ModuleSystemHeat.cs b/SystemHeat/SystemHeat/Modules/ModuleSystemHeat.cs index 2a05921..e7997c3 100644 --- a/SystemHeat/SystemHeat/Modules/ModuleSystemHeat.cs +++ b/SystemHeat/SystemHeat/Modules/ModuleSystemHeat.cs @@ -10,6 +10,7 @@ namespace SystemHeat /// The simulation interface to the heat system. All heat producing or consuming modules /// on a vessel interact with an instance of this module to add and remove heat. /// + public class ModuleSystemHeat : PartModule { // Unique name of the module on a part @@ -31,6 +32,9 @@ public class ModuleSystemHeat : PartModule [KSPField(isPersistant = false)] public int priority = 1; + // Whether this module should be used by the heat system at all + public bool moduleUsed = true; + // -- System level data storage -- // Current total system temperature of all associated modules [KSPField(isPersistant = true, guiActive = false, guiName = "System Temp")] @@ -41,8 +45,6 @@ public class ModuleSystemHeat : PartModule public float totalSystemFlux = 0f; public float consumedSystemFlux = 0f; - - public float systemNominalTemperature = 0f; // -- Loop level data storage -- @@ -82,7 +84,8 @@ public class ModuleSystemHeat : PartModule public HeatLoop Loop { - get { + get + { if (simulator != null) { return simulator.Loop(currentLoopID); @@ -165,7 +168,7 @@ public void Start() Fields["currentLoopFlux"].guiActive = SystemHeatSettings.DebugPartUI; Fields["currentLoopFlux"].guiActiveEditor = SystemHeatSettings.DebugPartUI; - + Utils.Log("[ModuleSystemHeat]: Setup complete", LogType.Modules); } @@ -315,7 +318,7 @@ public float GetFlux(string id) return fluxes.Where(x => x.Key != id).Sum(x => x.Value) * (float)(PhysicsGlobals.InternalHeatProductionFactor / 0.025d); - + } return 0f; } @@ -327,34 +330,85 @@ public void UpdateSimulationValues(float nominalTemp, float currentTemp, float c currentLoopFlux = currentNetFlux; } - protected void FixedUpdate() + public void SetSystemHeatModuleEnabled(bool enabled) { - SystemFluxUI = String.Format("{0:F0} kW", totalSystemFlux); - LoopTemperatureUI = String.Format("{0:F0} / {1:F0} K", currentLoopTemperature, nominalLoopTemperature); - if (totalSystemFlux > 0f) + if (simulator == null) + FindSimulator(); + + + if (enabled && !moduleUsed) { + Utils.Log($"[ModuleSystemHeat] seting module {moduleID} system state from {moduleUsed} to {enabled}", LogType.Modules); + moduleUsed = enabled; + if (simulator != null) + simulator.AddHeatModule(this); + + // turn things on Fields["SystemTemperatureUI"].guiActive = true; Fields["SystemTemperatureUI"].guiActiveEditor = true; - SystemTemperatureUI = String.Format("{0:F0} K", totalSystemTemperature); + Fields["SystemFluxUI"].guiActive = true; + Fields["SystemFluxUI"].guiActiveEditor = true; + Fields["LoopTemperatureUI"].guiActive = true; + Fields["LoopTemperatureUI"].guiActiveEditor = true; + Fields["currentLoopID"].guiActive = true; + Fields["currentLoopID"].guiActiveEditor = true; } - else + if (!enabled && moduleUsed) { + Utils.Log($"[ModuleSystemHeat] seting module {moduleID} system state from {moduleUsed} to {enabled}", LogType.Modules); + moduleUsed = enabled; + if (simulator != null) + simulator.RemoveHeatModule(this); + + // turn things off Fields["SystemTemperatureUI"].guiActive = false; Fields["SystemTemperatureUI"].guiActiveEditor = false; + Fields["SystemFluxUI"].guiActive = false; + Fields["SystemFluxUI"].guiActiveEditor = false; + Fields["LoopTemperatureUI"].guiActive = false; + Fields["LoopTemperatureUI"].guiActiveEditor = false; + Fields["currentLoopID"].guiActive = false; + Fields["currentLoopID"].guiActiveEditor = false; + } + } + + protected void FixedUpdate() + { + if (moduleUsed) + { + SystemFluxUI = String.Format("{0}W", Utils.ToSI(totalSystemFlux, "F0")); + LoopTemperatureUI = String.Format("{0:F0} / {1:F0} K", currentLoopTemperature, nominalLoopTemperature); + if (totalSystemFlux > 0f) + { + Fields["SystemTemperatureUI"].guiActive = true; + Fields["SystemTemperatureUI"].guiActiveEditor = true; + SystemTemperatureUI = String.Format("{0:F0} K", totalSystemTemperature); + } + else + { + Fields["SystemTemperatureUI"].guiActive = false; + Fields["SystemTemperatureUI"].guiActiveEditor = false; + } } + if (simulator == null) { - if (HighLogic.LoadedSceneIsFlight) - - simulator = part.vessel.GetComponent().Simulator; - - if (HighLogic.LoadedSceneIsEditor) - - simulator = SystemHeatEditor.Instance.Simulator; - + FindSimulator(); + } } + protected void FindSimulator() + { + if (HighLogic.LoadedSceneIsFlight) + + simulator = part.vessel.GetComponent().Simulator; + + if (HighLogic.LoadedSceneIsEditor) + + simulator = SystemHeatEditor.Instance.Simulator; + } + } diff --git a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatAsteroidHarvester.cs b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatAsteroidHarvester.cs index 33be5c0..425a3bf 100644 --- a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatAsteroidHarvester.cs +++ b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatAsteroidHarvester.cs @@ -50,7 +50,7 @@ public override string GetInfo() return info; else return info.Substring(0, pos) + Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatHarvester_PartInfoAdd", - systemPower.ToString("F0"), + Utils.ToSI(systemPower,"F0"), systemOutletTemperature.ToString("F0"), shutdownTemperature.ToString("F0") ) + info.Substring(pos); diff --git a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatBaseConverterAdapter.cs b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatBaseConverterAdapter.cs index d995788..b13058f 100644 --- a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatBaseConverterAdapter.cs +++ b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatBaseConverterAdapter.cs @@ -28,7 +28,7 @@ public class ModuleSystemHeatBaseConverterAdapter : PartModule /// [KSPField(isPersistant = false)] public float systemPower = 0f; - + /// /// Index of the converter module /// @@ -54,12 +54,13 @@ public class ModuleSystemHeatBaseConverterAdapter : PartModule public string EditConverterModuleInfo(string source) { - string msg = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatBaseConverterAdapter_PartInfo", systemPower.ToString("F0"), + string msg = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatBaseConverterAdapter_PartInfo", + Utils.ToSI(systemPower, "F0"), systemOutletTemperature.ToString("F0"), shutdownTemperature.ToString("F0")); if (source.Contains(msg)) - return source; + return source; int pos = source.IndexOf("\n\n"); if (pos < 0) @@ -75,11 +76,11 @@ public override void OnIconCreate() public void Start() { heatModule = ModuleUtils.FindHeatModule(this.part, systemHeatModuleID); - + if (converterModuleIndex != -1) { - base.part.partInfo.moduleInfos[converterModuleIndex].info = EditConverterModuleInfo(base.part.partInfo.moduleInfos[converterModuleIndex].info); - + base.part.partInfo.moduleInfos[converterModuleIndex].info = EditConverterModuleInfo(base.part.partInfo.moduleInfos[converterModuleIndex].info); + converterModule = base.part.Modules[converterModuleIndex] as BaseConverter; if (converterModule == null) { diff --git a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatConverter.cs b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatConverter.cs index 0de843f..d816aae 100644 --- a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatConverter.cs +++ b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatConverter.cs @@ -65,7 +65,7 @@ public override string GetInfo() return info; else return info.Substring(0, pos) + Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatConverter_PartInfoAdd", - systemPower.ToString("F0"), + Utils.ToSI(systemPower,"F0"), systemOutletTemperature.ToString("F0"), shutdownTemperature.ToString("F0") ) + info.Substring(pos); diff --git a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatCryoTank.cs b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatCryoTank.cs new file mode 100644 index 0000000..007e098 --- /dev/null +++ b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatCryoTank.cs @@ -0,0 +1,872 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using KSP.Localization; + +namespace SystemHeat +{ + public class ModuleSystemHeatCryoTank : PartModule + { + + // Module ID + [KSPField(isPersistant = false)] + public string moduleID = "CryoModule"; + + // SYSTEM HEAT FIELDS + // This should correspond to the related ModuleSystemHeat + [KSPField(isPersistant = false)] + public string systemHeatModuleID; + + // the current thermal cost to cool the tank + [KSPField(isPersistant = false)] + public double currentHeatGeneration = 0.0; + + // UI Fields + + /// Cost to cool off u/s per 1000 u + [KSPField(isPersistant = false)] + public float CoolingHeatCost = 0.0f; + + // The current heat cost to cool the tank + [KSPField(isPersistant = false)] + public double currentCoolingHeatCost = 0.0; + + + // Last timestamp that boiloff occurred + [KSPField(isPersistant = true)] + public double LastUpdateTime = 0; + + // Whether active tank refrigeration is allowed on the part + [KSPField(isPersistant = true)] + public bool CoolingAllowed = true; + + // Whether active tank refrigeration is occurring on the part + [KSPField(isPersistant = true)] + public bool CoolingEnabled = true; + + // Whether the tank is CURRENTLY cooled or not + [KSPField(isPersistant = true)] + public bool BoiloffOccuring = false; + + // Special Heat-based-cooling parameters + // Does longwave (energy emitted by planets) affect boiloff? + [KSPField(isPersistant = false)] + public bool LongwaveFluxAffectsBoiloff = false; + + // Does shortwave (energy emitted by solar sources) affect boiloff? + [KSPField(isPersistant = false)] + public bool ShortwaveFluxAffectsBoiloff = false; + + // Percent of solar energy reflected + [KSPField(isPersistant = false)] + public float Albedo = 0.5f; + + // The baseline solar flux (measured at Kerbin) + [KSPField(isPersistant = false)] + public float ShortwaveFluxBaseline = 0.7047f; + + // The baseline planetary flux (measured at Kerbin) + [KSPField(isPersistant = false)] + public float LongwaveFluxBaseline = 0.1231f; + + // The mimum scaling for boiloff + [KSPField(isPersistant = false)] + public double MinimumBoiloffScale = 0.1f; + + // The maximum scaling for boiloff + [KSPField(isPersistant = false)] + public double MaximumBoiloffScale = 5f; + + // How much to scale cooling if this module has been jettisioned + [KSPField(isPersistant = false)] + public double JettisonCoolingScale = 1d; + + // How much to scale boiloff if this module has been jettisioned + [KSPField(isPersistant = false)] + public double JettisonBoiloffScale = 1d; + + /// + /// Indicates whether there are any boilable resources currently on the part + /// + public bool HasAnyBoiloffResource { get; private set; } = false; + + // PRIVATE + private double finalCoolingHeatCost = 0.0; + private List fuels; + + private double fuelAmount = 0.0; + private double maxFuelAmount = 0.0; + + // Thermal + private double solarFlux = 1.0; + private double planetFlux = 1.0; + private double fluxScale = 1.0; + + protected ModuleSystemHeat heatModule; + protected PartModule jettisonModule; + protected bool isJettisoned = false; + // UI FIELDS/ BUTTONS + // Status string + [KSPField(isPersistant = false, guiActive = true, guiName = "#LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_BoiloffStatus", groupName = "cryotank", groupDisplayName = "#LOC_SystemHeat_ModuleSystemHeatCryoTank_UIGroup_Title")] + public string BoiloffStatus = "N/A"; + + [KSPField(isPersistant = false, guiActive = false, guiName = "#LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_CoolingStatus", groupName = "cryotank", groupDisplayName = "#LOC_SystemHeat_ModuleSystemHeatCryoTank_UIGroup_Title")] + public string CoolingStatus = "N/A"; + + [KSPEvent(guiActive = false, guiName = "#LOC_SystemHeat_ModuleSystemHeatCryoTank_Event_Enable", active = true, groupName = "cryotank", groupDisplayName = "#LOC_SystemHeat_ModuleSystemHeatCryoTank_UIGroup_Title")] + public void Enable() + { + CoolingEnabled = true; + } + [KSPEvent(guiActive = false, guiName = "#LOC_SystemHeat_ModuleSystemHeatCryoTank_Event_Disable", active = false, groupName = "cryotank", groupDisplayName = "#LOC_SystemHeat_ModuleSystemHeatCryoTank_UIGroup_Title")] + public void Disable() + { + CoolingEnabled = false; + } + + // DEBUG FIELDS + + [KSPField(isPersistant = true)] + public bool DebugMode = false; + + [KSPField(isPersistant = false, guiActive = false, guiName = "Albedo")] + public string D_Albedo = "N/A"; + + [KSPField(isPersistant = false, guiActive = false, guiName = "Emissivity")] + public string D_Emiss = "N/A"; + + [KSPField(isPersistant = false, guiActive = false, guiName = "SW_In(kW)")] + public string D_InSolar = "N/A"; + + [KSPField(isPersistant = false, guiActive = false, guiName = "LW_In(kW)")] + public string D_InPlanet = "N/A"; + + [KSPField(isPersistant = false, guiActive = false, guiName = "NetRad_In(kW)")] + public string D_NetRad = "N/A"; + + // ACTIONS + [KSPAction(guiName = "#LOC_SystemHeat_ModuleSystemHeatCryoTank_Action_EnableAction")] + public void EnableAction(KSPActionParam param) { Enable(); } + + [KSPAction(guiName = "#LOC_SystemHeat_ModuleSystemHeatCryoTank_Action_DisableAction")] + public void DisableAction(KSPActionParam param) { Disable(); } + + [KSPAction(guiName = "#LOC_SystemHeat_ModuleSystemHeatCryoTank_Action_ToggleAction")] + public void ToggleAction(KSPActionParam param) + { + CoolingEnabled = !CoolingEnabled; + } + + + public override string GetModuleDisplayName() + { + return Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_ModuleName"); + } + /// + /// BUilds the loc string + /// + /// + public override string GetInfo() + { + string msg; + string fuelDisplayName; + if (IsConfiguredAsCoolable()) + { + string sub = ""; + float baseCooling = CoolingHeatCost; + foreach (BoiloffFuel fuel in fuels) + { + fuelDisplayName = PartResourceLibrary.Instance.GetDefinition(fuel.fuelName).displayName; + sub += Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_PartInfoBoiloff", + fuelDisplayName, + (fuel.boiloffRate).ToString("F2"), + (baseCooling + fuel.coolingHeatCost).ToString("F2"), + fuel.cryoTemperature + ); + if (fuel.outputs.Count > 0) + { + foreach (ResourceRatio output in fuel.outputs) + { + string outputDisplayName = PartResourceLibrary.Instance.GetDefinition(output.ResourceName).displayName; + sub += Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_PartInfoBoiloffOutput", + outputDisplayName, + (fuel.boiloffRate * output.Ratio).ToString("F2")); + } + } + } + msg = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_PartInfoCooled", sub); + } + else + { + msg = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_PartInfoUncooled"); + foreach (BoiloffFuel fuel in fuels) + { + fuelDisplayName = PartResourceLibrary.Instance.GetDefinition(fuel.fuelName).displayName; + msg += Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_PartInfoBoiloff", fuelDisplayName, (fuel.boiloffRate).ToString("F2")); + if (fuel.outputs.Count > 0) + { + foreach (ResourceRatio output in fuel.outputs) + { + string outputDisplayName = PartResourceLibrary.Instance.GetDefinition(output.ResourceName).displayName; + msg += Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_PartInfoBoiloffOutput", + outputDisplayName, + (fuel.boiloffRate * output.Ratio).ToString("F2")); + } + } + } + } + return msg; + } + + + public void Start() + { + + heatModule = ModuleUtils.FindHeatModule(this.part, systemHeatModuleID); + + jettisonModule = part.Modules.GetModule("ModuleBdbJettison"); + + if (jettisonModule != null) + Utils.Log($"Found {jettisonModule.GUIName} on {part.partInfo.title}"); + + if (HighLogic.LoadedSceneIsFlight || HighLogic.LoadedSceneIsEditor) + { + ReloadDatabaseNodes(); + SetDebugMode(DebugMode); + } + + if (HighLogic.LoadedSceneIsFlight) + { + /// Check to see if there's any boiloff resources on this part + HasAnyBoiloffResource = false; + foreach (BoiloffFuel fuel in fuels) + { + if (IsPartResourcePresent(fuel.fuelName, part)) + { + HasAnyBoiloffResource = true; + fuel.Initialize(); + } + else + { + fuel.fuelPresent = false; + } + } + /// if no resources turn off the UI and return + if (!HasAnyBoiloffResource) + { + Events["Disable"].guiActive = false; + Events["Enable"].guiActive = false; + Fields["BoiloffStatus"].guiActive = false; + heatModule.SetSystemHeatModuleEnabled(false); + return; + } + + maxFuelAmount = GetTotalMaxResouceAmount(); + + planetFlux = LongwaveFluxBaseline; + solarFlux = ShortwaveFluxBaseline; + + if (GetTotalCoolingCost() > 0.0) + { + finalCoolingHeatCost = maxFuelAmount / 1000.0 * GetTotalCoolingCost(); + Events["Disable"].guiActive = true; + Events["Enable"].guiActive = true; + Events["Enable"].guiActiveEditor = true; + Events["Disable"].guiActiveEditor = true; + heatModule.SetSystemHeatModuleEnabled(true); + } + else + { + heatModule.SetSystemHeatModuleEnabled(false); + } + // Catchup + DoCatchup(); + } + Utils.Log("[ModuleSystemHeatCryoTank] Setup completed", LogType.Modules); + } + + /// + /// Sets the UI debug fields to active + /// + /// + void SetDebugMode(bool debug) + { + + Fields["D_NetRad"].guiActive = debug; + Fields["D_InSolar"].guiActive = debug; + Fields["D_InPlanet"].guiActive = debug; + Fields["D_InPlanet"].guiActive = debug; + Fields["D_Albedo"].guiActive = debug; + Fields["D_Emiss"].guiActive = debug; + + } + void ReloadDatabaseNodes() + { + if (fuels == null || fuels.Count == 0) + { + Utils.Log(String.Format("[ModuleSystemHeatCryoTank] Reloading ConfigNodes for {0}", part.partInfo.name), LogType.Modules); + + ConfigNode cfg; + foreach (UrlDir.UrlConfig pNode in GameDatabase.Instance.GetConfigs("PART")) + { + if (pNode.name.Replace("_", ".") == part.partInfo.name) + { + List cryoNodes = pNode.config.GetNodes("MODULE").ToList().FindAll(n => n.GetValue("name") == moduleName); + if (cryoNodes.Count > 1) + { + try + { + ConfigNode node = cryoNodes.Single(n => n.GetValue("moduleID") == moduleID); + OnLoad(node); + } + catch (InvalidOperationException) + { + // Thrown if predicate is not fulfilled, ie moduleName is not unqiue + Utils.Log(String.Format("[ModuleSystemHeatCryoTank] Critical configuration error: Multiple ModuleCryoTank nodes found with identical or no moduleName"), LogType.Modules); + } + catch (ArgumentNullException) + { + + // Thrown if ModuleCryoTank is not found (a Large Problem (tm)) + Utils.Log(String.Format("[ModuleSystemHeatCryoTank] Critical configuration error: No ModuleCryoTank nodes found in part"), LogType.Modules); + } + } + else + { + OnLoad(cryoNodes[0]); + } + } + } + } + } + + /// + /// Loads data from config node + /// + /// + public override void OnLoad(ConfigNode node) + { + base.OnLoad(node); + + ConfigNode[] varNodes = node.GetNodes("BOILOFFCONFIG"); + + if (fuels == null) + fuels = new List(); + if (varNodes.Length > 0) + { + + for (int i = 0; i < varNodes.Length; i++) + { + fuels.Add(new BoiloffFuel(varNodes[i], this.part)); + } + } + } + + /// + /// Execute a boiloff catchup operation, which looks at elapsed time to see how long we've been away and subtracts + /// resources appropriately + /// + public void DoCatchup() + { + if (part.vessel.missionTime > 0.0) + { + if (BoiloffOccuring) + { + double elapsedTime = Planetarium.GetUniversalTime() - LastUpdateTime; + if (elapsedTime > 0d) + { + + Utils.Log($"[ModuleSystemHeatCryoTank] Catching up {elapsedTime} s of time on load", LogType.Modules); + for (int i = 0; i < fuels.Count; i++) + { + fuels[i].Boiloff(elapsedTime, 1.0); + } + } + } + } + } + + public void Update() + { + if (HighLogic.LoadedSceneIsFlight && HasAnyBoiloffResource) + { + /// Show the insulation status field if there is a cooling cost + if (IsCoolable()) + { + Fields["CoolingStatus"].guiActive = true; + Fields["BoiloffStatus"].guiActive = true; + + + if (Events["Enable"].active == CoolingEnabled || Events["Disable"].active != CoolingEnabled) + { + Events["Disable"].active = CoolingEnabled; + Events["Enable"].active = !CoolingEnabled; + } + + heatModule.SetSystemHeatModuleEnabled(true); + } + else + { + Fields["CoolingStatus"].guiActive = false; + Events["Disable"].active = false; + Events["Enable"].active = false; + + heatModule.SetSystemHeatModuleEnabled(false); + } + + /// if there is no more fuel, hide the boiloff status + if (fuelAmount == 0.0) + { + Fields["BoiloffStatus"].guiActive = false; + heatModule.SetSystemHeatModuleEnabled(false); + } + } + else if (HighLogic.LoadedSceneIsEditor) + { + /// Check for the presence of any resource + HasAnyBoiloffResource = false; + foreach (BoiloffFuel fuel in fuels) + { + if (IsPartResourcePresent(fuel.fuelName, part)) + { + HasAnyBoiloffResource = true; + fuel.Initialize(); + } + else + { + fuel.fuelPresent = false; + } + } + + if (IsCoolable() && HasAnyBoiloffResource) + { + Fields["CoolingStatus"].guiActiveEditor = true; + Fields["CoolingStatus"].guiActiveEditor = true; + + double max = GetTotalMaxResouceAmount(); + + CoolingStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_CoolingStatus_Editor", (GetTotalCoolingCost() * (float)(max / 1000.0)).ToString("F2")); + + Events["Disable"].guiActiveEditor = true; + Events["Enable"].guiActiveEditor = true; + + if (Events["Enable"].active == CoolingEnabled || Events["Disable"].active != CoolingEnabled) + { + Events["Disable"].active = CoolingEnabled; + Events["Enable"].active = !CoolingEnabled; + } + heatModule.SetSystemHeatModuleEnabled(true); + } + else + { + Fields["CoolingStatus"].guiActiveEditor = false; + Events["Disable"].guiActiveEditor = false; + Events["Enable"].guiActiveEditor = false; + heatModule.SetSystemHeatModuleEnabled(false); + } + } + } + + protected void ClearHeat() + { + heatModule.AddFlux(moduleID, 0f, 0f, false); + } + + protected void FixedUpdate() + { + + if (HighLogic.LoadedSceneIsFlight && HasAnyBoiloffResource) + { + if (jettisonModule != null && bool.Parse(jettisonModule.Fields.GetValue("isJettisoned").ToString())) + isJettisoned = true; + else + isJettisoned = false; + fluxScale = CalculateRadiativeEffects(); + fuelAmount = GetTotalResouceAmount(); + + // If we have no fuel, no need to do any calculations + if (fuelAmount == 0.0) + { + BoiloffStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_BoiloffStatus_NoFuel"); + CoolingStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_CoolingStatus_NoFuel"); + currentCoolingHeatCost = 0.0; + ClearHeat(); + return; + } + + + // If the tank is not coolable we must boil off + if (!IsCoolable()) + { + BoiloffOccuring = true; + BoiloffStatus = FormatRate(GetTotalBoiloffRate() * fuelAmount * fluxScale); + currentCoolingHeatCost = 0.0; + ClearHeat(); + } + // else check cooling state + else + { + if (!CoolingEnabled) + { + BoiloffOccuring = true; + BoiloffStatus = FormatRate(GetTotalBoiloffRate() * fuelAmount * fluxScale); + CoolingStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_CoolingStatus_Disabled"); + currentCoolingHeatCost = 0.0; + ClearHeat(); + } + else + { + BoiloffOccuring = ResolveHeat(); + currentCoolingHeatCost = finalCoolingHeatCost; + } + } + + if (BoiloffOccuring) + { + DoBoiloff(); + } + if (part.vessel.missionTime > 0.0) + { + + } + } + if (HighLogic.LoadedSceneIsFlight && DebugMode) + { + D_Albedo = String.Format("{0:F4}", Albedo); + D_Emiss = String.Format("{0:F4}", part.emissiveConstant); + D_InPlanet = String.Format("{0:F4}", planetFlux); + D_InSolar = String.Format("{0:F4}", solarFlux * Albedo); + D_NetRad = String.Format("{0:F4}", fluxScale); + } + if (HighLogic.LoadedSceneIsEditor && HasAnyBoiloffResource) + { + if (jettisonModule != null && bool.Parse(jettisonModule.Fields.GetValue("isJettisoned").ToString())) + isJettisoned = true; + else + isJettisoned = false; + + if (!IsCoolable()) + { + + + currentCoolingHeatCost = 0.0; + ClearHeat(); + } + else + { + if (CoolingEnabled) + { + if (GetTotalCoolingCost() > 0.0) + { + + currentCoolingHeatCost = GetTotalCoolingCost() * GetTotalMaxResouceAmount() / 1000d; + if (isJettisoned) + currentCoolingHeatCost *= JettisonCoolingScale; + + heatModule.AddFlux(moduleID, GetSourceTemperature(), (float)currentCoolingHeatCost, true); + } + else + { + currentCoolingHeatCost = 0.0; + ClearHeat(); + } + } + else + { + currentCoolingHeatCost = 0.0; + ClearHeat(); + } + } + } + } + + /// + /// Calcualates boiloff scaling by radiation + /// + /// Scaling factor for boiloff depending on radiation + protected double CalculateRadiativeEffects() + { + if (part.ptd != null) + { + if (LongwaveFluxAffectsBoiloff) + planetFlux = part.ptd.bodyFlux; + + if (ShortwaveFluxAffectsBoiloff) + solarFlux = part.ptd.sunFlux / part.emissiveConstant; + } + + if (ShortwaveFluxAffectsBoiloff || LongwaveFluxAffectsBoiloff) + { + fluxScale = Math.Max((planetFlux / LongwaveFluxBaseline + (solarFlux * Albedo) / ShortwaveFluxBaseline) / 2.0f, MinimumBoiloffScale); + } + else + { + fluxScale = 1.0; + } + return fluxScale; + } + + /// + /// Sets the boiloff state + /// + /// Whether boiloff is occuring or not + /// The total cost of the boiloff + public double SetBoiloffState(bool isBoiling) + { + if (CoolingEnabled && IsCoolable()) + { + if (isBoiling) + { + BoiloffOccuring = true; + BoiloffStatus = FormatRate(GetTotalBoiloffRate() * fuelAmount * fluxScale); + CoolingStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_CoolingStatus_Uncooled"); + } + else + { + + BoiloffOccuring = false; + BoiloffStatus = String.Format("Insulated"); + CoolingStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_CoolingStatus_Cooling", finalCoolingHeatCost.ToString("F2")); + + } + return (double)finalCoolingHeatCost; + } + return 0d; + } + public override void OnSave(ConfigNode node) + { + LastUpdateTime = Planetarium.GetUniversalTime(); + base.OnSave(node); + } + + /// + /// Adds heat and evaluates whether boiloff should be happening + /// + /// + public bool ResolveHeat() + { + bool boiloff = false; + if (CoolingEnabled && IsCoolable()) + { + + if (isJettisoned) + heatModule.AddFlux(moduleID, GetSourceTemperature(), (float)(finalCoolingHeatCost * JettisonCoolingScale), true); + else + heatModule.AddFlux(moduleID, GetSourceTemperature(), (float)finalCoolingHeatCost, true); + + if (heatModule.LoopTemperature <= GetMaxSourceTemperature()) + { + boiloff = false; + BoiloffStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_BoiloffStatus_Insulated"); + CoolingStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_CoolingStatus_Cooling", finalCoolingHeatCost.ToString("F2")); + } + else + { + boiloff = true; + BoiloffStatus = FormatRate(GetTotalBoiloffRate() * fuelAmount * fluxScale); + CoolingStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_CoolingStatus_Uncooled"); + } + } + return boiloff; + } + + + /// + /// Iteractes through the fuels and applies boiloff + /// + protected void DoBoiloff() + { + double boiloffScale = fluxScale; + + if (isJettisoned) + boiloffScale *= JettisonBoiloffScale; + + // Apply settings stuff + if (!SystemHeatGameSettings_Boiloff.BoiloffEnabled) + { + boiloffScale = 0f; + } + else + { + boiloffScale *= SystemHeatGameSettings_Boiloff.BoiloffScale; + } + + for (int i = 0; i < fuels.Count; i++) + { + + if (!CoolingEnabled) + { + fuels[i].Boiloff(TimeWarp.fixedDeltaTime, boiloffScale); + } + else + { + if (heatModule.LoopTemperature > fuels[i].cryoTemperature) + { + fuels[i].Boiloff(TimeWarp.fixedDeltaTime, boiloffScale); + } + } + } + } + + + + /// + /// Get the total fuel amount of the tank (sum of all boilable fuels) + /// + /// Total fuel + protected double GetTotalResouceAmount() + { + double max = 0d; + for (int i = 0; i < fuels.Count; i++) + max += fuels[i].FuelAmount(); + return max; + } + + /// + /// Gets the total fuel maximum of the part (sum of all boilable fuels) + /// + /// Total max fuel + protected double GetTotalMaxResouceAmount() + { + double max = 0d; + for (int i = 0; i < fuels.Count; i++) + max += fuels[i].FuelAmountMax(); + return max; + } + + /// + /// Returns the total amount of fuel (units) boiling off per second + /// + /// Total fuel boiloff per second + protected double GetTotalBoiloffRate() + { + double max = 0d; + for (int i = 0; i < fuels.Count; i++) + { + + if (!CoolingEnabled && CoolingAllowed) + max += fuels[i].boiloffRateSeconds; + else if (heatModule.LoopTemperature >= fuels[i].cryoTemperature) + max += fuels[i].boiloffRateSeconds; + + if (isJettisoned) + max *= JettisonBoiloffScale; + } + return max; + } + + /// + /// Returns the total cooling cost per 1000 units of the tank (Base + added by fuels) + /// + /// Cooling cost, per 1000 units + protected float GetTotalCoolingCost() + { + float total = CoolingHeatCost; + for (int i = 0; i < fuels.Count; i++) + total += fuels[i].FuelCoolingHeatCost(); + return total; + } + + + /// + /// Returns the source temperature of the module, which is the lowest temperature of all boiloff fuels on the part + /// + /// source temperature + protected float GetSourceTemperature() + { + float min = 2000f; + for (int i = 0; i < fuels.Count; i++) + { + float temp = fuels[i].FuelCryocoolerTemperature(); + if (temp < min && temp > 0f) + min = temp; + } + return min; + } + protected float GetMaxSourceTemperature() + { + float max = 0; + for (int i = 0; i < fuels.Count; i++) + { + float temp = fuels[i].FuelCryocoolerTemperature(); + if (temp > max) + max = temp; + } + return max; + } + /// + /// Determines if the tank is currently coolable (ie, can create waste heat to stop boiloff) + /// + /// True if the tank can be cooled + protected bool IsCoolable() + { + if (!CoolingAllowed) + return false; + + for (int i = 0; i < fuels.Count; i++) + { + if (fuels[i].FuelCoolingHeatCost() > 0.0f) + return true; + } + if (CoolingHeatCost > 0.0f) + return true; + + return false; + } + + /// + /// Determines if the tank is currently coolable (ie, can generate heat to stop boiloff) + /// + /// True if the tank can be cooled + protected bool IsConfiguredAsCoolable() + { + if (!CoolingAllowed) + return false; + + for (int i = 0; i < fuels.Count; i++) + { + if (fuels[i].FuelConfiguredCoolingHeatCost() > 0.0f) + return true; + } + if (CoolingHeatCost > 0.0f) + return true; + + return false; + } + + + /// + /// Produces a friendly string describing boiloff rate + /// + /// The rate in seconds to format + /// String describing rate + public string FormatRate(double rate) + { + double adjRate = rate; + string interval = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_TimeInterval_Second_Abbrev"); + if (adjRate < 0.01) + { + adjRate = adjRate * 60.0; + interval = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_TimeInterval_Minute_Abbrev"); + } + if (adjRate < 0.01) + { + adjRate = adjRate * 60.0; + interval = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_TimeInterval_Hour_Abbrev"); + } + return Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatCryoTank_Field_BoiloffStatus_Boiloff", adjRate.ToString("F2"), interval.ToString()); + } + + public bool IsPartResourcePresent(string resourceName, Part p) + { + if (!SystemHeatGameSettings_Boiloff.BoiloffEnabled) return false; + int id = PartResourceLibrary.Instance.GetDefinition(resourceName).id; + PartResource res = p.Resources.Get(id); + if (res == null) + return false; + return true; + } + } +} diff --git a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatEngine.cs b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatEngine.cs index b58a60b..36d3dd2 100644 --- a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatEngine.cs +++ b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatEngine.cs @@ -66,7 +66,7 @@ public override string GetInfo() string msg = ""; ModuleEnginesFX[] engines = part.GetComponents(); msg += Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatEngine_PartInfo", - systemPower.ToString("F0"), + Utils.ToSI(systemPower,"F0"), systemOutletTemperature.ToString("F0"), temperatureCurve.Curve.keys[temperatureCurve.Curve.keys.Length - 1].time.ToString("F0") ); @@ -125,12 +125,14 @@ protected void GenerateHeatEditor() Fields["systemHeatGeneration"].guiActiveEditor = true; Fields["systemTemperature"].guiActiveEditor = true; heatModule.AddFlux(moduleID, systemOutletTemperature, engineFraction * systemPower, true); - systemHeatGeneration = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatEngine_Field_HeatGeneration_Running", (engineFraction * systemPower).ToString("F0")); + systemHeatGeneration = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatEngine_Field_HeatGeneration_Running", + Utils.ToSI((engineFraction * systemPower),"F0")); } else { - systemHeatGeneration = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatEngine_Field_HeatGeneration_Running", (engineFraction * systemPower).ToString("F0")); + systemHeatGeneration = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatEngine_Field_HeatGeneration_Running", + Utils.ToSI((engineFraction * systemPower),"F0")); heatModule.AddFlux(moduleID, 0f, engineFraction * systemPower, false); Fields["systemHeatGeneration"].guiActiveEditor = false; Fields["systemTemperature"].guiActiveEditor = false; @@ -144,7 +146,8 @@ protected void GenerateHeatEditor() Fields["systemHeatGeneration"].guiActiveEditor = true; Fields["systemTemperature"].guiActiveEditor = true; heatModule.AddFlux(moduleID, systemOutletTemperature, engineFraction * systemPower, true); - systemHeatGeneration = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatEngine_Field_HeatGeneration_Running", (engineFraction * systemPower).ToString("F0")); + systemHeatGeneration = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatEngine_Field_HeatGeneration_Running", + Utils.ToSI((engineFraction * systemPower), "F0")); } } @@ -162,7 +165,8 @@ protected void GenerateHeatFlight() heatModule.AddFlux(moduleID, systemOutletTemperature, engineFraction * systemPower, true); Fields["systemHeatGeneration"].guiActive = true; Fields["systemTemperature"].guiActive = true; - systemHeatGeneration = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatEngine_Field_HeatGeneration_Running", (engineFraction * systemPower).ToString("F0")); + systemHeatGeneration = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatEngine_Field_HeatGeneration_Running", + Utils.ToSI((engineFraction * systemPower),"F0")); systemTemperature = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatEngine_Field_Temperature_Running", heatModule.currentLoopTemperature.ToString("F0"), systemOutletTemperature.ToString("F0")); } else @@ -170,7 +174,8 @@ protected void GenerateHeatFlight() heatModule.AddFlux(moduleID, 0f, 0f, false); Fields["systemHeatGeneration"].guiActive = false; Fields["systemTemperature"].guiActive = false; - systemHeatGeneration = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatEngine_Field_HeatGeneration_Running", (engineFraction * systemPower).ToString("F0")); + systemHeatGeneration = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatEngine_Field_HeatGeneration_Running", + Utils.ToSI((engineFraction * systemPower),"F0")); systemTemperature = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatEngine_Field_Temperature_Running", heatModule.currentLoopTemperature.ToString("F0"), systemOutletTemperature.ToString("F0")); } diff --git a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatExchanger.cs b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatExchanger.cs index c94b55f..4e23079 100644 --- a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatExchanger.cs +++ b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatExchanger.cs @@ -126,10 +126,11 @@ public override string GetModuleDisplayName() public override string GetInfo() { string msg = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatExchanger_PartInfo", - 0f.ToString("F0"), temperatureDeltaCostCurve.Evaluate(0f).ToString("F0"), + 0f.ToString("F0"), + temperatureDeltaCostCurve.Evaluate(0f).ToString("F0"), 1000f.ToString("F0"), temperatureDeltaCostCurve.Evaluate(1000f).ToString("F0"), - temperatureDeltaHeatCurve.Evaluate(0).ToString("F0"), - temperatureDeltaHeatCurve.Evaluate(1000f).ToString("F0") + Utils.ToSI(temperatureDeltaHeatCurve.Evaluate(0), "F0"), + Utils.ToSI(temperatureDeltaHeatCurve.Evaluate(1000f), "F0") ); return msg; @@ -147,7 +148,7 @@ public void Start() { sourceModule = heatModule1; destModule = heatModule2; - + } else { @@ -214,7 +215,7 @@ public void FixedUpdate() private void ToggleDirection(BaseField field, object oldFieldValueObj) { - DoToggleDirection(); + DoToggleDirection(); } private void DoToggleDirection() @@ -268,7 +269,10 @@ protected void GenerateHeatEditor() outputHeat = Mathf.Min(-sourceModule.consumedSystemFlux, HeatRate) + temperatureDeltaHeatCurve.Evaluate(OutletAdjustement); PowerStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatExchanger_Field_PowerCost_Active", powerCost.ToString("F0")); - Status = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatExchanger_Field_Status_Active", outletTemperature.ToString("F0"), outputHeat.ToString("F0"), sourceModule.consumedSystemFlux.ToString("F1")); + Status = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatExchanger_Field_Status_Active", + outletTemperature.ToString("F0"), + Utils.ToSI(outputHeat, "F0"), + Utils.ToSI(sourceModule.consumedSystemFlux, "F0")); destModule.AddFlux(moduleID, outletTemperature, outputHeat, true); } else @@ -324,7 +328,9 @@ protected void GenerateHeatFlight() outputHeat = Mathf.Min(-sourceModule.consumedSystemFlux, HeatRate) + temperatureDeltaHeatCurve.Evaluate(OutletAdjustement); PowerStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatExchanger_Field_PowerCost_Active", powerCost.ToString("F1")); - Status = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatExchanger_Field_Status_Active", outletTemperature.ToString("F0"), outputHeat.ToString("F0")); + Status = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatExchanger_Field_Status_Active", + outletTemperature.ToString("F0"), + Utils.ToSI(outputHeat, "F0")); destModule.AddFlux(moduleID, outletTemperature, outputHeat, true); } else diff --git a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatFissionEngine.cs b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatFissionEngine.cs index 553ce5e..f9fc1a3 100644 --- a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatFissionEngine.cs +++ b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatFissionEngine.cs @@ -68,7 +68,7 @@ public override string GetInfo() Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatFissionEngine_PartInfo", ElectricalGeneration.Evaluate(100f).ToString("F0"), FindTimeRemaining(this.part.Resources.Get(PartResourceLibrary.Instance.GetDefinition(FuelName).id).amount, baseRate), - (HeatGeneration.Evaluate(100f) - ElectricalGeneration.Evaluate(100f)).ToString("F0"), + Utils.ToSI((HeatGeneration.Evaluate(100f) - ElectricalGeneration.Evaluate(100f)), "F0"), NominalTemperature.ToString("F0"), NominalTemperature.ToString("F0"), CriticalTemperature.ToString("F0"), @@ -79,7 +79,7 @@ public override string GetInfo() return Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatFissionEngine_PartInfo_NoPower", FindTimeRemaining(this.part.Resources.Get(PartResourceLibrary.Instance.GetDefinition(FuelName).id).amount, baseRate), - HeatGeneration.Evaluate(100f).ToString("F0"), + Utils.ToSI(HeatGeneration.Evaluate(100f), "F0"), NominalTemperature.ToString("F0"), NominalTemperature.ToString("F0"), CriticalTemperature.ToString("F0"), diff --git a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatFissionFuelHandler.cs b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatFissionFuelHandler.cs index 0591f95..cf67fe2 100644 --- a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatFissionFuelHandler.cs +++ b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatFissionFuelHandler.cs @@ -4,10 +4,11 @@ using System.Text; using UnityEngine; using KSP.Localization; +using Expansions.Missions.Adjusters; namespace SystemHeat.Modules { - public class ModuleSystemHeatFissionFuelContainer: PartModule + public class ModuleSystemHeatFissionFuelContainer : PartModule { /// @@ -22,11 +23,17 @@ public class ModuleSystemHeatFissionFuelContainer: PartModule [KSPField(isPersistant = false)] public int EngineerLevelForTransfer = 3; + /// + /// If this is toggled, we'll override the required engineer level in the settings + /// + [KSPField(isPersistant = false)] + public bool OverrideEngineerLevelSettings = false; + private string[] resourceNames; public override string GetInfo() { - return Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatFissionFuelContainer_PartInfo", + return Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatFissionFuelContainer_PartInfo", EngineerLevelForTransfer.ToString(), ResourceNames ); } @@ -40,7 +47,7 @@ public override void OnAwake() base.OnAwake(); if (HighLogic.LoadedSceneIsFlight) { - GameEvents.onCrewBoardVessel.Add(new EventData>.OnEvent(OnCrewBoardVessel)); + GameEvents.onCrewBoardVessel.Add(new EventData>.OnEvent(OnCrewBoardVessel)); GameEvents.onVesselCrewWasModified.Add(new EventData.OnEvent(onVesselCrewWasModified)); } } @@ -74,7 +81,7 @@ public void Start() if (HighLogic.LoadedSceneIsFlight) { resourceNames = ResourceNames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(p => p.Trim()).ToArray(); ; - + HandleTransferModes(); } } @@ -112,9 +119,19 @@ public int GetCrewLevel() /// public void HandleTransferModes() { - if (GetCrewLevel() >= EngineerLevelForTransfer) + int targetCrewLevel = EngineerLevelForTransfer; + if (SystemHeatGameSettings_NuclearFuel.RequireEngineersForTransfer) { - + if (!OverrideEngineerLevelSettings) + targetCrewLevel = SystemHeatGameSettings_NuclearFuel.EngineerLevel; + } + else + { + targetCrewLevel = 0; + } + if (GetCrewLevel() >= targetCrewLevel) + { + foreach (string resN in resourceNames) { Utils.Log($"[ModuleSystemHeatFissionFuelContainer]: Crew level is {GetCrewLevel()}, setting transfer for resource {resN} to PUMP", LogType.Modules); @@ -123,7 +140,7 @@ public void HandleTransferModes() } else { - + foreach (string resN in resourceNames) { Utils.Log($"[ModuleSystemHeatFissionFuelContainer]: Crew level is {GetCrewLevel()}, setting transfer for resource {resN} to NONE", LogType.Modules); @@ -131,6 +148,6 @@ public void HandleTransferModes() } } } - + } } diff --git a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatFissionReactor.cs b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatFissionReactor.cs index 7fe7145..5b20dc4 100644 --- a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatFissionReactor.cs +++ b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatFissionReactor.cs @@ -177,6 +177,13 @@ public class ModuleSystemHeatFissionReactor : PartModule, IContractObjectiveModu // REPAIR VARIABLES // integrity of the core + + /// + /// If this is toggled, we'll override the the settings + /// + [KSPField(isPersistant = false)] + public bool OverrideRepairSettings = false; + [KSPField(isPersistant = true)] public float CoreIntegrity = 100f; @@ -320,7 +327,7 @@ public override string GetInfo() Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatFissionReactor_PartInfo", ElectricalGeneration.Evaluate(100f).ToString("F0"), FindTimeRemaining(this.part.Resources.Get(PartResourceLibrary.Instance.GetDefinition(FuelName).id).amount, baseRate), - (HeatGeneration.Evaluate(100f) - ElectricalGeneration.Evaluate(100f)).ToString("F0"), + Utils.ToSI((HeatGeneration.Evaluate(100f) - ElectricalGeneration.Evaluate(100f)), "F0"), NominalTemperature.ToString("F0"), NominalTemperature.ToString("F0"), CriticalTemperature.ToString("F0"), @@ -680,7 +687,7 @@ protected virtual void HandleHeatGeneration() } else { - ReactorOutput = String.Format("{0:F1} {1}", CurrentHeatGeneration, Localizer.Format("#LOC_SystemHeat_Units_kW")); + ReactorOutput = String.Format("{0:F1} {1}", Utils.ToSI(CurrentHeatGeneration, "F0"), "W"); } if (heatModule) @@ -726,8 +733,9 @@ private void HandleCore() // If overheated too much, damage the core if (critExceedance > 0f && TimeWarp.CurrentRate < 100f) { + if (SystemHeatGameSettings_ReactorDamage.ReactorDamage) // core is damaged by Rate * temp exceedance * time - CoreIntegrity = Mathf.MoveTowards(CoreIntegrity, 0f, CoreDamageRate * critExceedance * TimeWarp.fixedDeltaTime); + CoreIntegrity = Mathf.MoveTowards(CoreIntegrity, 0f, CoreDamageRate * critExceedance * TimeWarp.fixedDeltaTime); } } @@ -869,15 +877,23 @@ protected float GetEnvironmentTemperature() #region Repair public bool TryRepairReactor() { - - if (CoreIntegrity <= MinRepairPercent) + float repairThreshold = SystemHeatGameSettings_ReactorDamage.RepairThreshold * 100f; + int engineerLevel = SystemHeatGameSettings_ReactorDamage.EngineerLevel; + float maxRepair = SystemHeatGameSettings_ReactorDamage.RepairMax * 100f; + if (OverrideRepairSettings) + { + repairThreshold = MinRepairPercent; + engineerLevel = EngineerLevelForRepair; + maxRepair = MaxRepairPercent; + } + if (CoreIntegrity <= repairThreshold) { ScreenMessages.PostScreenMessage(new ScreenMessage(Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatFissionReactor_Message_Repair_CoreTooDamaged"), 5.0f, ScreenMessageStyle.UPPER_CENTER)); return false; } - if (FlightGlobals.ActiveVessel.VesselValues.RepairSkill.value < EngineerLevelForRepair) + if (FlightGlobals.ActiveVessel.VesselValues.RepairSkill.value < engineerLevel) { - ScreenMessages.PostScreenMessage(new ScreenMessage(Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatFissionReactor_Message_Repair_EngineerLevelTooLow", EngineerLevelForRepair.ToString("F0")), 5.0f, ScreenMessageStyle.UPPER_CENTER)); + ScreenMessages.PostScreenMessage(new ScreenMessage(Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatFissionReactor_Message_Repair_EngineerLevelTooLow", engineerLevel.ToString("F0")), 5.0f, ScreenMessageStyle.UPPER_CENTER)); return false; } if (FlightGlobals.ActiveVessel.evaController.ModuleInventoryPartReference.TotalAmountOfPartStored("evaRepairKit") < 1) @@ -897,21 +913,28 @@ public bool TryRepairReactor() return false; } - //if (CoreIntegrity >= MaxRepairPercent) - //{ - // ScreenMessages.PostScreenMessage(new ScreenMessage(Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatFissionReactor_Message_Repair_CoreAlreadyRepaired", MaxRepairPercent.ToString("F0")), - // 5.0f, ScreenMessageStyle.UPPER_CENTER)); - // return false; - //} + if (CoreIntegrity >= maxRepair) + { + ScreenMessages.PostScreenMessage(new ScreenMessage(Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatFissionReactor_Message_Repair_CoreAlreadyRepaired", maxRepair.ToString("F0")), + 5.0f, ScreenMessageStyle.UPPER_CENTER)); + return false; + } return true; } // Repair the reactor to max Repair percent public void DoReactorRepair() { + float repairPercent = SystemHeatGameSettings_ReactorDamage.RepairPerKit * 100f; + float maxRepair = SystemHeatGameSettings_ReactorDamage.RepairMax * 100f; + if (OverrideRepairSettings) + { + maxRepair = MaxRepairPercent; + repairPercent = RepairAmountPerKit; + } FlightGlobals.ActiveVessel.evaController.ModuleInventoryPartReference.RemoveNPartsFromInventory("evaRepairKit", 1, playSound: true); - this.CoreIntegrity = Mathf.Clamp(this.CoreIntegrity + RepairAmountPerKit, 0f, 100f); + this.CoreIntegrity = Mathf.Clamp(this.CoreIntegrity + repairPercent, 0f, maxRepair); ScreenMessages.PostScreenMessage(new ScreenMessage(Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatFissionReactor_Message_Repair_RepairSuccess", this.CoreIntegrity.ToString("F0")), 5.0f, ScreenMessageStyle.UPPER_CENTER)); } diff --git a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatHarvester.cs b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatHarvester.cs index c442a2e..3219056 100644 --- a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatHarvester.cs +++ b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatHarvester.cs @@ -62,7 +62,7 @@ public override string GetInfo() return info; else return info.Substring(0, pos) + Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatHarvester_PartInfoAdd", - systemPower.ToString("F0"), + Utils.ToSI(systemPower,"F0"), systemOutletTemperature.ToString("F0"), shutdownTemperature.ToString("F0") ) + info.Substring(pos); diff --git a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatMultiJettison.cs b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatMultiJettison.cs new file mode 100644 index 0000000..607719c --- /dev/null +++ b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatMultiJettison.cs @@ -0,0 +1,283 @@ +using System; + + +public class ModuleSystemHeatMultiJettison : PartModule, IScalarModule, IPartMassModifier, IMultipleDragCube +{ + + + [KSPField()] + public string jettisonName = "jettison"; + + [KSPField()] + public Vector3 jettisonDirection = Vector3.up; + + [KSPField()] + public float jettisonForce = 5.0f; + + [KSPField()] + public float jettisonedObjectMass = 0.01f; + + private Transform[] jettisons; + + [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Fairing"), UI_Toggle(affectSymCounterparts = UI_Scene.Editor, disabledText = "Installed", enabledText = "Removed")] + public bool isJettisoned = false; + + [KSPField] + public string toggleJettisonEditorGuiName = "Fairing"; + + [KSPField] + public string jettisonGuiName = "Jettison"; + + [KSPField] + public bool enableDisabledModules = false; + + [KSPField] + public string fxGroupName = "jettison"; + + private const string DRAG_CUBE_JETTISONED = "Jettisoned"; + private const string DRAG_CUBE_COVERED = "Covered"; + + [UI_Toggle(scene = UI_Scene.All, disabledText = "No", enabledText = "Yes")] + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "Auto-Deploy Fairing")] + public bool autoDeploy = true; + + [UI_FloatRange(minValue = 0f, maxValue = 100f, stepIncrement = 1f, scene = UI_Scene.All)] + [KSPField(isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "Autodeploy Altitude (km)")] + public float deployAltitude = float.NaN; + + + public override void OnAwake() + { + OnMovingEvent = new EventData("OnMovingEvent"); + OnStopEvent = new EventData("OnStopEvent"); + + jettisons = part.FindModelTransforms(jettisonName); + + base.OnAwake(); + } + + public override void OnStart(StartState state) + { + if (jettisons.Length == 0) + isJettisoned = true; + + SetJettisoned(isJettisoned); + + Fields[nameof(isJettisoned)].uiControlEditor.onFieldChanged = OnEditorToggleJettisoned; + Fields[nameof(isJettisoned)].guiName = toggleJettisonEditorGuiName; + + Events[nameof(Jettison)].active = !isJettisoned; + Events[nameof(Jettison)].guiName = jettisonGuiName; + + Actions[nameof(JettisonAction)].active = !isJettisoned; + Actions[nameof(JettisonAction)].guiName = jettisonGuiName; + + Fields[nameof(autoDeploy)].uiControlEditor.onFieldChanged = OnToggleAutodeploy; + Fields[nameof(autoDeploy)].uiControlFlight.onFieldChanged = OnToggleAutodeploy; + + CalculateAutodeployAltitude(); + UpdateDeployAltitudeVisibility(); + } + + private void OnEditorToggleJettisoned(BaseField field, object oldValue) + { + if (!HighLogic.LoadedSceneIsEditor) + return; + + OnMoving.Fire(isJettisoned ? 0 : 1, isJettisoned ? 1 : 0); + + if (jettisons.Length > 0) + { + SetJettisoned(isJettisoned); + } + + OnStop.Fire(isJettisoned ? 1 : 0); + + part.UpdateStageability(true, true); + } + + private void OnToggleAutodeploy(BaseField field, object oldValue) + { + UpdateDeployAltitudeVisibility(); + } + + public virtual void FixedUpdate() + { + if (isJettisoned || !autoDeploy || HighLogic.LoadedSceneIsEditor || !part.started) return; + + if (deployAltitude * 1000f < vessel.altitude) Jettison(); + } + + [KSPEvent(guiActive = true, guiName = "Jettison")] + public void Jettison() + { + if (isJettisoned) + return; + + if (jettisons.Length == 0) + return; + + OnMoving.Fire(0, 1); + + for (int i = 0; i < jettisons.Length; i++) + { + Rigidbody rb = physicalObject.ConvertToPhysicalObject(part, jettisons[i].gameObject).rb; + rb.useGravity = true; + rb.mass = jettisonedObjectMass / jettisons.Length; + rb.maxAngularVelocity = PhysicsGlobals.MaxAngularVelocity; + rb.angularVelocity = part.Rigidbody.angularVelocity; + rb.velocity = part.Rigidbody.velocity + Vector3.Cross(part.Rigidbody.worldCenterOfMass - vessel.CurrentCoM, vessel.angularVelocity); + + Vector3 d = jettisonDirection; + if (d == Vector3.zero) + d = Vector3.Normalize(rb.transform.position - part.transform.position); + else + d = part.transform.TransformDirection(d); + + //rb.AddForce(part.transform.TransformDirection(jettisonDirection) * (jettisonForce * 0.5f), ForceMode.Force); + rb.AddForceAtPosition(d * (jettisonForce * 0.5f), part.transform.position, ForceMode.Force); + part.Rigidbody.AddForce(d * (-jettisonForce * 0.5f), ForceMode.Force); + } + + jettisons = new Transform[0]; + + if (part.temperature < part.skinMaxTemp) + part.skinTemperature = part.temperature; + + isJettisoned = true; + + SetJettisoned(isJettisoned); + + OnStop.Fire(1); + + EnableOtherModules(); + + FXGroup effect = part.findFxGroup(fxGroupName); + if (effect != null) + { + effect.Burst(); + } + + GameEvents.onVesselWasModified.Fire(vessel); + } + + [KSPAction("Deploy")] + public void JettisonAction(KSPActionParam param) + { + Jettison(); + } + + public override void OnActive() + { + Jettison(); + } + + private void EnableOtherModules() + { + if (enableDisabledModules) + { + foreach (PartModule pm in part.Modules) + { + if (!pm.moduleIsEnabled) + pm.moduleIsEnabled = true; + } + } + } + + private void SetJettisoned(bool b) + { + SetDragCube(b); + JettisonsSetActive(!b); + + Events[nameof(Jettison)].active = !b; + Actions[nameof(JettisonAction)].active = !b; + Fields[nameof(autoDeploy)].guiActive = !b; + Fields[nameof(autoDeploy)].guiActiveEditor = !b; + UpdateDeployAltitudeVisibility(); + } + + private void UpdateDeployAltitudeVisibility() + { + Fields[nameof(deployAltitude)].guiActive = !isJettisoned && autoDeploy; + Fields[nameof(deployAltitude)].guiActiveEditor = !isJettisoned && autoDeploy; + } + + public override bool IsStageable() + { + return !isJettisoned; + } + + public override bool StagingToggleEnabledEditor() + { + return true; + } + + private void SetDragCube(bool deployed) + { + if (deployed) + { + part.DragCubes.SetCubeWeight(DRAG_CUBE_JETTISONED, 1); + part.DragCubes.SetCubeWeight(DRAG_CUBE_COVERED, 0); + } + else + { + part.DragCubes.SetCubeWeight(DRAG_CUBE_JETTISONED, 0); + part.DragCubes.SetCubeWeight(DRAG_CUBE_COVERED, 1); + } + } + + private void JettisonsSetActive(bool b) + { + for (int i = 0; i < jettisons.Length; i++) + jettisons[i].gameObject.SetActive(b); + } + + private void CalculateAutodeployAltitude() + { + UI_FloatRange deployAltitudeControl; + if (HighLogic.LoadedSceneIsEditor) + deployAltitudeControl = (UI_FloatRange)Fields[nameof(deployAltitude)].uiControlEditor; + else + deployAltitudeControl = (UI_FloatRange)Fields[nameof(deployAltitude)].uiControlFlight; + + float newDeployAltitude; + + CelestialBody home = Planetarium.fetch.Home; + if (home != null) + { + newDeployAltitude = Mathf.Round((float)home.atmosphereDepth * 0.70f / 1000f);// / 5f) * 5f; + deployAltitudeControl.maxValue = (float)home.atmosphereDepth / 1000f; + } + else + { + Debug.LogError($"[{part.name} {GetType().Name}] Cannot find home celestial body to set altitude from"); + autoDeploy = false; + newDeployAltitude = 100f; + deployAltitudeControl.maxValue = 200f; + } + + if (float.IsNaN(deployAltitude)) deployAltitude = newDeployAltitude; + } + + #region IPartMassModifier + public float GetModuleMass(float defaultMass, ModifierStagingSituation sit) + { + float mass = 0; + switch (sit) + { + case ModifierStagingSituation.CURRENT: + if (!isJettisoned) + mass = jettisonedObjectMass; + break; + case ModifierStagingSituation.UNSTAGED: + mass = jettisonedObjectMass; + break; + } + return mass; + } + + public ModifierChangeWhen GetModuleMassChangeWhen() + { + return ModifierChangeWhen.STAGED; + } +} diff --git a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatRadiator.cs b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatRadiator.cs index 45bbea2..4356a51 100644 --- a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatRadiator.cs +++ b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatRadiator.cs @@ -149,9 +149,9 @@ public override string GetInfo() { string message = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatRadiator_PartInfo", - temperatureCurve.Curve.keys[0].time.ToString("F0"), - temperatureCurve.Evaluate(temperatureCurve.Curve.keys[0].time).ToString("F0"), - temperatureCurve.Evaluate(temperatureCurve.Curve.keys[temperatureCurve.Curve.keys.Length - 1].time).ToString("F0"), + Utils.ToSI(temperatureCurve.Curve.keys[0].time, "F0"), + temperatureCurve.Evaluate(temperatureCurve.Curve.keys[0].time).ToString("F0"), + Utils.ToSI(temperatureCurve.Evaluate(temperatureCurve.Curve.keys[temperatureCurve.Curve.keys.Length - 1].time), "F0"), temperatureCurve.Curve.keys[temperatureCurve.Curve.keys.Length - 1].time.ToString("F0") ); message += base.GetInfo(); @@ -187,7 +187,8 @@ public override void FixedUpdate() } - ConvectionStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatRadiator_ConvectionStatus_Running", convectiveFlux.ToString("F1")); + ConvectionStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatRadiator_ConvectionStatus_Running", + Utils.ToSI(convectiveFlux, "F0")); } else { @@ -199,7 +200,8 @@ public override void FixedUpdate() RadiatorEfficiency = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatRadiator_RadiatorEfficiency_Running", (-radiativeFlux / temperatureCurve.Evaluate(temperatureCurve.Curve.keys[temperatureCurve.Curve.keys.Length - 1].time) * 100f).ToString("F0")); - RadiatorStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatRadiator_RadiatorStatus_Running", radiativeFlux.ToString("F1")); + RadiatorStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatRadiator_RadiatorStatus_Running", + Utils.ToSI(radiativeFlux, "F0")); if (scalarModule != null) { @@ -246,10 +248,11 @@ public override void FixedUpdate() //Utils.Log($"tD {tDelta}, tC, {lp.ConvectionTemperature} tL {heatModule.LoopTemperature}, tMax {temperatureCurve.Curve.keys[temperatureCurve.Curve.keys.Length - 1].time}, convA {convectiveArea}"); } - + heatModule.AddFlux(moduleID, 0f, radiativeFlux + convectiveFlux, false); - ConvectionStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatRadiator_ConvectionStatus_Running", convectiveFlux.ToString("F1")); + ConvectionStatus = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatRadiator_ConvectionStatus_Running", + Utils.ToSI(convectiveFlux, "F0")); RadiatorEfficiency = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatRadiator_RadiatorEfficiency_Running", ((temperatureCurve.Evaluate(heatModule.LoopTemperature) / temperatureCurve.Evaluate(temperatureCurve.Curve.keys[temperatureCurve.Curve.keys.Length - 1].time)) * 100f).ToString("F0")); } diff --git a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatSink.cs b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatSink.cs index 9a7a948..e2837ad 100644 --- a/SystemHeat/SystemHeat/Modules/ModuleSystemHeatSink.cs +++ b/SystemHeat/SystemHeat/Modules/ModuleSystemHeatSink.cs @@ -101,7 +101,7 @@ public override string GetInfo() // Need to update this to strip the CoreHeat stuff from it string message = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatSink_PartInfo", (heatStorageMaximum / 1000f).ToString("F0"), - maxHeatRate.ToString("F0") + Utils.ToSI(maxHeatRate, "F0") ); return message; } @@ -188,7 +188,9 @@ public void FixedUpdate() GenerateHeatFlight(); systemTemperature = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatSink_Field_SystemTemperature_Running", storageTemperature.ToString("F0")); systemHeatStored = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatSink_Field_SystemHeatStored_Fraction", (heatStored / heatStorageMaximum * 100f).ToString("F0")); - systemHeatGeneration = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatSink_Field_SystemHeatGeneration_Storing", (-heatModule.consumedSystemFlux).ToString("F0"), maxHeatRate.ToString("F0")); + systemHeatGeneration = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatSink_Field_SystemHeatGeneration_Storing", + Utils.ToSI((-heatModule.consumedSystemFlux), "F0"), + Utils.ToSI(maxHeatRate, "F0")); } if (HighLogic.LoadedSceneIsEditor) @@ -196,7 +198,9 @@ public void FixedUpdate() GenerateHeatEditor(); systemTemperature = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatSink_Field_SystemTemperature_Running", storageTemperature.ToString("F0")); systemHeatStored = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatSink_Field_SystemHeatStored_Fraction", (heatStored / heatStorageMaximum * 100f).ToString("F0")); - systemHeatGeneration = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatSink_Field_SystemHeatGeneration_Storing", (-heatModule.consumedSystemFlux).ToString("F0"), maxHeatRate.ToString("F0")); + systemHeatGeneration = Localizer.Format("#LOC_SystemHeat_ModuleSystemHeatSink_Field_SystemHeatGeneration_Storing", + Utils.ToSI((-heatModule.consumedSystemFlux), "F0"), + Utils.ToSI(maxHeatRate, "F0")); } } diff --git a/SystemHeat/SystemHeat/SystemHeat.csproj b/SystemHeat/SystemHeat/SystemHeat.csproj index 20f8ba3..02e43b7 100644 --- a/SystemHeat/SystemHeat/SystemHeat.csproj +++ b/SystemHeat/SystemHeat/SystemHeat.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU @@ -10,7 +10,7 @@ Properties SystemHeat SystemHeat - v4.5 + v4.8 512 @@ -81,10 +81,12 @@ + + @@ -102,6 +104,7 @@ + diff --git a/SystemHeat/SystemHeat/SystemHeatAtmosphereSimulator.cs b/SystemHeat/SystemHeat/SystemHeatAtmosphereSimulator.cs index 3891d58..3a44fa7 100644 --- a/SystemHeat/SystemHeat/SystemHeatAtmosphereSimulator.cs +++ b/SystemHeat/SystemHeat/SystemHeatAtmosphereSimulator.cs @@ -46,6 +46,8 @@ public void CalculateConstants(CelestialBody body, float speed, double altitude) atmoTemp = body.GetFullTemperature(altitude, atmosphereTemperatureOffset); density = body.GetDensity(staticPressurekPa, atmoTemp); mach = CalculateMachNumber(body, speed, staticPressurekPa, density); + + staticPressurekPa = body.GetPressure(altitude); convectiveMachScale = Math.Pow(UtilMath.Clamp01( (mach - PhysicsGlobals.NewtonianMachTempLerpStartMach) / (PhysicsGlobals.NewtonianMachTempLerpEndMach - PhysicsGlobals.NewtonianMachTempLerpStartMach)), @@ -56,6 +58,10 @@ public void CalculateConstants(CelestialBody body, float speed, double altitude) public float CalculateMachNumber(CelestialBody body, float speed, double staticPressure, double atmDensity) { double speedOfSound = body.GetSpeedOfSound(staticPressure, atmDensity); + + if (speedOfSound < 0.00001) + return 0f; + return speed/(float)speedOfSound; } public virtual float CalculateShockTemperature(CelestialBody body, float machNumber, float speed) diff --git a/SystemHeat/SystemHeat/SystemHeatGameSettings.cs b/SystemHeat/SystemHeat/SystemHeatGameSettings.cs new file mode 100644 index 0000000..a0272b9 --- /dev/null +++ b/SystemHeat/SystemHeat/SystemHeatGameSettings.cs @@ -0,0 +1,385 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SystemHeat +{ + + public class SystemHeatGameSettings_ReactorDamage : GameParameters.CustomParameterNode + { + + [GameParameters.CustomParameterUI("Allow Reactor Damage", + toolTip = "If enabled, overheated reactors will take damage", + autoPersistance = true)] + public bool reactorDamage = true; + + [GameParameters.CustomParameterUI("Allow Reactor Repairs", + toolTip = "If enabled, damaged fission reactors can be repaired", + autoPersistance = true)] + public bool reactorRepairs = true; + + [GameParameters.CustomIntParameterUI("Engineer Level Required", + maxValue = 5, minValue = 1, stepSize = 1, + toolTip = "Engineer level needed to repair a reactor", + autoPersistance = true)] + public int engineerLevel = 5; + + + [GameParameters.CustomFloatParameterUI("Amount Repaired per Repair Kit", + maxValue = 100f, minValue = 5f, asPercentage = true, stepCount = 20, + toolTip = "The amount of repairs a single kit makes", autoPersistance = true)] + public float repairPerKit = 25f; + + [GameParameters.CustomFloatParameterUI("Minimum Repairable level", + maxValue = 100f, minValue = 5f, asPercentage = true, stepCount = 20, + toolTip = "If a reactor is below this level it can't be repaired", autoPersistance = true)] + public float repairThreshold = 10f; + + + [GameParameters.CustomFloatParameterUI("Maximum Allowed Repair Amount", + maxValue = 100f, minValue = 5f, asPercentage = true, stepCount = 20, + toolTip = "The maximum amount you are allowed to repair a reactor", autoPersistance = true)] + public float repairMax = 75f; + + public override string DisplaySection + { + get + { + return Section; + } + } + + public override string Section + { + get + { + return "System Heat"; + } + } + + public override string Title + { + get + { + return "Fission Reactor Damage"; + } + } + + public override int SectionOrder + { + get + { + return 3; + } + } + + public override GameParameters.GameMode GameMode + { + get + { + return GameParameters.GameMode.ANY; + } + } + + public override bool HasPresets + { + get + { + return false; + } + } + + public override bool Enabled(System.Reflection.MemberInfo member, GameParameters parameters) + { + if (reactorDamage || member.Name == "reactorDamage") + return true; + else + return false; + } + + public static bool ReactorDamage + { + get + { + if (HighLogic.LoadedScene == GameScenes.MAINMENU) + return true; + SystemHeatGameSettings_ReactorDamage settings = HighLogic.CurrentGame.Parameters.CustomParams(); + return settings.reactorDamage; + } + + set + { + SystemHeatGameSettings_ReactorDamage settings = HighLogic.CurrentGame.Parameters.CustomParams(); + settings.reactorDamage = value; + } + } + + public static int EngineerLevel + { + get + { + SystemHeatGameSettings_ReactorDamage settings = HighLogic.CurrentGame.Parameters.CustomParams(); + return settings.engineerLevel; + } + + set + { + SystemHeatGameSettings_ReactorDamage settings = HighLogic.CurrentGame.Parameters.CustomParams(); + settings.engineerLevel = value; + } + } + + public static float RepairPerKit + { + get + { + SystemHeatGameSettings_ReactorDamage settings = HighLogic.CurrentGame.Parameters.CustomParams(); + return settings.repairPerKit; + } + + set + { + SystemHeatGameSettings_ReactorDamage settings = HighLogic.CurrentGame.Parameters.CustomParams(); + settings.repairPerKit = value; + } + } + public static float RepairThreshold + { + get + { + SystemHeatGameSettings_ReactorDamage settings = HighLogic.CurrentGame.Parameters.CustomParams(); + return settings.repairThreshold; + } + + set + { + SystemHeatGameSettings_ReactorDamage settings = HighLogic.CurrentGame.Parameters.CustomParams(); + settings.repairThreshold = value; + } + } + + + public static float RepairMax + { + get + { + SystemHeatGameSettings_ReactorDamage settings = HighLogic.CurrentGame.Parameters.CustomParams(); + return settings.repairMax; + } + + set + { + SystemHeatGameSettings_ReactorDamage settings = HighLogic.CurrentGame.Parameters.CustomParams(); + settings.repairMax = value; + } + } + + } +} + +public class SystemHeatGameSettings_NuclearFuel : GameParameters.CustomParameterNode +{ + [GameParameters.CustomParameterUI("Nuclear Fuel Transfer Needs Engineers", + toolTip = "If enabled, nuclear fuel transfer needs experienced engineers", + autoPersistance = true)] + public bool requireEngineersForTransfer = true; + + [GameParameters.CustomIntParameterUI("Engineer Level Required", + maxValue = 5, minValue = 1, stepSize = 1, + toolTip = "Engineer level needed to transfer", + autoPersistance = true)] + public int engineerLevel = 3; + + public override string DisplaySection + { + get + { + return Section; + } + } + + public override string Section + { + get + { + return "System Heat"; + } + } + + public override string Title + { + get + { + return "Nuclear Fuel"; + } + } + + public override int SectionOrder + { + get + { + return 1; + } + } + + public override GameParameters.GameMode GameMode + { + get + { + return GameParameters.GameMode.ANY; + } + } + + public override bool HasPresets + { + get + { + return false; + } + } + + public override bool Enabled(System.Reflection.MemberInfo member, GameParameters parameters) + { + if (requireEngineersForTransfer || member.Name == "requireEngineersForTransfer") + return true; + else + return false; + } + + public static bool RequireEngineersForTransfer + { + get + { + if (HighLogic.LoadedScene == GameScenes.MAINMENU) + return true; + SystemHeatGameSettings_NuclearFuel settings = HighLogic.CurrentGame.Parameters.CustomParams(); + return settings.requireEngineersForTransfer; + } + + set + { + SystemHeatGameSettings_NuclearFuel settings = HighLogic.CurrentGame.Parameters.CustomParams(); + settings.requireEngineersForTransfer = value; + } + } + + public static int EngineerLevel + { + get + { + SystemHeatGameSettings_NuclearFuel settings = HighLogic.CurrentGame.Parameters.CustomParams(); + return settings.engineerLevel; + } + + set + { + SystemHeatGameSettings_NuclearFuel settings = HighLogic.CurrentGame.Parameters.CustomParams(); + settings.engineerLevel = value; + } + } + +} + +public class SystemHeatGameSettings_Boiloff : GameParameters.CustomParameterNode +{ + [GameParameters.CustomParameterUI("Fuel Boiloff", + toolTip = "If enabled, configured fuels that need coolin", autoPersistance = true)] + public bool boiloffEnabled = true; + + [GameParameters.CustomFloatParameterUI("Fuel Boiloff Rate", + maxValue = 5.0f, minValue = 0.01f, asPercentage = true, stepCount = 100, + toolTip = "Modifies base boiloff rate", autoPersistance = true)] + public float boiloffRate = 1.0f; + + public override string DisplaySection + { + get + { + return Section; + } + } + + public override string Section + { + get + { + return "System Heat"; + } + } + + public override string Title + { + get + { + return "Boiloff"; + } + } + + public override int SectionOrder + { + get + { + return 1; + } + } + + public override GameParameters.GameMode GameMode + { + get + { + return GameParameters.GameMode.ANY; + } + } + + public override bool HasPresets + { + get + { + return false; + } + } + + public override bool Enabled(System.Reflection.MemberInfo member, GameParameters parameters) + { + if (boiloffEnabled || member.Name == "boiloffEnabled") + return true; + else + return false; + } + + public static bool BoiloffEnabled + { + get + { + if (HighLogic.LoadedScene == GameScenes.MAINMENU) + return true; + SystemHeatGameSettings_Boiloff settings = HighLogic.CurrentGame.Parameters.CustomParams(); + return settings.boiloffEnabled; + } + + set + { + SystemHeatGameSettings_Boiloff settings = HighLogic.CurrentGame.Parameters.CustomParams(); + settings.boiloffEnabled = value; + } + } + + public static float BoiloffScale + { + get + { + SystemHeatGameSettings_Boiloff settings = HighLogic.CurrentGame.Parameters.CustomParams(); + return settings.boiloffRate; + } + + set + { + SystemHeatGameSettings_Boiloff settings = HighLogic.CurrentGame.Parameters.CustomParams(); + settings.boiloffRate = value; + } + } + + +} diff --git a/SystemHeat/SystemHeat/SystemHeatSimulator.cs b/SystemHeat/SystemHeat/SystemHeatSimulator.cs index c760a5f..338aadf 100644 --- a/SystemHeat/SystemHeat/SystemHeatSimulator.cs +++ b/SystemHeat/SystemHeat/SystemHeatSimulator.cs @@ -130,7 +130,7 @@ public virtual void Simulate() { if (SimulationBody != null) atmoSim.SimulateAtmosphere(SimulationBody, SimulationSpeed, SimulationAltitude); - + foreach (HeatLoop loop in HeatLoops) { loop.Simulate(TimeWarp.fixedDeltaTime); @@ -147,7 +147,9 @@ public virtual void SimulateEditor() if (HeatLoops != null) { if (SimulationBody != null) + { atmoSim.SimulateAtmosphere(SimulationBody, SimulationSpeed, SimulationAltitude); + } foreach (HeatLoop loop in HeatLoops) { loop.Simulate(SystemHeatSettings.SimulationRateEditor); @@ -161,7 +163,8 @@ public virtual void SimulateEditor() /// the module to add public void AddHeatModule(ModuleSystemHeat module) { - AddHeatModuleToLoop(module.currentLoopID, module); + if (module.moduleUsed) + AddHeatModuleToLoop(module.currentLoopID, module); } /// @@ -171,21 +174,26 @@ public void AddHeatModule(ModuleSystemHeat module) /// the module to add public void AddHeatModuleToLoop(int loopID, ModuleSystemHeat module) { - // Build a new heat loop as needed - if (!HasLoop(loopID)) + if (module.moduleUsed) { - HeatLoops.Add(new HeatLoop(this, loopID)); + // Build a new heat loop as needed + if (!HasLoop(loopID)) + { + if (HeatLoops != null) + HeatLoops.Add(new HeatLoop(this, loopID)); + Utils.Log(String.Format("[SystemHeatSimulator]: Created new Heat Loop {0}", loopID), LogType.Simulator); + } + if (HeatLoops != null) + { + foreach (HeatLoop loop in HeatLoops) + { + if (loop.ID == loopID) + loop.AddHeatModule(module); + } + } - Utils.Log(String.Format("[SystemHeatSimulator]: Created new Heat Loop {0}", loopID), LogType.Simulator); - } - foreach (HeatLoop loop in HeatLoops) - { - if (loop.ID == loopID) - loop.AddHeatModule(module); + Utils.Log(String.Format("[SystemHeatSimulator]: Added module {0} to Heat Loop {1}", module.moduleID, loopID), LogType.Simulator); } - - - Utils.Log(String.Format("[SystemHeatSimulator]: Added module {0} to Heat Loop {1}", module.moduleID, loopID), LogType.Simulator); } /// diff --git a/SystemHeat/SystemHeat/SystemHeatVessel.cs b/SystemHeat/SystemHeat/SystemHeatVessel.cs index 286e9da..cea638f 100644 --- a/SystemHeat/SystemHeat/SystemHeatVessel.cs +++ b/SystemHeat/SystemHeat/SystemHeatVessel.cs @@ -37,6 +37,7 @@ protected override void OnStart() simulator = new SystemHeatSimulator(); // These events need to trigger a refresh + GameEvents.onVesselChange.Add(new EventData.OnEvent(OnVesselChanged)); GameEvents.onVesselGoOnRails.Add(new EventData.OnEvent(RefreshVesselData)); GameEvents.onVesselWasModified.Add(new EventData.OnEvent(RefreshVesselData)); GameEvents.onVesselDocking.Add(new EventData.OnEvent(OnVesselsDocked)); @@ -51,6 +52,7 @@ void OnDestroy() GameEvents.OnVesselRollout.Remove(OnVesselRollout); GameEvents.onVesselDocking.Remove(OnVesselsDocked); GameEvents.onVesselsUndocking.Remove(OnVesselsUndocked); + GameEvents.onVesselChange.Remove(OnVesselChanged); } void FixedUpdate() @@ -96,6 +98,18 @@ protected void RefreshVesselData(ConfigNode node) Utils.Log(String.Format("[SystemHeatVessel]: Refreshing VesselData from save node event", this.GetType().Name), LogType.Simulator); } + protected void OnVesselChanged(Vessel v) + { + Utils.Log(String.Format("[SystemHeatVessel]: Vessel changed", this.GetType().Name), LogType.Simulator); + ResetSimulation(); + + SystemHeatOverlay.Instance.ResetOverlay(); + if (FlightGlobals.ActiveVessel == this.vessel) + { + SystemHeatOverlay.Instance.AssignSimulator(simulator); + SystemHeatUI.Instance.toolbarPanel.AssignSimulator(simulator); + } + } protected void OnVesselsDocked(uint v1, uint v2) { Utils.Log(String.Format("[SystemHeatVessel]: Vessel docked", this.GetType().Name), LogType.Simulator); diff --git a/SystemHeat/SystemHeat/UI/Overlay/OverlayLoop.cs b/SystemHeat/SystemHeat/UI/Overlay/OverlayLoop.cs index 1ed96c6..40f5f83 100644 --- a/SystemHeat/SystemHeat/UI/Overlay/OverlayLoop.cs +++ b/SystemHeat/SystemHeat/UI/Overlay/OverlayLoop.cs @@ -67,18 +67,21 @@ protected void UpdateLinePositions() public void GenerateLinePoints() { + int moduleCount = heatLoop.GetActiveModuleCount(); // Collect positions of all heat modules - Vector3[] systemCoords = new Vector3[heatLoop.LoopModules.Count]; - for (int i = 0; i < heatLoop.LoopModules.Count; i++) + Vector3[] systemCoords = new Vector3[moduleCount]; + for (int i = 0; i < moduleCount; i++) { - if (HighLogic.LoadedSceneIsEditor) - { - systemCoords[i] = heatLoop.LoopModules[i].part.transform.position; - } - - if (HighLogic.LoadedSceneIsFlight) - { - systemCoords[i] = heatLoop.LoopModules[i].part.vessel.vesselTransform.InverseTransformPoint(heatLoop.LoopModules[i].part.transform.position); + if (heatLoop.LoopModules[i].moduleUsed) { + if (HighLogic.LoadedSceneIsEditor) + { + systemCoords[i] = heatLoop.LoopModules[i].part.transform.position; + } + + if (HighLogic.LoadedSceneIsFlight) + { + systemCoords[i] = heatLoop.LoopModules[i].part.vessel.vesselTransform.InverseTransformPoint(heatLoop.LoopModules[i].part.transform.position); + } } } diff --git a/SystemHeat/SystemHeat/UI/Overlay/OverlayPanel.cs b/SystemHeat/SystemHeat/UI/Overlay/OverlayPanel.cs index 28c2f9e..26d2cb4 100644 --- a/SystemHeat/SystemHeat/UI/Overlay/OverlayPanel.cs +++ b/SystemHeat/SystemHeat/UI/Overlay/OverlayPanel.cs @@ -80,15 +80,15 @@ public void LateUpdate() { if (heatModule.totalSystemFlux <= 0f) { - infoPanelUpperText.text = Localizer.Format("#LOC_SystemHeat_OverlayPanel_UpperTextNoTemp", heatModule.totalSystemFlux.ToString("F0")); + infoPanelUpperText.text = Localizer.Format("#LOC_SystemHeat_OverlayPanel_UpperTextNoTemp", Utils.ToSI(heatModule.totalSystemFlux,"F0")); infoPanelTop.sizeDelta = new Vector2(infoPanelTop.sizeDelta.x, 26f); } else { - infoPanelUpperText.text = Localizer.Format("#LOC_SystemHeat_OverlayPanel_UpperText", heatModule.systemNominalTemperature.ToString("F0"), heatModule.totalSystemFlux.ToString("F0")); + infoPanelUpperText.text = Localizer.Format("#LOC_SystemHeat_OverlayPanel_UpperText", heatModule.systemNominalTemperature.ToString("F0"), Utils.ToSI(heatModule.totalSystemFlux, "F0")); infoPanelTop.sizeDelta = new Vector2(infoPanelTop.sizeDelta.x, 48f); } - infoPanelLowerText.text = Localizer.Format("#LOC_SystemHeat_OverlayPanel_LowerText", loop.Temperature.ToString("F0"), loop.NominalTemperature.ToString("F0"), loop.NetFlux.ToString("F0"), loop.Volume.ToString("F2")); + infoPanelLowerText.text = Localizer.Format("#LOC_SystemHeat_OverlayPanel_LowerText", loop.Temperature.ToString("F0"), loop.NominalTemperature.ToString("F0"), Utils.ToSI(loop.NetFlux,"F0"), loop.Volume.ToString("F2")); } float nominalTempDelta = loop.NominalTemperature - heatModule.systemNominalTemperature; diff --git a/SystemHeat/SystemHeat/UI/Overlay/SystemHeatOverlay.cs b/SystemHeat/SystemHeat/UI/Overlay/SystemHeatOverlay.cs index 1f5a42c..46e2f88 100644 --- a/SystemHeat/SystemHeat/UI/Overlay/SystemHeatOverlay.cs +++ b/SystemHeat/SystemHeat/UI/Overlay/SystemHeatOverlay.cs @@ -202,7 +202,7 @@ protected void LateUpdate() if (simulator != null && !(HighLogic.LoadedSceneIsFlight && MapView.MapIsEnabled)) { - if (simulator.HeatLoops == null || simulator.HeatLoops.Count == 0 && overlayLoops.Count >0) + if (simulator.HeatLoops == null || simulator.HeatLoops.Count == 0 && overlayLoops.Count > 0) { Utils.Log(String.Format("[SystemHeatOverlay]: No loops, destroying overlay"), LogType.Overlay); @@ -245,6 +245,7 @@ protected void LateUpdate() foreach (ModuleSystemHeat system in loop.LoopModules) { int index = overlayPanels.FindIndex(f => f.heatModule == system); + if (index == -1) { Utils.Log($"[SystemHeatOverlay]: Building new OverlayPanel for system {system.moduleID}", LogType.Overlay); @@ -254,14 +255,30 @@ protected void LateUpdate() newUIPanel.transform.localPosition = Vector3.zero; OverlayPanel panel = newUIPanel.AddComponent(); panel.parentCanvas = UIMasterController.Instance.appCanvas; - panel.SetupLoop(loop, system, (SystemHeatUI.Instance.OverlayMasterState && SystemHeatUI.Instance.OverlayLoopState(loop.ID))); + if (system.moduleUsed) + { + panel.SetupLoop(loop, system, (SystemHeatUI.Instance.OverlayMasterState && SystemHeatUI.Instance.OverlayLoopState(loop.ID))); + } + else + { + panel.SetupLoop(loop, system, (false && SystemHeatUI.Instance.OverlayLoopState(loop.ID))); + } overlayPanels.Add(panel); + } else { // Update the panel - overlayPanels[index].UpdateLoop(loop, system, (SystemHeatUI.Instance.OverlayMasterState && SystemHeatUI.Instance.OverlayLoopState(loop.ID))); + if (system.moduleUsed) + { + overlayPanels[index].UpdateLoop(loop, system, (SystemHeatUI.Instance.OverlayMasterState && SystemHeatUI.Instance.OverlayLoopState(loop.ID))); + } + else + { + overlayPanels[index].UpdateLoop(loop, system, (false && SystemHeatUI.Instance.OverlayLoopState(loop.ID))); + } + } } } @@ -309,10 +326,6 @@ public void SetVisible(bool visible) SetLoopVisiblity(visible); SetPanelVisiblity(visible); - //foreach (int id in overlayLoopVisibility.Keys.ToList()) - //{ - // overlayLoopVisibility[id] = visible; - //} } public void SetVisible(bool visible, int loopID) { @@ -321,8 +334,6 @@ public void SetVisible(bool visible, int loopID) SetLoopVisiblity(visible, loopID); SetPanelVisiblity(visible, loopID); - - // overlayLoopVisibility[loopID] = visible; } private void SetPanelVisiblity(bool visible) { @@ -331,8 +342,10 @@ private void SetPanelVisiblity(bool visible) { if (overlayPanels[i] != null) { - - overlayPanels[i].SetVisibility(visible); + if (overlayPanels[i].heatModule.moduleUsed) + overlayPanels[i].SetVisibility(visible); + else + overlayPanels[i].SetVisibility(false); } } } @@ -343,7 +356,10 @@ private void SetPanelVisiblity(bool visible, int loopID) { if (overlayPanels[i] != null && overlayPanels[i].loop.ID == loopID) { - overlayPanels[i].SetVisibility(visible); + if (overlayPanels[i].heatModule.moduleUsed) + overlayPanels[i].SetVisibility(visible); + else + overlayPanels[i].SetVisibility(false); } } } diff --git a/SystemHeat/SystemHeat/UI/ReactorUI/ReactorWidget.cs b/SystemHeat/SystemHeat/UI/ReactorUI/ReactorWidget.cs index 2033b4a..493faf8 100644 --- a/SystemHeat/SystemHeat/UI/ReactorUI/ReactorWidget.cs +++ b/SystemHeat/SystemHeat/UI/ReactorUI/ReactorWidget.cs @@ -225,7 +225,8 @@ protected void FixedUpdate() if (module.moduleName == "FusionReactor" || module.moduleName == "ModuleFusionEngine") { nominalTemp = float.Parse(module.Fields.GetValue("SystemOutletTemperature").ToString()); - datafields["heatGenerated"].SetValue(Localizer.Format("#LOC_SystemHeat_ReactorPanel_Field_HeatGenerated", float.Parse(module.Fields.GetValue("SystemPower").ToString()).ToString("F0"))); + datafields["heatGenerated"].SetValue(Localizer.Format("#LOC_SystemHeat_ReactorPanel_Field_HeatGenerated", + Utils.ToSI(float.Parse(module.Fields.GetValue("SystemPower").ToString()), "F0"))); datafields["powerGenerated"].SetValue(Localizer.Format("#LOC_SystemHeat_ReactorPanel_Field_PowerGenerated", float.Parse(module.Fields.GetValue("CurrentPowerProduced").ToString()).ToString("F0"))); datafields["lifetime"].SetValue(Localizer.Format("#LOC_SystemHeat_ReactorPanel_Field_CoreLife", module.Fields.GetValue("FuelInput"))); datafields["temperature"].SetValue(Localizer.Format("#LOC_SystemHeat_ReactorPanel_Field_CoreTemperature", heatModule.LoopTemperature.ToString("F0"))); @@ -263,7 +264,8 @@ protected void FixedUpdate() if (module.moduleName == "ModuleSystemHeatFissionReactor" || module.moduleName == "ModuleSystemHeatFissionEngine") { nominalTemp = float.Parse(module.Fields.GetValue("NominalTemperature").ToString()); - datafields["heatGenerated"].SetValue(Localizer.Format("#LOC_SystemHeat_ReactorPanel_Field_HeatGenerated", float.Parse(module.Fields.GetValue("CurrentHeatGeneration").ToString()).ToString("F0"))); + datafields["heatGenerated"].SetValue(Localizer.Format("#LOC_SystemHeat_ReactorPanel_Field_HeatGenerated", + Utils.ToSI(float.Parse(module.Fields.GetValue("CurrentHeatGeneration").ToString()), "F0"))); datafields["powerGenerated"].SetValue(Localizer.Format("#LOC_SystemHeat_ReactorPanel_Field_PowerGenerated", float.Parse(module.Fields.GetValue("CurrentElectricalGeneration").ToString()).ToString("F0"))); datafields["lifetime"].SetValue(Localizer.Format("#LOC_SystemHeat_ReactorPanel_Field_CoreLife", module.Fields.GetValue("FuelStatus"))); diff --git a/SystemHeat/SystemHeat/UI/ToolbarPanel.cs b/SystemHeat/SystemHeat/UI/ToolbarPanel.cs index 1c42b81..90a6160 100644 --- a/SystemHeat/SystemHeat/UI/ToolbarPanel.cs +++ b/SystemHeat/SystemHeat/UI/ToolbarPanel.cs @@ -350,8 +350,8 @@ protected void Update() } totalLoopsValue.text = simulator.HeatLoops.Count.ToString(); - totalOutgoingFluxValue.text = Localizer.Format("#LOC_SystemHeat_ToolbarPanel_OutgoingFluxValue", simulator.TotalHeatRejection.ToString("F0")); - totalIncomingFluxValue.text = Localizer.Format("#LOC_SystemHeat_ToolbarPanel_IncomingFluxValue", simulator.TotalHeatGeneration.ToString("F0")); + totalOutgoingFluxValue.text = Localizer.Format("#LOC_SystemHeat_ToolbarPanel_OutgoingFluxValue", Utils.ToSI(simulator.TotalHeatRejection,"F0")); + totalIncomingFluxValue.text = Localizer.Format("#LOC_SystemHeat_ToolbarPanel_IncomingFluxValue", Utils.ToSI(simulator.TotalHeatGeneration, "F0")); } } void DestroyLoopWidgets() diff --git a/SystemHeat/SystemHeat/UI/ToolbarPanelLoopWidget.cs b/SystemHeat/SystemHeat/UI/ToolbarPanelLoopWidget.cs index d50d85b..f9cbf2d 100644 --- a/SystemHeat/SystemHeat/UI/ToolbarPanelLoopWidget.cs +++ b/SystemHeat/SystemHeat/UI/ToolbarPanelLoopWidget.cs @@ -95,7 +95,7 @@ void Update() prefix = ""; fluxTextValue.text = Localizer.Format("#LOC_SystemHeat_ToolbarPanel_LoopFluxValue", prefix, - lp.NetFlux.ToString("F1")); + Utils.ToSI(lp.NetFlux,"F0")); if (lp.Temperature >= lp.NominalTemperature) { diff --git a/SystemHeat/SystemHeat/Utils.cs b/SystemHeat/SystemHeat/Utils.cs index 3a4317a..67bf2a7 100644 --- a/SystemHeat/SystemHeat/Utils.cs +++ b/SystemHeat/SystemHeat/Utils.cs @@ -53,6 +53,35 @@ public static void LogError(string toLog) { Debug.LogError(String.Format("[{0}]{1}", logTag, toLog)); } + + + public static string ToSI(float d, string format = null, float factor= 1000f) + { + if (d == 0.0) + return d.ToString(format); + + char[] incPrefixes = new[] { 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' }; + char[] decPrefixes = new[] { 'm', '\u03bc', 'n', 'p', 'f', 'a', 'z', 'y' }; + + d *= factor; + + int degree = Mathf.Clamp((int)Math.Floor(Math.Log10(Math.Abs(d)) / 3), -8, 8); + if (degree == 0) + return d.ToString(format); + + double scaled = d * Math.Pow(1000, -degree); + + char? prefix = null; + + switch (Math.Sign(degree)) + { + case 1: prefix = incPrefixes[degree - 1]; break; + case -1: prefix = decPrefixes[-degree - 1]; break; + } + + return scaled.ToString(format) + " " + prefix; + } + } public static class TransformDeepChildExtension @@ -73,6 +102,7 @@ public static Transform FindDeepChild(this Transform aParent, string aName) return null; } + /* //Depth-first search public static Transform FindDeepChild(this Transform aParent, string aName) diff --git a/changelog.txt b/changelog.txt index 5b9827f..8f261a2 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,17 @@ +v0.6.0 +------ +- Most UI fields now report in SI-prefixed units for flux (W, kW, MW, etc). This is not (yet) true for all modules though. +- Added new functionality to cool fuel tanks with radiators and the SH system +- Added optional patch to apply this to CryoTanks +- Added some guardrails around the handling of Kopernicus planets that might have inconsistently configured atmospheres +- Added ingame settings. The following things can be customized per game +-- Boiloff: enabled and scale +-- Nuclear fuel transfer: require engineers, what level +-- Fission reactor damage: allowed, various tuning variables +- Fixed engine heat for VASIMR argon modes +- Support USI reactors +- Fixes to universal harvester patch + v0.5.6 ------ - Added action groups to ModuleSystemHeatExchanger for Enable, Disable, Toggle and Toggle Direction diff --git a/readme.txt b/readme.txt index 5f3cce8..4f9d60e 100644 --- a/readme.txt +++ b/readme.txt @@ -1,10 +1,9 @@ ================ -SystemHeat 0.5.5 +SystemHeat 0.6.0 ================ A mod for Kerbal Space Program, intended to provide a better experience for heat management, particularly geared towards resource extraction, high energy engines, and nuclear reactors. - ============ DEPENDENCIES ============ @@ -31,6 +30,8 @@ WARNING: Please note that these extras may break your ships in flight as they wi - SystemHeatConverters: Converts converters to use SystemHeat - SystemHeatFissionReactors: Converts nuclear reactors to use SystemHeat - SystemHeatFissionEngines: When installed, nuclear engines will have integrated reactors +- SystemHeatIonEngines: When installed, ion engines will generate heat +- SystemHeatBoiloff: When installed, CryoTanks boiloff will require cooling instead of electricity ======= KSP-AVC