├── .gitignore ├── screenshots ├── sas.png ├── crew.png ├── isru.png ├── Mk1Pod.png ├── Mk2Can.png ├── antennas.png ├── robotics.png ├── scanning.png ├── science.png ├── batteries.png ├── fuel cell.png ├── telescope.png ├── MakingHistory.png ├── blinkenlight.png ├── docking ports.png ├── magnetometer.png ├── turn signals.gif ├── emissive arrays.gif ├── fuel cell array.png ├── reaction wheels.png └── data transmission.gif ├── diagrams ├── bl01diagram.png ├── simpleDiagram.png └── complexDiagram.png ├── files ├── Meshes │ ├── White.png │ ├── z100lamp.mu │ ├── Lightbulb.png │ ├── nubbinLamp.mu │ ├── squareLamp.mu │ └── squareLamp2.mu ├── ModuleManager.4.2.3.dll ├── Parts │ ├── fuelCells │ │ ├── White.png │ │ ├── Lightbulb.png │ │ ├── arrayIndicatorModel.mu │ │ ├── rectangularIndicatorModel.mu │ │ ├── FuelCellArrayIndicator.cfg │ │ └── FuelCellIndicator.cfg │ ├── indicatorLightSmall │ │ ├── Bezel.png │ │ ├── White.png │ │ ├── model.mu │ │ ├── Lightbulb.png │ │ └── indicatorLightSmall.cfg │ ├── cones │ │ └── avionicsCone.cfg │ ├── scanners │ │ ├── surveyScanner.cfg │ │ └── surfaceScanner.cfg │ ├── dockingPorts │ │ ├── inflatableAirlock.cfg │ │ ├── smallPort.cfg │ │ └── mediumPort.cfg │ ├── robotics │ │ └── kal1000.cfg │ ├── science │ │ ├── scienceBox.cfg │ │ ├── gravioli.cfg │ │ ├── barometer.cfg │ │ ├── magnetometer.cfg │ │ ├── variometer.cfg │ │ ├── thermometer.cfg │ │ ├── accelerometer.cfg │ │ ├── telescope.cfg │ │ ├── goo.cfg │ │ └── scienceJr.cfg │ ├── crewable │ │ ├── mk1cockpit.cfg │ │ ├── mk1cockpitInline.cfg │ │ ├── mk1pod_v2.cfg │ │ ├── kv1pod.cfg │ │ ├── mk1cabin.cfg │ │ ├── cupola.cfg │ │ ├── mk1can.cfg │ │ ├── scienceLab.cfg │ │ ├── mk2cockpit.cfg │ │ └── mk2cockpitInline.cfg │ ├── batteries │ │ ├── Z400Indicator.cfg │ │ ├── Z100Indicator.cfg │ │ ├── Z200Indicator.cfg │ │ ├── Z1KIndicator.cfg │ │ └── Z4KIndicator.cfg │ ├── reactionWheels │ │ ├── smallReactionWheel.cfg │ │ ├── largeReactionWheel.cfg │ │ └── mediumReactionWheel.cfg │ ├── antennas │ │ ├── hg55.cfg │ │ ├── smallRelayDish.cfg │ │ ├── mediumRelayDish.cfg │ │ ├── communotron16S.cfg │ │ ├── largeRelayDish.cfg │ │ ├── 88-88.cfg │ │ ├── communotron16.cfg │ │ ├── dtsM1.cfg │ │ └── hg5.cfg │ └── obsolete │ │ └── mk1pod.cfg ├── Agencies │ ├── Blinkenlights.png │ └── Agents.cfg ├── Flags │ └── Blinkenlights-Flag.png ├── Patches │ ├── IndicatorLights_TextureReplacer.cfg │ ├── IndicatorLights_TweakScale.cfg │ └── IndicatorLights_KIS.cfg ├── Config │ └── IndicatorLights.cfg └── ModuleManagerLicense.md ├── release ├── IndicatorLights-1.0.zip ├── IndicatorLights-1.1.zip ├── IndicatorLights-1.2.zip ├── IndicatorLights-1.3.zip ├── IndicatorLights-1.4.zip ├── IndicatorLights-1.5.zip ├── IndicatorLights-1.6.zip ├── IndicatorLights-1.7.zip ├── IndicatorLights-1.8.zip ├── IndicatorLights-1.2.1.zip ├── IndicatorLights-1.2.10.zip ├── IndicatorLights-1.2.11.zip ├── IndicatorLights-1.2.12.zip ├── IndicatorLights-1.2.2.zip ├── IndicatorLights-1.2.3.zip ├── IndicatorLights-1.2.4.zip ├── IndicatorLights-1.2.5.zip ├── IndicatorLights-1.2.6.zip ├── IndicatorLights-1.2.7.zip ├── IndicatorLights-1.2.8.zip ├── IndicatorLights-1.2.9.zip ├── IndicatorLights-1.3.1.zip ├── IndicatorLights-1.4.1.zip ├── IndicatorLights-1.4.2.zip ├── IndicatorLights-1.8.1.zip ├── IndicatorLights-1.8.2.zip ├── IndicatorLights-1.8.3.zip └── assemble_release ├── prerelease ├── IndicatorLights-0.1.zip ├── IndicatorLights-0.2.zip ├── IndicatorLights-0.3.zip ├── IndicatorLights-0.4.zip ├── IndicatorLights-0.5.zip ├── IndicatorLights-0.6.zip ├── IndicatorLights-0.7.zip ├── IndicatorLights-0.8.zip ├── IndicatorLights-0.9.zip ├── IndicatorLights-0.10.zip ├── IndicatorLights-0.11.zip ├── IndicatorLights-0.7.1.zip ├── IndicatorLights-0.9.1.zip ├── IndicatorLights-0.10.1.zip └── assemble_release ├── src ├── IScalar.cs ├── ModuleScienceContainerIndicator.cs ├── IToggle.cs ├── GlobalSettings.cs ├── IColorSource.cs ├── ScalarIDField.cs ├── StaticField.cs ├── ToggleIDField.cs ├── Vessels.cs ├── ColorSourceIDField.cs ├── ModuleScienceDataIndicator.cs ├── IndicatorLights.sln ├── ExperimentalController.cs ├── Console │ ├── HelpCommand.cs │ ├── LightsEnabledCommand.cs │ └── PartsCommand.cs ├── Properties │ └── AssemblyInfo.cs ├── ModuleDockingCrossfeedIndicator.cs ├── RateLimiter.cs ├── VesselScienceTracker.cs ├── ScienceValue.cs ├── ModuleEmissiveArrayController.cs ├── ModuleResourceEnabledIndicator.cs ├── ModuleSourceIndicator.cs ├── RenderType.cs ├── ModuleDockingStateIndicator.cs ├── ModuleBiomeScannerIndicator.cs ├── Logging.cs ├── PartSearchStrategy.cs ├── ModuleCrewIndicatorToggle.cs ├── Loader.cs ├── ModuleDataTransmitterIndicator.cs ├── ModuleBooleanIndicator.cs ├── ModuleEmissiveController.cs ├── ColorSourceArray.cs ├── Extensions.cs └── ModuleResourceIndicator.cs ├── README.md └── examples ├── VesselControlLevel.cfg ├── AttachableResourceIndicator.cfg ├── RandomLights.cfg ├── VesselSituation.cfg └── ArbitraryFieldInputs.cfg /.gitignore: -------------------------------------------------------------------------------- 1 | src/.vs 2 | src/bin 3 | src/obj 4 | -------------------------------------------------------------------------------- /screenshots/sas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/sas.png -------------------------------------------------------------------------------- /screenshots/crew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/crew.png -------------------------------------------------------------------------------- /screenshots/isru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/isru.png -------------------------------------------------------------------------------- /diagrams/bl01diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/diagrams/bl01diagram.png -------------------------------------------------------------------------------- /files/Meshes/White.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Meshes/White.png -------------------------------------------------------------------------------- /files/Meshes/z100lamp.mu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Meshes/z100lamp.mu -------------------------------------------------------------------------------- /screenshots/Mk1Pod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/Mk1Pod.png -------------------------------------------------------------------------------- /screenshots/Mk2Can.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/Mk2Can.png -------------------------------------------------------------------------------- /screenshots/antennas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/antennas.png -------------------------------------------------------------------------------- /screenshots/robotics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/robotics.png -------------------------------------------------------------------------------- /screenshots/scanning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/scanning.png -------------------------------------------------------------------------------- /screenshots/science.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/science.png -------------------------------------------------------------------------------- /diagrams/simpleDiagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/diagrams/simpleDiagram.png -------------------------------------------------------------------------------- /files/Meshes/Lightbulb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Meshes/Lightbulb.png -------------------------------------------------------------------------------- /files/Meshes/nubbinLamp.mu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Meshes/nubbinLamp.mu -------------------------------------------------------------------------------- /files/Meshes/squareLamp.mu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Meshes/squareLamp.mu -------------------------------------------------------------------------------- /screenshots/batteries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/batteries.png -------------------------------------------------------------------------------- /screenshots/fuel cell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/fuel cell.png -------------------------------------------------------------------------------- /screenshots/telescope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/telescope.png -------------------------------------------------------------------------------- /diagrams/complexDiagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/diagrams/complexDiagram.png -------------------------------------------------------------------------------- /files/Meshes/squareLamp2.mu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Meshes/squareLamp2.mu -------------------------------------------------------------------------------- /files/ModuleManager.4.2.3.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/ModuleManager.4.2.3.dll -------------------------------------------------------------------------------- /screenshots/MakingHistory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/MakingHistory.png -------------------------------------------------------------------------------- /screenshots/blinkenlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/blinkenlight.png -------------------------------------------------------------------------------- /screenshots/docking ports.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/docking ports.png -------------------------------------------------------------------------------- /screenshots/magnetometer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/magnetometer.png -------------------------------------------------------------------------------- /screenshots/turn signals.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/turn signals.gif -------------------------------------------------------------------------------- /files/Parts/fuelCells/White.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Parts/fuelCells/White.png -------------------------------------------------------------------------------- /release/IndicatorLights-1.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.0.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.1.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.2.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.3.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.3.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.4.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.4.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.5.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.5.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.6.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.6.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.7.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.7.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.8.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.8.zip -------------------------------------------------------------------------------- /screenshots/emissive arrays.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/emissive arrays.gif -------------------------------------------------------------------------------- /screenshots/fuel cell array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/fuel cell array.png -------------------------------------------------------------------------------- /screenshots/reaction wheels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/reaction wheels.png -------------------------------------------------------------------------------- /files/Agencies/Blinkenlights.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Agencies/Blinkenlights.png -------------------------------------------------------------------------------- /files/Flags/Blinkenlights-Flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Flags/Blinkenlights-Flag.png -------------------------------------------------------------------------------- /prerelease/IndicatorLights-0.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/prerelease/IndicatorLights-0.1.zip -------------------------------------------------------------------------------- /prerelease/IndicatorLights-0.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/prerelease/IndicatorLights-0.2.zip -------------------------------------------------------------------------------- /prerelease/IndicatorLights-0.3.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/prerelease/IndicatorLights-0.3.zip -------------------------------------------------------------------------------- /prerelease/IndicatorLights-0.4.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/prerelease/IndicatorLights-0.4.zip -------------------------------------------------------------------------------- /prerelease/IndicatorLights-0.5.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/prerelease/IndicatorLights-0.5.zip -------------------------------------------------------------------------------- /prerelease/IndicatorLights-0.6.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/prerelease/IndicatorLights-0.6.zip -------------------------------------------------------------------------------- /prerelease/IndicatorLights-0.7.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/prerelease/IndicatorLights-0.7.zip -------------------------------------------------------------------------------- /prerelease/IndicatorLights-0.8.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/prerelease/IndicatorLights-0.8.zip -------------------------------------------------------------------------------- /prerelease/IndicatorLights-0.9.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/prerelease/IndicatorLights-0.9.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.2.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.2.1.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.2.10.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.2.10.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.2.11.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.2.11.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.2.12.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.2.12.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.2.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.2.2.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.2.3.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.2.3.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.2.4.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.2.4.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.2.5.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.2.5.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.2.6.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.2.6.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.2.7.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.2.7.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.2.8.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.2.8.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.2.9.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.2.9.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.3.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.3.1.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.4.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.4.1.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.4.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.4.2.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.8.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.8.1.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.8.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.8.2.zip -------------------------------------------------------------------------------- /release/IndicatorLights-1.8.3.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/release/IndicatorLights-1.8.3.zip -------------------------------------------------------------------------------- /screenshots/data transmission.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/screenshots/data transmission.gif -------------------------------------------------------------------------------- /files/Parts/fuelCells/Lightbulb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Parts/fuelCells/Lightbulb.png -------------------------------------------------------------------------------- /prerelease/IndicatorLights-0.10.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/prerelease/IndicatorLights-0.10.zip -------------------------------------------------------------------------------- /prerelease/IndicatorLights-0.11.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/prerelease/IndicatorLights-0.11.zip -------------------------------------------------------------------------------- /prerelease/IndicatorLights-0.7.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/prerelease/IndicatorLights-0.7.1.zip -------------------------------------------------------------------------------- /prerelease/IndicatorLights-0.9.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/prerelease/IndicatorLights-0.9.1.zip -------------------------------------------------------------------------------- /prerelease/IndicatorLights-0.10.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/prerelease/IndicatorLights-0.10.1.zip -------------------------------------------------------------------------------- /files/Parts/indicatorLightSmall/Bezel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Parts/indicatorLightSmall/Bezel.png -------------------------------------------------------------------------------- /files/Parts/indicatorLightSmall/White.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Parts/indicatorLightSmall/White.png -------------------------------------------------------------------------------- /files/Parts/indicatorLightSmall/model.mu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Parts/indicatorLightSmall/model.mu -------------------------------------------------------------------------------- /files/Patches/IndicatorLights_TextureReplacer.cfg: -------------------------------------------------------------------------------- 1 | @TextureReplacer:AFTER[TextureReplacer] 2 | { 3 | keepLoaded = ^IndicatorLights/ 4 | } 5 | -------------------------------------------------------------------------------- /files/Parts/fuelCells/arrayIndicatorModel.mu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Parts/fuelCells/arrayIndicatorModel.mu -------------------------------------------------------------------------------- /files/Parts/indicatorLightSmall/Lightbulb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Parts/indicatorLightSmall/Lightbulb.png -------------------------------------------------------------------------------- /files/Parts/fuelCells/rectangularIndicatorModel.mu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KSPSnark/IndicatorLights/HEAD/files/Parts/fuelCells/rectangularIndicatorModel.mu -------------------------------------------------------------------------------- /src/IScalar.cs: -------------------------------------------------------------------------------- 1 | namespace IndicatorLights 2 | { 3 | /// 4 | /// A simple interface for controllers that work off a scalar quatity (e.g. resource level). 5 | /// 6 | public interface IScalar 7 | { 8 | double ScalarValue { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ModuleScienceContainerIndicator.cs: -------------------------------------------------------------------------------- 1 | namespace IndicatorLights 2 | { 3 | class ModuleScienceContainerIndicator : ModuleScienceDataIndicatorBase 4 | { 5 | // Nothing to see here, folks... does nothing other than specialize the base 6 | // class for ModuleScienceContainer. 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /files/Patches/IndicatorLights_TweakScale.cfg: -------------------------------------------------------------------------------- 1 | // Thanks to Sharpy in the KSP forums for providing both the feature 2 | // request and the implementation technique! 3 | 4 | @PART[indicatorLightSmall]:AFTER[IndicatorLights]:NEEDS[TweakScale] 5 | { 6 | MODULE { 7 | name = TweakScale 8 | type = free 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/IToggle.cs: -------------------------------------------------------------------------------- 1 | namespace IndicatorLights 2 | { 3 | /// 4 | /// A simple interface for toggle controllers. 5 | /// 6 | public interface IToggle 7 | { 8 | /// 9 | /// The current on/off status of the toggle. 10 | /// 11 | bool ToggleStatus { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /files/Agencies/Agents.cfg: -------------------------------------------------------------------------------- 1 | AGENT 2 | { 3 | name = Blinkenlights LLC 4 | title = Blinkenlights LLC 5 | 6 | description = Give us your tired old technology and we'll glitz it up with distracting glowy things! 7 | 8 | logoURL = IndicatorLights/Agencies/Blinkenlights 9 | 10 | // mentalities 11 | mentality = Commercial 12 | mentality = Industrial 13 | mentality = Economic 14 | } 15 | -------------------------------------------------------------------------------- /prerelease/assemble_release: -------------------------------------------------------------------------------- 1 | echo Creating pre-release file structure 2 | mkdir -p GameData/IndicatorLights 3 | cp ../files/ModuleManager.*.dll GameData 4 | cp ../files/ModuleManagerLicense.md GameData 5 | cp ../files/changelog.txt GameData/IndicatorLights 6 | cp ../src/bin/Release/IndicatorLights.dll GameData/IndicatorLights 7 | cp ../LICENSE GameData/IndicatorLights 8 | cp ../README.md GameData/IndicatorLights 9 | cp -R ../files/Agencies GameData/IndicatorLights 10 | cp -R ../files/Flags GameData/IndicatorLights 11 | cp -R ../files/Meshes GameData/IndicatorLights 12 | cp -R ../files/Parts GameData/IndicatorLights 13 | cp -r ../files/Patches GameData/IndicatorLights 14 | echo Done. 15 | -------------------------------------------------------------------------------- /files/Config/IndicatorLights.cfg: -------------------------------------------------------------------------------- 1 | IndicatorLights { 2 | // Default color choices for each kerbal class. This is the color that gets used 3 | // for crew indicators on any part that doesn't override the colors with something 4 | // specific. 5 | // 6 | // Note that IndicatorLights has integrated support for the Community Trait Icons 7 | // mod. If Community Trait Icons is installed, then IndicatorLights will use the 8 | // colors that come from there, overriding any colors specified here. 9 | CrewIndicatorDefaultColors { 10 | Pilot = $CrewPilot 11 | Engineer = $CrewEngineer 12 | Scientist = $CrewScientist 13 | Tourist = $CrewTourist 14 | Unknown = $Unknown 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/GlobalSettings.cs: -------------------------------------------------------------------------------- 1 | namespace IndicatorLights 2 | { 3 | /// 4 | /// Stores important settings that apply across the whole mod, and which can be modified 5 | /// at run time. 6 | /// 7 | internal static class GlobalSettings 8 | { 9 | private static bool isEnabled = true; 10 | 11 | /// 12 | /// Gets or sets whether all lights everywhere are enabled, across the module. 13 | /// True by default. When set to false, all lights go dark. 14 | /// 15 | public static bool IsEnabled 16 | { 17 | get { return isEnabled; } 18 | set { isEnabled = value; } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/IColorSource.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace IndicatorLights 4 | { 5 | /// 6 | /// Interface for objects that can provide a color. 7 | /// 8 | public interface IColorSource 9 | { 10 | /// 11 | /// Whether a color is currently available. 12 | /// 13 | bool HasColor { get; } 14 | 15 | /// 16 | /// Current color. Do not call this unless HasColor returns true. 17 | /// 18 | Color OutputColor { get; } 19 | 20 | /// 21 | /// Gets the ID of this source. 22 | /// 23 | string ColorSourceID { get; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /release/assemble_release: -------------------------------------------------------------------------------- 1 | echo Creating release file structure 2 | mkdir -p GameData/IndicatorLights 3 | cp ../files/ModuleManager.*.dll GameData 4 | cp ../files/ModuleManagerLicense.md GameData 5 | cp ../files/changelog.txt GameData/IndicatorLights 6 | cp ../src/bin/Release/IndicatorLights.dll GameData/IndicatorLights 7 | cp ../LICENSE GameData/IndicatorLights 8 | cp ../README.md GameData/IndicatorLights 9 | cp -R ../files/Agencies GameData/IndicatorLights 10 | cp -R ../files/Config GameData/IndicatorLights 11 | cp -R ../files/Flags GameData/IndicatorLights 12 | cp -R ../files/Meshes GameData/IndicatorLights 13 | cp -R ../files/Parts GameData/IndicatorLights 14 | cp -r ../files/Patches GameData/IndicatorLights 15 | echo Done. 16 | -------------------------------------------------------------------------------- /files/ModuleManagerLicense.md: -------------------------------------------------------------------------------- 1 | ModuleManager 2 | ============= 3 | 4 | 5 | Original (c) from Ialdabaoth ( https://github.com/Ialdabaoth ) 6 | 7 | Modified by // Modifications by // Maintained by sarbian ( https://github.com/sarbian ) 8 | 9 | 10 | The original licence requirement was: 11 | 12 | --- 13 | 14 | under a CC share-alike license. Anyone is free to do anything they like with ModuleManager's source, with two caveats: 15 | 16 | 1. You credit me as the original creator that your code is based on 17 | 2. You make it ABSOLUTELY CLEAR that your code is not the original ModuleManager, and that any problems that people have with your fork should be taken up with YOU, not me. 18 | 19 | --- 20 | 21 | 22 | THIS IS NOT THE ORIGINAL MODULEMANAGER CODE. 23 | 24 | Do not bother Ialdabaoth about any problems with it. 25 | -------------------------------------------------------------------------------- /src/ScalarIDField.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IndicatorLights 4 | { 5 | /// 6 | /// Used for decorating module properties that are intended to be parsed as scalar IDs. 7 | /// 8 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] 9 | class ScalarIDField : Attribute 10 | { 11 | /// 12 | /// Gets whether the specified field is a scalar ID field. 13 | /// 14 | /// 15 | /// 16 | public static bool Is(BaseField field) 17 | { 18 | return field.FieldInfo.IsDefined(typeof(ScalarIDField), true) 19 | && (typeof(string).IsAssignableFrom(field.FieldInfo.FieldType)); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/StaticField.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace IndicatorLights 5 | { 6 | /// 7 | /// Used for decorating module properties that are intended to be used as static inputs. 8 | /// 9 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] 10 | class StaticField : Attribute 11 | { 12 | /// 13 | /// Gets whether the specified field is a scalar field. 14 | /// 15 | /// 16 | /// 17 | public static bool Is(FieldInfo field) 18 | { 19 | return field.IsDefined(typeof(StaticField), true) 20 | && (typeof(double).IsAssignableFrom(field.FieldType)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/ToggleIDField.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IndicatorLights 4 | { 5 | /// 6 | /// Used for decorating module properties that are intended to be parsed as toggle IDs. 7 | /// 8 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] 9 | class ToggleIDField : Attribute 10 | { 11 | /// 12 | /// Gets whether the specified field is a scalar ID field. 13 | /// 14 | /// 15 | /// 16 | public static bool Is(BaseField field) 17 | { 18 | return field.FieldInfo.IsDefined(typeof(ToggleIDField), true) 19 | && (typeof(string).IsAssignableFrom(field.FieldInfo.FieldType)); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Vessels.cs: -------------------------------------------------------------------------------- 1 | namespace IndicatorLights 2 | { 3 | static class Vessels 4 | { 5 | /// 6 | /// Get the current biome of the ship. Returns null if none. 7 | /// 8 | /// 9 | /// 10 | public static string GetCurrentBiome(this Vessel vessel) 11 | { 12 | if (vessel == null) return null; 13 | double lat = ResourceUtilities.Deg2Rad((vessel.latitude + 180.0 + 90.0) % 180.0 - 90.0); 14 | double lon = ResourceUtilities.Deg2Rad((vessel.longitude + 360.0 + 180.0) % 360.0 - 180.0); 15 | CBAttributeMapSO.MapAttribute biome = ResourceUtilities.GetBiome(lat, lon, vessel.mainBody); 16 | return (biome == null) ? null : biome.name; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/ColorSourceIDField.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IndicatorLights 4 | { 5 | /// 6 | /// Used for decorating module properties that are intended to be parsed as color source IDs. 7 | /// 8 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] 9 | public class ColorSourceIDField : Attribute 10 | { 11 | /// 12 | /// Gets whether the specified field is a color source ID field. 13 | /// 14 | /// 15 | /// 16 | public static bool Is(BaseField field) 17 | { 18 | return field.FieldInfo.IsDefined(typeof(ColorSourceIDField), true) 19 | && (typeof(string).IsAssignableFrom(field.FieldInfo.FieldType)); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## What it does 2 | 3 | Adds various helpful LED "indicators" to enhance your ships. Broadly speaking, these fall into two categories: 4 | 5 | * **Standalone parts:** radially attachable LED lights with customizable colors, which can be toggled on/off with action groups. 6 | * **Enhancements to existing parts:** add LEDs to stock parts to provide a visible, automatic indication of part status (such as whether a fuel cell is on or off). 7 | 8 | For details (either as a player or as a modder), please see the **[IndicatorLights wiki](https://github.com/KSPSnark/IndicatorLights/wiki)**. 9 | 10 | 11 | ## How to install 12 | 13 | Unzip the contents of "GameData" to your GameData folder, same as with most mods. (Note, includes ModuleManager.) 14 | 15 | 16 | ## It's not done yet! 17 | 18 | I'm still working on adding features and content. Please see the wiki to stay up to date! 19 | -------------------------------------------------------------------------------- /src/ModuleScienceDataIndicator.cs: -------------------------------------------------------------------------------- 1 | namespace IndicatorLights 2 | { 3 | /// 4 | /// Indicates whether a science experiment contains data or not. Has the ability 5 | /// to show different colors depending on the value of the science data. 6 | /// 7 | class ModuleScienceDataIndicator : ModuleScienceDataIndicatorBase 8 | { 9 | /// 10 | /// Indicates the experiment which corresponds to this indicator (mainly useful 11 | /// for cases where one part may have multiple science experiments on it). 12 | /// Optional field. If null or empty, will simply use the first science experiment 13 | /// found on the part. 14 | /// 15 | [KSPField] 16 | public string experimentID = null; 17 | 18 | protected override bool IsSource(ModuleScienceExperiment candidate) 19 | { 20 | return string.IsNullOrEmpty(experimentID) || (experimentID.Equals(candidate.experimentID)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/IndicatorLights.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IndicatorLights", "IndicatorLights.csproj", "{8448DE0D-400E-4F55-815E-FDF996F6BC4B}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {8448DE0D-400E-4F55-815E-FDF996F6BC4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {8448DE0D-400E-4F55-815E-FDF996F6BC4B}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {8448DE0D-400E-4F55-815E-FDF996F6BC4B}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {8448DE0D-400E-4F55-815E-FDF996F6BC4B}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /files/Parts/cones/avionicsCone.cfg: -------------------------------------------------------------------------------- 1 | // Instrument the avionics nosecone to indicate the current SAS mode. 2 | 3 | @PART[avionicsNoseCone]:FOR[IndicatorLights] 4 | { 5 | @description ^= :(.)$:$0 Indicator light shows current SAS status.: 6 | 7 | //------------------------------------------------------------------------- 8 | // INDICATOR MESHES 9 | //------------------------------------------------------------------------- 10 | MODEL 11 | { 12 | model = IndicatorLights/Meshes/nubbinLamp 13 | scale = 1.5, 1.5, 2 14 | position = 0, 0.137, -0.227 15 | rotation = -90, 0, 0 16 | } 17 | 18 | //------------------------------------------------------------------------- 19 | // CONTROLLABLE EMISSIVES 20 | //------------------------------------------------------------------------- 21 | MODULE { 22 | name = ModuleControllableEmissive 23 | target = IndicatorLights/Meshes/nubbinLamp 24 | emissiveName = indicator 25 | } 26 | 27 | //------------------------------------------------------------------------- 28 | // CONTROLLERS 29 | //------------------------------------------------------------------------- 30 | MODULE { 31 | name = ModuleSasIndicator 32 | emissiveName = indicator 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/ExperimentalController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IndicatorLights 4 | { 5 | /// 6 | /// Used for designating controllers that are considered "experimental": i.e. subject to change 7 | /// or even deletion without notice, so nobody should be building code or config that depends 8 | /// on it if they care what happens to it. 9 | /// 10 | /// The purpose is to allow "pre-releasing" early rough drafts of new ideas, to give people 11 | /// a chance to play around with it and provide feedback, while making clear that it's 12 | /// subject to breaking changes without warning. 13 | /// 14 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 15 | public class ExperimentalController : Attribute 16 | { 17 | /// 18 | /// Gets whether the specified controller is of an experimental type. 19 | /// 20 | /// 21 | /// 22 | public static bool Is(ModuleEmissiveControllerBase controller) 23 | { 24 | return controller.GetType().IsDefined(typeof(ExperimentalController), true); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /files/Parts/fuelCells/FuelCellArrayIndicator.cfg: -------------------------------------------------------------------------------- 1 | // Adds an LED indicator to the fuel cell array. 2 | 3 | @PART[FuelCellArray]:FOR[IndicatorLights] { 4 | @description ^= :(.)$:$0 New, improved model now has status light!: 5 | 6 | //------------------------------------------------------------------------- 7 | // INDICATOR MESHES 8 | //------------------------------------------------------------------------- 9 | 10 | MODEL 11 | { 12 | model = IndicatorLights/Parts/fuelCells/arrayIndicatorModel 13 | position = -0.24708, -0.41497, 0.23743 14 | rotation = 0, 90, 0 15 | } 16 | 17 | 18 | //------------------------------------------------------------------------- 19 | // CONTROLLABLE EMISSIVES 20 | //------------------------------------------------------------------------- 21 | 22 | MODULE { 23 | name = ModuleControllableEmissive 24 | target = IndicatorLights/Parts/fuelCells/arrayIndicatorModel 25 | emissiveName = indicator 26 | } 27 | 28 | 29 | //------------------------------------------------------------------------- 30 | // CONTROLLERS 31 | //------------------------------------------------------------------------- 32 | 33 | MODULE { 34 | name = ModuleConverterIndicator 35 | converterName = Fuel Cell 36 | emissiveName = indicator 37 | } 38 | } -------------------------------------------------------------------------------- /files/Parts/fuelCells/FuelCellIndicator.cfg: -------------------------------------------------------------------------------- 1 | // Adds an LED indicator to the fuel cell. 2 | 3 | @PART[FuelCell]:FOR[IndicatorLights] { 4 | @description ^= :(.)$:$0 New, improved model now has status light!: 5 | 6 | //------------------------------------------------------------------------- 7 | // INDICATOR MESHES 8 | //------------------------------------------------------------------------- 9 | 10 | MODEL 11 | { 12 | model = IndicatorLights/Parts/fuelCells/rectangularIndicatorModel 13 | scale = 0.11077, 0.8, 0.5 14 | position = -0.19, 0, -0.07 15 | rotation = 0, 180, 0 16 | } 17 | 18 | 19 | //------------------------------------------------------------------------- 20 | // CONTROLLABLE EMISSIVES 21 | //------------------------------------------------------------------------- 22 | 23 | MODULE { 24 | name = ModuleControllableEmissive 25 | target = IndicatorLights/Parts/fuelCells/rectangularIndicatorModel 26 | emissiveName = indicator 27 | } 28 | 29 | 30 | //------------------------------------------------------------------------- 31 | // CONTROLLERS 32 | //------------------------------------------------------------------------- 33 | 34 | MODULE { 35 | name = ModuleConverterIndicator 36 | converterName = Fuel Cell 37 | emissiveName = indicator 38 | } 39 | } -------------------------------------------------------------------------------- /files/Parts/scanners/surveyScanner.cfg: -------------------------------------------------------------------------------- 1 | // Adds an LED indicator to the survey scanner unit. 2 | 3 | @PART[SurveyScanner]:FOR[IndicatorLights] { 4 | @description ^= :(.)$:$0 LED indicates operational status.: 5 | 6 | //------------------------------------------------------------------------- 7 | // INDICATOR MESHES 8 | //------------------------------------------------------------------------- 9 | 10 | MODEL 11 | { 12 | model = IndicatorLights/Meshes/squareLamp2 13 | scale = 0.7, 0.6, 2.83 14 | position = 0, 1.452, -0.0147 15 | } 16 | 17 | //------------------------------------------------------------------------- 18 | // CONTROLLABLE EMISSIVES 19 | //------------------------------------------------------------------------- 20 | 21 | MODULE { 22 | name = ModuleControllableEmissive 23 | target = IndicatorLights/Meshes/squareLamp2 24 | emissiveName = indicator 25 | } 26 | 27 | //------------------------------------------------------------------------- 28 | // CONTROLLERS 29 | //------------------------------------------------------------------------- 30 | 31 | MODULE { 32 | name = ModuleOrbitalSurveyorIndicator 33 | emissiveName = indicator 34 | alreadyScannedColor = dim($ToggleLED, 0.65) 35 | potentialScanColor = blink(dim($ToggleLED, 0.8), 150, $Off, 1050) 36 | readyScanColor = blink($ToggleLED, 200, $Off, 200) 37 | unusableColor = blink($Warning, 200, $Off, 200) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /files/Patches/IndicatorLights_KIS.cfg: -------------------------------------------------------------------------------- 1 | // This is needed to let KIS know that the various PartModules that 2 | // IndicatorLights provides shouldn't disqualify parts from being 3 | // stackable in KIS containers. 4 | // 5 | // Thanks to Enceos in the KSP forums for pointing out the need 6 | // for this patch. 7 | 8 | @KISConfig[*]:NEEDS[KIS] 9 | { 10 | @StackableModule 11 | { 12 | moduleName = ModuleBiomeScannerIndicator 13 | moduleName = ModuleBooleanIndicator 14 | moduleName = ModuleControllableEmissive 15 | moduleName = ModuleConverterIndicator 16 | moduleName = ModuleCrewIndicator 17 | moduleName = ModuleCrewIndicatorToggle 18 | moduleName = ModuleCustomBlink 19 | moduleName = ModuleCustomColoredEmissive 20 | moduleName = ModuleDataTransmitterIndicator 21 | moduleName = ModuleDockingCrossfeedIndicator 22 | moduleName = ModuleDockingStateIndicator 23 | moduleName = ModuleEmissiveArrayController 24 | moduleName = ModuleIndicatorToggle 25 | moduleName = ModuleOrbitalSurveyorIndicator 26 | moduleName = ModuleReactionWheelIndicator 27 | moduleName = ModuleResourceEnabledIndicator 28 | moduleName = ModuleResourceLevelIndicator 29 | moduleName = ModuleResourceScannerIndicator 30 | moduleName = ModuleScalarIndicator 31 | moduleName = ModuleScienceAvailabilityIndicator 32 | moduleName = ModuleScienceContainerIndicator 33 | moduleName = ModuleScienceDataIndicator 34 | moduleName = ModuleToggleLED 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Console/HelpCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace IndicatorLights.Console 4 | { 5 | /// 6 | /// Show all the various IndicatorLights console commands. 7 | /// 8 | internal class HelpCommand : DebugConsole.DebugConsoleCommand 9 | { 10 | public const string COMMAND = "help"; 11 | private const string HELP = "Shows this message"; 12 | 13 | public HelpCommand() : base(COMMAND, HELP) {} 14 | 15 | public override void Call(string[] arguments) 16 | { 17 | StringBuilder builder = new StringBuilder(); 18 | builder.AppendFormat("Commands available via /{0}:", DebugConsole.COMMAND); 19 | for (int i = 0; i < DebugConsole.COMMANDS.Length; ++i) 20 | { 21 | builder.Append("\n").Append(HelpStringOf(DebugConsole.COMMANDS[i])); 22 | } 23 | Logging.Log(builder.ToString()); 24 | } 25 | 26 | internal static string HelpStringOf(DebugConsole.DebugConsoleCommand command) 27 | { 28 | if (command.Usage == null) 29 | { 30 | return string.Format("/{0} {1}: {2}", DebugConsole.COMMAND, command.Command, command.Help); 31 | } 32 | else 33 | { 34 | return string.Format("/{0} {1} {2}: {3}", DebugConsole.COMMAND, command.Command, command.Usage, command.Help); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("IndicatorLights")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyProduct("IndicatorLights")] 11 | [assembly: AssemblyCopyright("Copyright © Snark 2016")] 12 | [assembly: AssemblyTrademark("")] 13 | [assembly: AssemblyCulture("")] 14 | 15 | // Setting ComVisible to false makes the types in this assembly not visible 16 | // to COM components. If you need to access a type in this assembly from 17 | // COM, set the ComVisible attribute to true on that type. 18 | [assembly: ComVisible(false)] 19 | 20 | // The following GUID is for the ID of the typelib if this project is exposed to COM 21 | [assembly: Guid("8448de0d-400e-4f55-815e-fdf996f6bc4b")] 22 | 23 | // Version information for an assembly consists of the following four values: 24 | // 25 | // Major Version 26 | // Minor Version 27 | // Build Number 28 | // Revision 29 | // 30 | // You can specify all the values or you can default the Build and Revision Numbers 31 | // by using the '*' as shown below: 32 | // [assembly: AssemblyVersion("1.0.*")] 33 | [assembly: AssemblyVersion("1.0.0.0")] 34 | [assembly: AssemblyFileVersion("1.0.0.0")] 35 | -------------------------------------------------------------------------------- /src/ModuleDockingCrossfeedIndicator.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace IndicatorLights 4 | { 5 | /// 6 | /// Picks a solid color, based on crossfeed enabled/disabled status. 7 | /// 8 | public class ModuleDockingCrossfeedIndicator : ModuleSourceIndicator, IToggle 9 | { 10 | [KSPField] 11 | [ColorSourceIDField] 12 | public string crossfeedOnSource = Colors.ToString(DefaultColor.DockingCrossfeedOn); 13 | 14 | [KSPField] 15 | [ColorSourceIDField] 16 | public string crossfeedOffSource = Colors.ToString(DefaultColor.DockingCrossfeedOff); 17 | 18 | private IColorSource onSource = null; 19 | private IColorSource offSource = null; 20 | 21 | public override void ParseIDs() 22 | { 23 | base.ParseIDs(); 24 | onSource = FindColorSource(crossfeedOnSource); 25 | offSource = FindColorSource(crossfeedOffSource); 26 | } 27 | 28 | public override Color OutputColor 29 | { 30 | get 31 | { 32 | if (SourceModule == null) return Color.black; 33 | return SourceModule.crossfeed ? onSource.OutputColor : offSource.OutputColor; 34 | } 35 | } 36 | 37 | /// 38 | /// IToggle implementation. 39 | /// 40 | public bool ToggleStatus 41 | { 42 | get 43 | { 44 | return (SourceModule == null)? false : SourceModule.crossfeed; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /files/Parts/dockingPorts/inflatableAirlock.cfg: -------------------------------------------------------------------------------- 1 | // Adds an indicator to the inflatable airlock. 2 | // When someone's in the airlock: it's a crew indicator. 3 | // When there's nobody in the airlock: it acts as a standard docking-port indicator. 4 | 5 | @PART[InflatableAirlock]:NEEDS[SquadExpansion]:FOR[IndicatorLights] { 6 | 7 | //------------------------------------------------------------------------- 8 | // INDICATOR MESHES 9 | //------------------------------------------------------------------------- 10 | MODEL 11 | { 12 | model = IndicatorLights/Meshes/squareLamp 13 | scale = 1, 0.25, 1 14 | position = 0, 0.165, -0.477 15 | rotation = 0, 180, 0 16 | } 17 | 18 | 19 | //------------------------------------------------------------------------- 20 | // CONTROLLABLE EMISSIVES 21 | //------------------------------------------------------------------------- 22 | MODULE { 23 | name = ModuleControllableEmissive 24 | target = IndicatorLights/Meshes/squareLamp 25 | emissiveName = indicator 26 | } 27 | 28 | 29 | //------------------------------------------------------------------------- 30 | // CONTROLLERS 31 | //------------------------------------------------------------------------- 32 | MODULE { 33 | name = ModuleDockingCrossfeedIndicator 34 | } 35 | 36 | MODULE { 37 | name = ModuleDockingStateIndicator 38 | readyColor = $Off 39 | acquireColor = blink(ModuleDockingCrossfeedIndicator, 100, $Off, 100) 40 | disengageColor = blink(ModuleDockingCrossfeedIndicator, 120, $Off, 1080) 41 | } 42 | 43 | MODULE { 44 | name = ModuleCrewIndicator 45 | emissiveName = indicator 46 | emptyColor = ModuleDockingStateIndicator 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /examples/VesselControlLevel.cfg: -------------------------------------------------------------------------------- 1 | // Demonstrate the use of controlLevel() syntax. 2 | // 3 | // This example adds a light to the HECS2 probe core, which lights up in 4 | // various vessel control levels (see comments below). 5 | 6 | @PART[HECS2_ProbeCore] { 7 | 8 | //------------------------------------------------------------------------- 9 | // INDICATOR MESHES 10 | //------------------------------------------------------------------------- 11 | 12 | MODEL 13 | { 14 | model = IndicatorLights/Meshes/nubbinLamp 15 | scale = 1.5, 1.5, 0.6 16 | position = 0, 0.02, -0.73 17 | rotation = 0, 180, 0 18 | } 19 | 20 | 21 | //------------------------------------------------------------------------- 22 | // CONTROLLABLE EMISSIVES 23 | //------------------------------------------------------------------------- 24 | 25 | // This emissive shows current control status of the vessel. 26 | MODULE { 27 | name = ModuleControllableEmissive 28 | target = IndicatorLights/Meshes/nubbinLamp 29 | emissiveName = controlLevelIndicator 30 | } 31 | 32 | 33 | //------------------------------------------------------------------------- 34 | // CONTROLLERS 35 | //------------------------------------------------------------------------- 36 | 37 | // This controls the control level indicator. Green when full control, yellow 38 | // when partial control (manned), red for partial control (unmanned), off for no control. 39 | MODULE { 40 | name = ModuleBooleanIndicator 41 | input = controlLevel(PARTIAL_UNMANNED, PARTIAL_MANNED, FULL) 42 | emissiveName = controlLevelIndicator 43 | activeColor = if(controlLevel(FULL), $ToggleLED, if(controlLevel(PARTIAL_MANNED), $MediumResource, $LowResource)) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /files/Parts/robotics/kal1000.cfg: -------------------------------------------------------------------------------- 1 | // Adds an LED indicator to the KAL-1000 robotic controller. 2 | 3 | @PART[controller1000]:NEEDS[SquadExpansion/Serenity]:FOR[IndicatorLights] { 4 | @description ^= :(.)$:$0 New, improved model now has status light!: 5 | 6 | // We have to re-specify the model for the stock part, because this part 7 | // uses the older "mesh =" syntax in its .cfg file instead of the newer 8 | // "MODEL" syntax. The "mesh =" syntax doesn't allow having multiple models 9 | // as part of the same part, which would prevent this mod from adding meshes 10 | // for the indicator lights. 11 | MODEL 12 | { 13 | model = SquadExpansion/Serenity/Parts/Robotics/Controllers/controller1000 14 | } 15 | 16 | //------------------------------------------------------------------------- 17 | // INDICATOR MESHES 18 | //------------------------------------------------------------------------- 19 | MODEL 20 | { 21 | model = IndicatorLights/Meshes/nubbinLamp 22 | scale = 0.65, 0.65, 1 23 | position = 0.093147, 0.034181, -0.13876 24 | rotation = 180, 0, 0 25 | } 26 | 27 | 28 | //------------------------------------------------------------------------- 29 | // CONTROLLABLE EMISSIVES 30 | //------------------------------------------------------------------------- 31 | 32 | MODULE { 33 | name = ModuleControllableEmissive 34 | target = IndicatorLights/Meshes/nubbinLamp 35 | emissiveName = indicator 36 | } 37 | 38 | //------------------------------------------------------------------------- 39 | // CONTROLLERS 40 | //------------------------------------------------------------------------- 41 | 42 | MODULE { 43 | name = ModuleRoboticControllerIndicator 44 | emissiveName = indicator 45 | } 46 | } -------------------------------------------------------------------------------- /files/Parts/science/scienceBox.cfg: -------------------------------------------------------------------------------- 1 | @PART[ScienceBox]:FOR[IndicatorLights] 2 | { 3 | 4 | //------------------------------------------------------------------------- 5 | // INDICATOR MESHES 6 | //------------------------------------------------------------------------- 7 | MODEL 8 | { 9 | model = IndicatorLights/Meshes/squareLamp 10 | scale = 0.3, 4, 0.5 11 | position = -0.2150, 0, -0.2150 12 | rotation = 0, 225, 0 13 | } 14 | 15 | MODEL 16 | { 17 | model = IndicatorLights/Meshes/squareLamp 18 | scale = 0.3, 4, 0.5 19 | position = 0.2150, 0, -0.2150 20 | rotation = 0, 135, 0 21 | } 22 | 23 | MODEL 24 | { 25 | model = IndicatorLights/Meshes/squareLamp 26 | scale = 0.3, 4, 0.5 27 | position = 0.2150, 0, 0.2150 28 | rotation = 0, 45, 0 29 | } 30 | 31 | MODEL 32 | { 33 | model = IndicatorLights/Meshes/squareLamp 34 | scale = 0.3, 4, 0.5 35 | position = -0.2150, 0, 0.2150 36 | rotation = 0, 315, 0 37 | } 38 | 39 | 40 | //------------------------------------------------------------------------- 41 | // CONTROLLABLE EMISSIVES 42 | //------------------------------------------------------------------------- 43 | MODULE { 44 | name = ModuleControllableEmissive 45 | target = IndicatorLights/Meshes/squareLamp 46 | emissiveName = indicator 47 | } 48 | 49 | 50 | //------------------------------------------------------------------------- 51 | // CONTROLLERS 52 | //------------------------------------------------------------------------- 53 | 54 | MODULE { 55 | name = ModuleScienceContainerIndicator 56 | emissiveName = indicator 57 | dataColor = $HighScience 58 | partialDataColor = $MediumScience 59 | lowDataColor = $LowScience 60 | emptyColor = $Off 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /files/Parts/science/gravioli.cfg: -------------------------------------------------------------------------------- 1 | @PART[sensorGravimeter]:FOR[IndicatorLights] 2 | { 3 | 4 | // We have to re-specify the model for the stock part, because this is 5 | // an older part that uses the "mesh =" syntax in its .cfg file instead 6 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 7 | // multiple models as part of the same part, which would prevent this mod 8 | // from adding meshes for the indicator lights. 9 | MODEL 10 | { 11 | model = Squad/Parts/Science/sensorGravimeter/model 12 | } 13 | 14 | 15 | //------------------------------------------------------------------------- 16 | // INDICATOR MESHES 17 | //------------------------------------------------------------------------- 18 | MODEL 19 | { 20 | model = IndicatorLights/Meshes/squareLamp2 21 | scale = 1.16, 0.6, 1.16 22 | position = 0, 0.1053, -0.0862 23 | } 24 | 25 | 26 | //------------------------------------------------------------------------- 27 | // CONTROLLABLE EMISSIVES 28 | //------------------------------------------------------------------------- 29 | MODULE { 30 | name = ModuleControllableEmissive 31 | target = IndicatorLights/Meshes/squareLamp2 32 | emissiveName = indicator 33 | } 34 | 35 | 36 | //------------------------------------------------------------------------- 37 | // CONTROLLERS 38 | //------------------------------------------------------------------------- 39 | 40 | MODULE { 41 | name = ModuleScienceDataIndicator 42 | emissiveName = indicator 43 | dataColor = $HighScience 44 | partialDataColor = $MediumScience 45 | lowDataColor = $LowScience 46 | emptyColor = ModuleScienceAvailabilityIndicator 47 | } 48 | 49 | MODULE { 50 | name = ModuleScienceAvailabilityIndicator 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/RateLimiter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IndicatorLights 4 | { 5 | /// 6 | /// Utility class for enabling performing an action no more frequently than 7 | /// a specified time interval. (For example, if updating on every Unity frame 8 | /// would be too expensive, so you want to do it no more often than every 9 | /// N milliseconds.) 10 | /// 11 | internal class RateLimiter 12 | { 13 | private readonly TimeSpan interval; 14 | private DateTime nextUpdate; 15 | 16 | /// 17 | /// Constructor. 18 | /// 19 | /// 20 | public RateLimiter(TimeSpan interval) 21 | { 22 | this.interval = interval; 23 | this.nextUpdate = DateTime.MinValue; 24 | } 25 | 26 | /// 27 | /// Resets the limiter, i.e. forces the next call to Check() to return true. 28 | /// 29 | public void Reset() 30 | { 31 | nextUpdate = DateTime.MinValue; 32 | } 33 | 34 | /// 35 | /// Returns true if it's never been called before, or if the time since it 36 | /// last returned true is at least the specified interval. Otherwise, returns false. 37 | /// 38 | /// 39 | public bool Check() 40 | { 41 | DateTime now = DateTime.Now; 42 | if (now > nextUpdate) 43 | { 44 | nextUpdate = now + interval; 45 | return true; 46 | } 47 | else 48 | { 49 | return false; 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /files/Parts/crewable/mk1cockpit.cfg: -------------------------------------------------------------------------------- 1 | // Adds an occupancy indicator to the Mk1 cockpit. 2 | 3 | @PART[Mark1Cockpit]:FOR[IndicatorLights] { 4 | 5 | //------------------------------------------------------------------------- 6 | // INDICATOR MESHES 7 | //------------------------------------------------------------------------- 8 | 9 | MODEL 10 | { 11 | model = IndicatorLights/Meshes/squareLamp 12 | scale = 1, 0.25, 0.5 13 | position = -0.583, 0.035, 0.2126 14 | rotation = 0, -70, 0 15 | } 16 | 17 | //------------------------------------------------------------------------- 18 | // CONTROLLABLE EMISSIVES 19 | //------------------------------------------------------------------------- 20 | 21 | MODULE { 22 | name = ModuleControllableEmissive 23 | target = IndicatorLights/Meshes/squareLamp 24 | emissiveName = indicator 25 | } 26 | 27 | //------------------------------------------------------------------------- 28 | // CONTROLLERS 29 | //------------------------------------------------------------------------- 30 | 31 | MODULE { 32 | name = ModuleCrewIndicatorToggle 33 | toggleName = indicatorToggle 34 | } 35 | 36 | MODULE { 37 | name = ModuleCrewIndicator 38 | toggleName = indicatorToggle 39 | } 40 | 41 | MODULE 42 | { 43 | name = ModuleScienceAvailabilityIndicator 44 | experimentID = crewReport 45 | lowValueColor = ModuleCrewIndicator 46 | mediumValueColor = highValueColor 47 | highValueColor = blink(lowValueColor, 200, $Off, 200) 48 | } 49 | 50 | MODULE { 51 | name = ModuleScienceDataIndicator 52 | experimentID = crewReport 53 | emissiveName = indicator 54 | dataColor = ModuleCrewIndicator 55 | emptyColor = ModuleScienceAvailabilityIndicator 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /files/Parts/science/barometer.cfg: -------------------------------------------------------------------------------- 1 | @PART[sensorBarometer]:FOR[IndicatorLights] 2 | { 3 | 4 | // We have to re-specify the model for the stock part, because this is 5 | // an older part that uses the "mesh =" syntax in its .cfg file instead 6 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 7 | // multiple models as part of the same part, which would prevent this mod 8 | // from adding meshes for the indicator lights. 9 | MODEL 10 | { 11 | model = Squad/Parts/Science/sensorBarometer/model 12 | } 13 | 14 | 15 | //------------------------------------------------------------------------- 16 | // INDICATOR MESHES 17 | //------------------------------------------------------------------------- 18 | MODEL 19 | { 20 | model = IndicatorLights/Meshes/nubbinLamp 21 | scale = 0.5, 0.5, 0.5 22 | position = 0.02, 0, -0.0427 23 | rotation = 0, 0, 0 24 | } 25 | 26 | 27 | //------------------------------------------------------------------------- 28 | // CONTROLLABLE EMISSIVES 29 | //------------------------------------------------------------------------- 30 | MODULE { 31 | name = ModuleControllableEmissive 32 | target = IndicatorLights/Meshes/nubbinLamp 33 | emissiveName = indicator 34 | } 35 | 36 | 37 | //------------------------------------------------------------------------- 38 | // CONTROLLERS 39 | //------------------------------------------------------------------------- 40 | 41 | MODULE { 42 | name = ModuleScienceDataIndicator 43 | emissiveName = indicator 44 | dataColor = $HighScience 45 | partialDataColor = $MediumScience 46 | lowDataColor = $LowScience 47 | emptyColor = ModuleScienceAvailabilityIndicator 48 | } 49 | 50 | MODULE { 51 | name = ModuleScienceAvailabilityIndicator 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /files/Parts/science/magnetometer.cfg: -------------------------------------------------------------------------------- 1 | @PART[Magnetometer]:FOR[IndicatorLights] 2 | { 3 | 4 | // We have to re-specify the model for the stock part, because this part 5 | // inexplicably uses the older "mesh =" syntax in its .cfg file instead 6 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 7 | // multiple models as part of the same part, which would prevent this mod 8 | // from adding meshes for the indicator lights. 9 | MODEL 10 | { 11 | model = Squad/Parts/Science/Magnetometer/Magnetometer 12 | } 13 | 14 | 15 | //------------------------------------------------------------------------- 16 | // INDICATOR MESHES 17 | //------------------------------------------------------------------------- 18 | MODEL 19 | { 20 | model = IndicatorLights/Meshes/nubbinLamp 21 | scale = 4.6, 4.6, 3 22 | position = 0, 0.21325, 0 23 | rotation = 90, 0, 0 24 | } 25 | 26 | 27 | //------------------------------------------------------------------------- 28 | // CONTROLLABLE EMISSIVES 29 | //------------------------------------------------------------------------- 30 | MODULE { 31 | name = ModuleControllableEmissive 32 | target = IndicatorLights/Meshes/nubbinLamp 33 | emissiveName = indicator 34 | } 35 | 36 | 37 | //------------------------------------------------------------------------- 38 | // CONTROLLERS 39 | //------------------------------------------------------------------------- 40 | 41 | MODULE { 42 | name = ModuleScienceDataIndicator 43 | emissiveName = indicator 44 | dataColor = $HighScience 45 | partialDataColor = $MediumScience 46 | lowDataColor = $LowScience 47 | emptyColor = ModuleScienceAvailabilityIndicator 48 | } 49 | 50 | MODULE { 51 | name = ModuleScienceAvailabilityIndicator 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /files/Parts/crewable/mk1cockpitInline.cfg: -------------------------------------------------------------------------------- 1 | // Adds an occupancy indicator to the Mk1 inline cockpit. 2 | 3 | @PART[Mark2Cockpit]:FOR[IndicatorLights] { 4 | 5 | //------------------------------------------------------------------------- 6 | // INDICATOR MESHES 7 | //------------------------------------------------------------------------- 8 | 9 | MODEL 10 | { 11 | model = IndicatorLights/Meshes/squareLamp 12 | scale = 0.25, 1.1, 0.5 13 | position = -0.419, 0.1, -0.501 14 | rotation = 0, -125, 0 15 | } 16 | 17 | //------------------------------------------------------------------------- 18 | // CONTROLLABLE EMISSIVES 19 | //------------------------------------------------------------------------- 20 | 21 | MODULE { 22 | name = ModuleControllableEmissive 23 | target = IndicatorLights/Meshes/squareLamp 24 | emissiveName = indicator 25 | } 26 | 27 | //------------------------------------------------------------------------- 28 | // CONTROLLERS 29 | //------------------------------------------------------------------------- 30 | 31 | MODULE { 32 | name = ModuleCrewIndicatorToggle 33 | toggleName = indicatorToggle 34 | } 35 | 36 | MODULE { 37 | name = ModuleCrewIndicator 38 | toggleName = indicatorToggle 39 | } 40 | 41 | MODULE 42 | { 43 | name = ModuleScienceAvailabilityIndicator 44 | experimentID = crewReport 45 | lowValueColor = ModuleCrewIndicator 46 | mediumValueColor = highValueColor 47 | highValueColor = blink(lowValueColor, 200, $Off, 200) 48 | } 49 | 50 | MODULE { 51 | name = ModuleScienceDataIndicator 52 | experimentID = crewReport 53 | emissiveName = indicator 54 | dataColor = ModuleCrewIndicator 55 | emptyColor = ModuleScienceAvailabilityIndicator 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /files/Parts/science/variometer.cfg: -------------------------------------------------------------------------------- 1 | @PART[sensorAtmosphere]:FOR[IndicatorLights] 2 | { 3 | 4 | // We have to re-specify the model for the stock part, because this is 5 | // an older part that uses the "mesh =" syntax in its .cfg file instead 6 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 7 | // multiple models as part of the same part, which would prevent this mod 8 | // from adding meshes for the indicator lights. 9 | MODEL 10 | { 11 | model = Squad/Parts/Science/AtmosphereSensor/model 12 | } 13 | 14 | 15 | //------------------------------------------------------------------------- 16 | // INDICATOR MESHES 17 | //------------------------------------------------------------------------- 18 | MODEL 19 | { 20 | model = IndicatorLights/Meshes/squareLamp 21 | scale = 0.3, 2, 0.3 22 | position = 0, 0.11416, -0.30133 23 | rotation = 0, 180, 0 24 | } 25 | 26 | 27 | //------------------------------------------------------------------------- 28 | // CONTROLLABLE EMISSIVES 29 | //------------------------------------------------------------------------- 30 | MODULE { 31 | name = ModuleControllableEmissive 32 | target = IndicatorLights/Meshes/squareLamp 33 | emissiveName = indicator 34 | } 35 | 36 | 37 | //------------------------------------------------------------------------- 38 | // CONTROLLERS 39 | //------------------------------------------------------------------------- 40 | 41 | MODULE { 42 | name = ModuleScienceDataIndicator 43 | emissiveName = indicator 44 | dataColor = $HighScience 45 | partialDataColor = $MediumScience 46 | lowDataColor = $LowScience 47 | emptyColor = ModuleScienceAvailabilityIndicator 48 | } 49 | 50 | MODULE { 51 | name = ModuleScienceAvailabilityIndicator 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /files/Parts/science/thermometer.cfg: -------------------------------------------------------------------------------- 1 | @PART[sensorThermometer]:FOR[IndicatorLights] 2 | { 3 | 4 | // We have to re-specify the model for the stock part, because this is 5 | // an older part that uses the "mesh =" syntax in its .cfg file instead 6 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 7 | // multiple models as part of the same part, which would prevent this mod 8 | // from adding meshes for the indicator lights. 9 | MODEL 10 | { 11 | model = Squad/Parts/Science/sensorThermometer/model 12 | } 13 | 14 | 15 | //------------------------------------------------------------------------- 16 | // INDICATOR MESHES 17 | //------------------------------------------------------------------------- 18 | MODEL 19 | { 20 | model = IndicatorLights/Meshes/squareLamp 21 | scale = 0.6, 0.3, 0.5 22 | position = 0, 0.11614, -0.05016 23 | rotation = 0, 180, 0 24 | } 25 | 26 | 27 | //------------------------------------------------------------------------- 28 | // CONTROLLABLE EMISSIVES 29 | //------------------------------------------------------------------------- 30 | MODULE { 31 | name = ModuleControllableEmissive 32 | target = IndicatorLights/Meshes/squareLamp 33 | emissiveName = indicator 34 | } 35 | 36 | 37 | //------------------------------------------------------------------------- 38 | // CONTROLLERS 39 | //------------------------------------------------------------------------- 40 | 41 | MODULE { 42 | name = ModuleScienceDataIndicator 43 | emissiveName = indicator 44 | dataColor = $HighScience 45 | partialDataColor = $MediumScience 46 | lowDataColor = $LowScience 47 | emptyColor = ModuleScienceAvailabilityIndicator 48 | } 49 | 50 | MODULE { 51 | name = ModuleScienceAvailabilityIndicator 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /files/Parts/science/accelerometer.cfg: -------------------------------------------------------------------------------- 1 | @PART[sensorAccelerometer]:FOR[IndicatorLights] 2 | { 3 | 4 | // We have to re-specify the model for the stock part, because this is 5 | // an older part that uses the "mesh =" syntax in its .cfg file instead 6 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 7 | // multiple models as part of the same part, which would prevent this mod 8 | // from adding meshes for the indicator lights. 9 | MODEL 10 | { 11 | model = Squad/Parts/Science/sensorAccelerometer/model 12 | } 13 | 14 | 15 | //------------------------------------------------------------------------- 16 | // INDICATOR MESHES 17 | //------------------------------------------------------------------------- 18 | MODEL 19 | { 20 | model = IndicatorLights/Meshes/nubbinLamp 21 | scale = 1, 1, 0.3 22 | position = 0, 0.11417, -0.08097 23 | rotation = -90, 0, 0 24 | } 25 | 26 | 27 | //------------------------------------------------------------------------- 28 | // CONTROLLABLE EMISSIVES 29 | //------------------------------------------------------------------------- 30 | MODULE { 31 | name = ModuleControllableEmissive 32 | target = IndicatorLights/Meshes/nubbinLamp 33 | emissiveName = indicator 34 | } 35 | 36 | 37 | //------------------------------------------------------------------------- 38 | // CONTROLLERS 39 | //------------------------------------------------------------------------- 40 | 41 | MODULE { 42 | name = ModuleScienceDataIndicator 43 | emissiveName = indicator 44 | dataColor = $HighScience 45 | partialDataColor = $MediumScience 46 | lowDataColor = $LowScience 47 | emptyColor = ModuleScienceAvailabilityIndicator 48 | } 49 | 50 | MODULE { 51 | name = ModuleScienceAvailabilityIndicator 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /files/Parts/batteries/Z400Indicator.cfg: -------------------------------------------------------------------------------- 1 | // Adds LED indicators to the 400 EC battery. 2 | 3 | @PART[ksp_r_largeBatteryPack]:FOR[IndicatorLights] { 4 | @description ^= :(.)$:$0 New, improved model now has status light!: 5 | 6 | // We have to re-specify the model for the stock part, because this is 7 | // an older part that uses the "mesh =" syntax in its .cfg file instead 8 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 9 | // multiple models as part of the same part, which would prevent this mod 10 | // from adding meshes for the indicator lights. 11 | MODEL 12 | { 13 | model = Squad/Parts/Electrical/z-400Battery/model 14 | } 15 | 16 | //------------------------------------------------------------------------- 17 | // INDICATOR MESHES 18 | //------------------------------------------------------------------------- 19 | 20 | MODEL 21 | { 22 | model = IndicatorLights/Meshes/nubbinLamp 23 | position = 0, 0.292, -0.139 24 | rotation = -90, 0, 0 25 | } 26 | 27 | 28 | //------------------------------------------------------------------------- 29 | // CONTROLLABLE EMISSIVES 30 | //------------------------------------------------------------------------- 31 | 32 | MODULE { 33 | name = ModuleControllableEmissive 34 | target = IndicatorLights/Meshes/nubbinLamp 35 | emissiveName = indicator 36 | } 37 | 38 | 39 | //------------------------------------------------------------------------- 40 | // CONTROLLERS 41 | //------------------------------------------------------------------------- 42 | 43 | MODULE { 44 | name = ModuleResourceLevelIndicator 45 | } 46 | 47 | MODULE { 48 | name = ModuleResourceEnabledIndicator 49 | enabledColor = ModuleResourceLevelIndicator 50 | disabledColor = blink(ModuleResourceLevelIndicator, 900, $Off, 300) 51 | emissiveName = indicator 52 | } 53 | } -------------------------------------------------------------------------------- /examples/AttachableResourceIndicator.cfg: -------------------------------------------------------------------------------- 1 | // Sample config to have a part that shows the resource level of its *parent* part. 2 | // We'll use ModuleManager to clone the BL-01 standalone indicator light and give 3 | // it new behavior. 4 | // 5 | // If you're wondering "hey, this is cool, why is this just an example, rather than 6 | // being an actual part?", it's because there's still a sizable chunk of work left 7 | // to do to make this into a polished, shippable product. First, there would need to 8 | // be some sort of UI available to pick which resource to show. Second, a real standalone 9 | // part really ought to have a model of its own, rather than being an exact visual copy 10 | // of the BL-01 light. Someday I may make something like this as an actual part for 11 | // IndicatorLights that gets installed by default, but for now it's just a sample. 12 | // 13 | +PART[indicatorLightSmall] { 14 | @name = attachableResourceIndicator 15 | @title = Attachable Resource Indicator 16 | @description = This prototype indicator can be attached anywhere. When attached to a resource-containing part, it shows the level of the resource. (If the part has multiple resources, it picks the first one.) 17 | @tags = snark indicator resource sample example 18 | 19 | // get rid of all the IndicatorLights modules on the parent part 20 | -MODULE[ModuleToggleLED] {} 21 | -MODULE[ModuleCustomColoredEmissive] {} 22 | -MODULE[ModuleCustomColoredEmissive] {} 23 | -MODULE[ModuleCustomBlink] {} 24 | 25 | // now add new ones 26 | MODULE { 27 | name = ModuleResourceLevelIndicator 28 | searchStrategy = parent 29 | } 30 | 31 | MODULE { 32 | name = ModuleResourceEnabledIndicator 33 | searchStrategy = parent 34 | enabledColor = ModuleResourceLevelIndicator 35 | disabledColor = blink(ModuleResourceLevelIndicator, 900, $Off, 300) 36 | emissiveName = light 37 | } 38 | } -------------------------------------------------------------------------------- /files/Parts/batteries/Z100Indicator.cfg: -------------------------------------------------------------------------------- 1 | // Adds LED indicators to the 100 EC battery. 2 | 3 | @PART[batteryPack]:FOR[IndicatorLights] { 4 | @description ^= :(.)$:$0 New, improved model now has status light!: 5 | 6 | // We have to re-specify the model for the stock part, because this is 7 | // an older part that uses the "mesh =" syntax in its .cfg file instead 8 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 9 | // multiple models as part of the same part, which would prevent this mod 10 | // from adding meshes for the indicator lights. 11 | MODEL 12 | { 13 | model = Squad/Parts/Electrical/z-100Battery/model 14 | } 15 | 16 | //------------------------------------------------------------------------- 17 | // INDICATOR MESHES 18 | //------------------------------------------------------------------------- 19 | MODEL 20 | { 21 | model = IndicatorLights/Meshes/z100lamp 22 | scale = 1.02, 1, 1.02 23 | position = 0.04252, 0.208, -0.08216 // inverted X, moved Y up a bit 24 | } 25 | 26 | 27 | //------------------------------------------------------------------------- 28 | // CONTROLLABLE EMISSIVES 29 | //------------------------------------------------------------------------- 30 | 31 | MODULE { 32 | name = ModuleControllableEmissive 33 | target = IndicatorLights/Meshes/z100lamp 34 | emissiveName = indicator 35 | } 36 | 37 | 38 | //------------------------------------------------------------------------- 39 | // CONTROLLERS 40 | //------------------------------------------------------------------------- 41 | 42 | MODULE { 43 | name = ModuleResourceLevelIndicator 44 | } 45 | 46 | MODULE { 47 | name = ModuleResourceEnabledIndicator 48 | enabledColor = ModuleResourceLevelIndicator 49 | disabledColor = blink(ModuleResourceLevelIndicator, 900, $Off, 300) 50 | emissiveName = indicator 51 | } 52 | } -------------------------------------------------------------------------------- /src/VesselScienceTracker.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | 4 | namespace IndicatorLights 5 | { 6 | /// 7 | /// Utility class for tracking which science subjects are contained in vessels. 8 | /// 9 | [KSPAddon(KSPAddon.Startup.Flight, false)] 10 | public class VesselScienceTracker : VesselRegistrar 11 | { 12 | private static VesselScienceTracker instance = null; 13 | 14 | public void Start() 15 | { 16 | instance = this; 17 | } 18 | 19 | /// 20 | /// Gets the science contents information for the specified vessel, or null if not found. 21 | /// 22 | /// 23 | /// 24 | public static VesselScienceContents Get(Vessel vessel) 25 | { 26 | return (instance == null) ? null : instance[vessel]; 27 | } 28 | 29 | /// 30 | /// Here when a vessel is added. 31 | /// 32 | /// 33 | /// 34 | protected override VesselScienceContents OnAddVessel(Vessel vessel) 35 | { 36 | return new VesselScienceContents(vessel); 37 | } 38 | 39 | /// 40 | /// Here when a vessel is modified. 41 | /// 42 | /// 43 | /// 44 | /// 45 | protected override VesselScienceContents OnModifyVessel(Vessel vessel, VesselScienceContents data) 46 | { 47 | return new VesselScienceContents(vessel); 48 | } 49 | 50 | private void OnVesselSituationChange(GameEvents.HostedFromToAction data) 51 | { 52 | throw new NotImplementedException(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /files/Parts/science/telescope.cfg: -------------------------------------------------------------------------------- 1 | @PART[InfraredTelescope]:FOR[IndicatorLights] 2 | { 3 | //------------------------------------------------------------------------- 4 | // INDICATOR MESHES 5 | //------------------------------------------------------------------------- 6 | 7 | // ring around the lens, indicates active/inactive 8 | MODEL 9 | { 10 | model = IndicatorLights/Meshes/nubbinLamp 11 | scale = 7, 7, 3 12 | position = 0, 0.96, -0.725 13 | rotation = 0, 180, 0 14 | } 15 | 16 | // small side lamp, shows science availability 17 | MODEL 18 | { 19 | model = IndicatorLights/Meshes/nubbinLamp 20 | scale = 1.5, 1.5, 1 21 | position = -.309, 0.804, -0.715 22 | rotation = 0, 180, 0 23 | } 24 | 25 | 26 | //------------------------------------------------------------------------- 27 | // CONTROLLABLE EMISSIVES 28 | //------------------------------------------------------------------------- 29 | MODULE { 30 | name = ModuleControllableEmissive 31 | target = IndicatorLights/Meshes/nubbinLamp:0 32 | emissiveName = activity 33 | } 34 | 35 | MODULE { 36 | name = ModuleControllableEmissive 37 | target = IndicatorLights/Meshes/nubbinLamp:1 38 | emissiveName = science 39 | } 40 | 41 | 42 | //------------------------------------------------------------------------- 43 | // CONTROLLERS 44 | //------------------------------------------------------------------------- 45 | 46 | MODULE { 47 | name = ModuleBooleanIndicator 48 | emissiveName = activity 49 | input = isTracking@SentinelModule 50 | activeColor = pulsate(random(#FF3000, #FF6000, 5000, 0.7), 2370, 0.5) 51 | inactiveColor = $Off 52 | } 53 | 54 | MODULE { 55 | name = ModuleScienceDataIndicator 56 | emissiveName = science 57 | dataColor = $HighScience 58 | partialDataColor = $MediumScience 59 | lowDataColor = $LowScience 60 | emptyColor = ModuleScienceAvailabilityIndicator 61 | } 62 | 63 | MODULE { 64 | name = ModuleScienceAvailabilityIndicator 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Console/LightsEnabledCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IndicatorLights.Console 4 | { 5 | class LightsEnabledCommand : DebugConsole.DebugConsoleCommand 6 | { 7 | private const string COMMAND = "enabled"; 8 | private const string STATUS_ON = "on"; 9 | private const string STATUS_OFF = "off"; 10 | private const string HELP = "Global switch for all lights in the mod"; 11 | private const string USAGE = "{" + STATUS_ON + " | " + STATUS_OFF + "}"; 12 | 13 | public LightsEnabledCommand() : base(COMMAND, HELP, USAGE) {} 14 | 15 | 16 | public override void Call(string[] arguments) 17 | { 18 | if (arguments.Length != 1) 19 | { 20 | Logging.Log(HelpCommand.HelpStringOf(this)); 21 | return; 22 | } 23 | 24 | switch (arguments[0]) 25 | { 26 | case STATUS_ON: 27 | if (GlobalSettings.IsEnabled) 28 | { 29 | Logging.Log("IndicatorLights is already enabled."); 30 | } 31 | else 32 | { 33 | GlobalSettings.IsEnabled = true; 34 | Logging.Log("Enabled IndicatorLights."); 35 | } 36 | break; 37 | case STATUS_OFF: 38 | if (GlobalSettings.IsEnabled) 39 | { 40 | GlobalSettings.IsEnabled = false; 41 | Logging.Log("Disabled IndicatorLights."); 42 | } 43 | else 44 | { 45 | Logging.Log("IndicatorLights is already disabled."); 46 | } 47 | break; 48 | default: 49 | Logging.Log(HelpCommand.HelpStringOf(this)); 50 | break; 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /files/Parts/batteries/Z200Indicator.cfg: -------------------------------------------------------------------------------- 1 | // Adds LED indicators to the 200 EC battery. 2 | 3 | @PART[batteryBankMini]:FOR[IndicatorLights] { 4 | @description ^= :(.)$:$0 New, improved model now has status lights!: 5 | 6 | // We have to re-specify the model for the stock part, because this is 7 | // an older part that uses the "mesh =" syntax in its .cfg file instead 8 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 9 | // multiple models as part of the same part, which would prevent this mod 10 | // from adding meshes for the indicator lights. 11 | MODEL 12 | { 13 | model = Squad/Parts/Electrical/z-200Battery/model 14 | } 15 | 16 | //------------------------------------------------------------------------- 17 | // INDICATOR MESHES 18 | //------------------------------------------------------------------------- 19 | MODEL 20 | { 21 | model = IndicatorLights/Meshes/squareLamp 22 | scale = 0.4, 0.4, 1 23 | position = 0, 0, 0.3125 24 | rotation = 0, 0, 0 25 | } 26 | 27 | MODEL 28 | { 29 | model = IndicatorLights/Meshes/squareLamp 30 | scale = 0.4, 0.4, 1 31 | position = 0, 0, -0.3125 32 | rotation = 0, 180, 0 33 | } 34 | 35 | 36 | //------------------------------------------------------------------------- 37 | // CONTROLLABLE EMISSIVES 38 | //------------------------------------------------------------------------- 39 | 40 | MODULE { 41 | name = ModuleControllableEmissive 42 | target = IndicatorLights/Meshes/squareLamp 43 | emissiveName = indicator 44 | } 45 | 46 | 47 | //------------------------------------------------------------------------- 48 | // CONTROLLERS 49 | //------------------------------------------------------------------------- 50 | 51 | MODULE { 52 | name = ModuleResourceLevelIndicator 53 | } 54 | 55 | MODULE { 56 | name = ModuleResourceEnabledIndicator 57 | enabledColor = ModuleResourceLevelIndicator 58 | disabledColor = blink(ModuleResourceLevelIndicator, 900, $Off, 300) 59 | emissiveName = indicator 60 | } 61 | } -------------------------------------------------------------------------------- /src/ScienceValue.cs: -------------------------------------------------------------------------------- 1 | namespace IndicatorLights 2 | { 3 | /// 4 | /// Classifies science value as low/medium/high. 5 | /// 6 | static class ScienceValue 7 | { 8 | public enum Fraction 9 | { 10 | Low, 11 | Medium, 12 | High 13 | } 14 | 15 | public const float DEFAULT_LOW_SCIENCE_THRESHOLD = 0.15f; 16 | public const float DEFAULT_HIGH_SCIENCE_THRESHOLD = 0.7f; 17 | 18 | /// 19 | /// Gets the subject with the specified ID as a fraction category. 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// 25 | public static Fraction Get(string subjectID, float lowThreshold, float highThreshold) 26 | { 27 | return FractionOf(Get(subjectID), lowThreshold, highThreshold); 28 | } 29 | 30 | /// 31 | /// Gets the subject with the specified ID as a floating-point fraction. 32 | /// 33 | /// 34 | /// 35 | public static float Get(string subjectID) 36 | { 37 | if (ResearchAndDevelopment.Instance == null) return 1; // it's a sandbox game 38 | ScienceSubject subject = ResearchAndDevelopment.GetSubjectByID(subjectID); 39 | // subject will be null if we've never retrieved this science result before 40 | return (subject == null) ? 1 : ResearchAndDevelopment.GetSubjectValue(subject.science, subject); 41 | } 42 | 43 | public static Fraction FractionOf(float fraction, float lowThreshold, float highThreshold) 44 | { 45 | if (fraction >= highThreshold) return Fraction.High; 46 | if (fraction < lowThreshold) return Fraction.Low; 47 | return Fraction.Medium; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /files/Parts/reactionWheels/smallReactionWheel.cfg: -------------------------------------------------------------------------------- 1 | // Adds status indicators to the small 0.625m reaction wheel. 2 | 3 | @PART[sasModule]:FOR[IndicatorLights] { 4 | @description ^= :(.)$:$0 Indicator lights show the reaction wheel's status.: 5 | 6 | // We have to re-specify the model for the stock part, because this is 7 | // an older part that uses the "mesh =" syntax in its .cfg file instead 8 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 9 | // multiple models as part of the same part, which would prevent this mod 10 | // from adding meshes for the indicator lights. 11 | MODEL 12 | { 13 | model = Squad/Parts/Command/inlineReactionWheel/model 14 | } 15 | 16 | 17 | //------------------------------------------------------------------------- 18 | // INDICATOR MESHES 19 | //------------------------------------------------------------------------- 20 | 21 | // The lights we add have to be double the scale & position of what we'd 22 | // normally want, because this part has a rescaleFactor of 0.5 applied to 23 | // it for legacy reasons. 24 | 25 | MODEL 26 | { 27 | model = IndicatorLights/Meshes/squareLamp 28 | scale = 0.8, 0.8, 1 29 | position = 0, 0, 0.625 30 | rotation = 0, 0, 0 31 | } 32 | 33 | MODEL 34 | { 35 | model = IndicatorLights/Meshes/squareLamp 36 | scale = 0.8, 0.8, 1 37 | position = 0, 0, -0.625 38 | rotation = 0, 180, 0 39 | } 40 | 41 | 42 | //------------------------------------------------------------------------- 43 | // CONTROLLABLE EMISSIVES 44 | //------------------------------------------------------------------------- 45 | 46 | MODULE { 47 | name = ModuleControllableEmissive 48 | target = IndicatorLights/Meshes/squareLamp 49 | emissiveName = indicator 50 | } 51 | 52 | 53 | //------------------------------------------------------------------------- 54 | // CONTROLLERS 55 | //------------------------------------------------------------------------- 56 | 57 | MODULE { 58 | name = ModuleReactionWheelIndicator 59 | emissiveName = indicator 60 | } 61 | } -------------------------------------------------------------------------------- /files/Parts/science/goo.cfg: -------------------------------------------------------------------------------- 1 | @PART[GooExperiment]:FOR[IndicatorLights] 2 | { 3 | 4 | // We have to re-specify the model for the stock part, because this is 5 | // an older part that uses the "mesh =" syntax in its .cfg file instead 6 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 7 | // multiple models as part of the same part, which would prevent this mod 8 | // from adding meshes for the indicator lights. 9 | MODEL 10 | { 11 | model = Squad/Parts/Science/GooExperiment/GooExperiment 12 | } 13 | 14 | 15 | //------------------------------------------------------------------------- 16 | // INDICATOR MESHES 17 | //------------------------------------------------------------------------- 18 | MODEL 19 | { 20 | model = IndicatorLights/Meshes/squareLamp 21 | scale = 0.5, 1.7, 0.5 22 | position = -0.2146, 0, -0.358 23 | rotation = 0, -90, 0 24 | } 25 | 26 | MODEL 27 | { 28 | model = IndicatorLights/Meshes/squareLamp 29 | scale = 0.5, 1.7, 0.5 30 | position = 0.2146, 0, -0.358 31 | rotation = 0, 90, 0 32 | } 33 | 34 | //------------------------------------------------------------------------- 35 | // CONTROLLABLE EMISSIVES 36 | //------------------------------------------------------------------------- 37 | MODULE { 38 | name = ModuleControllableEmissive 39 | target = IndicatorLights/Meshes/squareLamp 40 | emissiveName = indicator 41 | } 42 | 43 | 44 | //------------------------------------------------------------------------- 45 | // CONTROLLERS 46 | //------------------------------------------------------------------------- 47 | 48 | MODULE { 49 | name = ModuleScienceDataIndicator 50 | emissiveName = indicator 51 | dataColor = $HighScience 52 | partialDataColor = $MediumScience 53 | lowDataColor = $LowScience 54 | emptyColor = ModuleScienceAvailabilityIndicator 55 | lowScienceThreshold = 0.25 56 | highScienceThreshold = 0.8 57 | } 58 | 59 | MODULE { 60 | name = ModuleScienceAvailabilityIndicator 61 | lowScienceThreshold = 0.25 62 | highScienceThreshold = 0.8 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/ModuleEmissiveArrayController.cs: -------------------------------------------------------------------------------- 1 | namespace IndicatorLights 2 | { 3 | /// 4 | /// A simple array controller that sets all meshes to the same basic pattern. 5 | /// Allows for an optional master toggle switch for picking which pattern is 6 | /// shown. 7 | /// 8 | [ExperimentalController] 9 | class ModuleEmissiveArrayController : ModuleEmissiveControllerBase 10 | { 11 | [KSPField] 12 | [ToggleIDField] 13 | public string toggleID = string.Empty; 14 | 15 | [KSPField] 16 | public string activeColor = string.Empty; 17 | 18 | [KSPField] 19 | public string inactiveColor = string.Empty; 20 | 21 | private IToggle toggle = null; 22 | private ColorSourceArray activeSource = null; 23 | private ColorSourceArray inactiveSource = null; 24 | 25 | public override void OnStart(StartState state) 26 | { 27 | base.OnStart(state); 28 | toggle = Identifiers.FindFirst(part, toggleID); 29 | activeSource = ColorSourceArray.of(activeColor); 30 | if (toggle == null) 31 | { 32 | if (!string.IsNullOrEmpty(inactiveColor)) 33 | { 34 | Logging.Warn("Ignoring inactiveColor on " + GetType() + " of " + part.GetTitle() + " (no toggle was specified)"); 35 | } 36 | } 37 | else 38 | { 39 | inactiveSource = ColorSourceArray.of(inactiveColor); 40 | } 41 | } 42 | 43 | /// 44 | /// Called on every frame when it's time to set colors on the controllable emissives. 45 | /// 46 | protected override void SetColors() 47 | { 48 | if ((toggle == null) || toggle.ToggleStatus) 49 | { 50 | activeSource.SetColors(this); 51 | } 52 | else 53 | { 54 | inactiveSource.SetColors(this); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /files/Parts/scanners/surfaceScanner.cfg: -------------------------------------------------------------------------------- 1 | // Adds an LED indicator to the surface scanner unit. 2 | 3 | @PART[SurfaceScanner]:FOR[IndicatorLights] { 4 | @description ^= :(.)$:$0 LED indicates operational status.: 5 | 6 | // We have to re-specify the model for the stock part, because this is 7 | // an older part that uses the "mesh =" syntax in its .cfg file instead 8 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 9 | // multiple models as part of the same part, which would prevent this mod 10 | // from adding meshes for the indicator lights. 11 | MODEL 12 | { 13 | model = Squad/Parts/Resources/SurfaceScanner/SurfaceScanner 14 | } 15 | 16 | //------------------------------------------------------------------------- 17 | // INDICATOR MESHES 18 | //------------------------------------------------------------------------- 19 | 20 | MODEL 21 | { 22 | model = IndicatorLights/Meshes/squareLamp 23 | scale = 1, 0.5, 0.5 24 | position = 0, 0.047, -0.229 25 | rotation = 0, 180, 0 26 | } 27 | 28 | //------------------------------------------------------------------------- 29 | // CONTROLLABLE EMISSIVES 30 | //------------------------------------------------------------------------- 31 | 32 | MODULE { 33 | name = ModuleControllableEmissive 34 | target = IndicatorLights/Meshes/squareLamp 35 | emissiveName = indicator 36 | } 37 | 38 | //------------------------------------------------------------------------- 39 | // CONTROLLERS 40 | //------------------------------------------------------------------------- 41 | 42 | // Pick a color based on resource abundance, and whether we've unlocked 43 | // the current biome. 44 | MODULE { 45 | name = ModuleResourceScannerIndicator 46 | } 47 | 48 | // Then, if we're in a situation where we can do a biome scan, override 49 | // that with a "hey, you should do a scan now!" indication. 50 | MODULE { 51 | name = ModuleBiomeScannerIndicator 52 | emissiveName = indicator 53 | inactiveColor = ModuleResourceScannerIndicator 54 | readyColor = blink($ToggleLED, 200, $Off, 200) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Console/PartsCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | 4 | namespace IndicatorLights.Console 5 | { 6 | class PartsCommand : DebugConsole.DebugConsoleCommand 7 | { 8 | private const string COMMAND = "parts"; 9 | private const string HELP = "List all IndicatorLights parts on the current vessel"; 10 | 11 | public PartsCommand() : base(COMMAND, HELP) {} 12 | 13 | public override void Call(string[] arguments) 14 | { 15 | List parts = GetIndicatorLightsParts(this); 16 | if (parts.Count == 0) 17 | { 18 | Logging.Log("No IndicatorLights parts are present on this vessel."); 19 | return; 20 | } 21 | StringBuilder builder = new StringBuilder(); 22 | builder.AppendFormat("{0} IndicatorLights parts found:", parts.Count); 23 | for (int i = 0; i < parts.Count; ++i) 24 | { 25 | builder.AppendFormat("\n{0}: {1}", i, Logging.GetTitle(parts[i])); 26 | } 27 | Logging.Log(builder.ToString()); 28 | } 29 | 30 | internal static List GetIndicatorLightsParts(DebugConsole.DebugConsoleCommand command) 31 | { 32 | List allParts = command.GetVesselParts(); 33 | List filteredParts = new List(); 34 | for (int i = 0; i < allParts.Count; ++i) 35 | { 36 | Part part = allParts[i]; 37 | if (IsIndicatorLightsPart(part)) 38 | { 39 | filteredParts.Add(part); 40 | } 41 | } 42 | return filteredParts; 43 | } 44 | 45 | internal static bool IsIndicatorLightsPart(Part part) 46 | { 47 | for (int i = 0; i < part.Modules.Count; ++i) 48 | { 49 | PartModule module = part.Modules[i]; 50 | if (module is ModuleControllableEmissive) return true; 51 | if (module is ModuleEmissiveController) return true; 52 | } 53 | return false; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /files/Parts/reactionWheels/largeReactionWheel.cfg: -------------------------------------------------------------------------------- 1 | // Adds status indicators to the large 2.5m reaction wheel. 2 | 3 | @PART[asasmodule1-2]:FOR[IndicatorLights] { 4 | @description ^= :(.)$:$0 Indicator lights show the reaction wheel's status.: 5 | 6 | // We have to re-specify the model for the stock part, because this is 7 | // an older part that uses the "mesh =" syntax in its .cfg file instead 8 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 9 | // multiple models as part of the same part, which would prevent this mod 10 | // from adding meshes for the indicator lights. 11 | MODEL 12 | { 13 | model = Squad/Parts/Command/advancedSasModuleLarge/model 14 | } 15 | 16 | 17 | //------------------------------------------------------------------------- 18 | // INDICATOR MESHES 19 | //------------------------------------------------------------------------- 20 | 21 | MODEL 22 | { 23 | model = IndicatorLights/Meshes/squareLamp 24 | scale = 0.8, 0.8, 1 25 | position = -0.884, 0, 0.884 26 | rotation = 0, -45, 0 27 | } 28 | 29 | MODEL 30 | { 31 | model = IndicatorLights/Meshes/squareLamp 32 | scale = 0.8, 0.8, 1 33 | position = 0.884, 0, -0.884 34 | rotation = 0, 135, 0 35 | } 36 | 37 | MODEL 38 | { 39 | model = IndicatorLights/Meshes/squareLamp 40 | scale = 0.8, 0.8, 1 41 | position = 0.884, 0, 0.884 42 | rotation = 0, 45, 0 43 | } 44 | 45 | MODEL 46 | { 47 | model = IndicatorLights/Meshes/squareLamp 48 | scale = 0.8, 0.8, 1 49 | position = -0.884, 0, -0.884 50 | rotation = 0, 225, 0 51 | } 52 | 53 | 54 | //------------------------------------------------------------------------- 55 | // CONTROLLABLE EMISSIVES 56 | //------------------------------------------------------------------------- 57 | 58 | MODULE { 59 | name = ModuleControllableEmissive 60 | target = IndicatorLights/Meshes/squareLamp 61 | emissiveName = indicator 62 | } 63 | 64 | 65 | //------------------------------------------------------------------------- 66 | // CONTROLLERS 67 | //------------------------------------------------------------------------- 68 | 69 | MODULE { 70 | name = ModuleReactionWheelIndicator 71 | emissiveName = indicator 72 | } 73 | } -------------------------------------------------------------------------------- /files/Parts/reactionWheels/mediumReactionWheel.cfg: -------------------------------------------------------------------------------- 1 | // Adds status indicators to the medium 1.25m reaction wheel. 2 | 3 | @PART[advSasModule]:FOR[IndicatorLights] { 4 | @description ^= :(.)$:$0 Indicator lights show the reaction wheel's status.: 5 | 6 | // We have to re-specify the model for the stock part, because this is 7 | // an older part that uses the "mesh =" syntax in its .cfg file instead 8 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 9 | // multiple models as part of the same part, which would prevent this mod 10 | // from adding meshes for the indicator lights. 11 | MODEL 12 | { 13 | model = Squad/Parts/Command/inlineAdvancedStabilizer/model 14 | } 15 | 16 | 17 | //------------------------------------------------------------------------- 18 | // INDICATOR MESHES 19 | //------------------------------------------------------------------------- 20 | 21 | MODEL 22 | { 23 | model = IndicatorLights/Meshes/squareLamp 24 | scale = 0.5, 0.5, 1 25 | position = -0.442, 0, 0.442 26 | rotation = 0, -45, 0 27 | } 28 | 29 | MODEL 30 | { 31 | model = IndicatorLights/Meshes/squareLamp 32 | scale = 0.5, 0.5, 1 33 | position = 0.442, 0, -0.442 34 | rotation = 0, 135, 0 35 | } 36 | 37 | MODEL 38 | { 39 | model = IndicatorLights/Meshes/squareLamp 40 | scale = 0.5, 0.5, 1 41 | position = 0.442, 0, 0.442 42 | rotation = 0, 45, 0 43 | } 44 | 45 | MODEL 46 | { 47 | model = IndicatorLights/Meshes/squareLamp 48 | scale = 0.5, 0.5, 1 49 | position = -0.442, 0, -0.442 50 | rotation = 0, 215, 0 51 | } 52 | 53 | 54 | //------------------------------------------------------------------------- 55 | // CONTROLLABLE EMISSIVES 56 | //------------------------------------------------------------------------- 57 | 58 | MODULE { 59 | name = ModuleControllableEmissive 60 | target = IndicatorLights/Meshes/squareLamp 61 | emissiveName = indicator 62 | } 63 | 64 | 65 | //------------------------------------------------------------------------- 66 | // CONTROLLERS 67 | //------------------------------------------------------------------------- 68 | 69 | MODULE { 70 | name = ModuleReactionWheelIndicator 71 | emissiveName = indicator 72 | } 73 | } -------------------------------------------------------------------------------- /src/ModuleResourceEnabledIndicator.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace IndicatorLights 4 | { 5 | /// 6 | /// A controller that sets the display based on whether a resource is enabled/disabled. 7 | /// 8 | class ModuleResourceEnabledIndicator : ModuleResourceIndicator, IToggle 9 | { 10 | private IColorSource enabledSource = null; 11 | private IColorSource disabledSource = null; 12 | 13 | /// 14 | /// The color to display when the resource is enabled. 15 | /// 16 | [KSPField] 17 | [ColorSourceIDField] 18 | public string enabledColor = Colors.ToString(DefaultColor.ToggleLED.Value()); 19 | 20 | /// 21 | /// The color to display when the resource is disabled. 22 | /// 23 | [KSPField] 24 | [ColorSourceIDField] 25 | public string disabledColor = ColorSources.Blink( 26 | ColorSources.Constant(DefaultColor.ToggleLED), 27 | 900, 28 | ColorSources.Constant(DefaultColor.Off), 29 | 300).ColorSourceID; 30 | 31 | public override void ParseIDs() 32 | { 33 | base.ParseIDs(); 34 | enabledSource = FindColorSource(enabledColor); 35 | disabledSource = FindColorSource(disabledColor); 36 | } 37 | 38 | public override bool HasColor 39 | { 40 | get 41 | { 42 | return base.HasColor && CurrentSource.HasColor; 43 | } 44 | } 45 | 46 | public override Color OutputColor 47 | { 48 | get { return CurrentSource.OutputColor; } 49 | } 50 | 51 | private IColorSource CurrentSource 52 | { 53 | get 54 | { 55 | return ToggleStatus ? enabledSource : disabledSource; 56 | } 57 | } 58 | 59 | /// 60 | /// IToggle implementation. 61 | /// 62 | public bool ToggleStatus 63 | { 64 | get 65 | { 66 | PartResource resource = Resource; 67 | return (resource != null) && resource.flowState; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/ModuleSourceIndicator.cs: -------------------------------------------------------------------------------- 1 | namespace IndicatorLights 2 | { 3 | /// 4 | /// Base class for modules indicator controllers that depend on some other 5 | /// PartModule (e.g. a stock one) for their color. 6 | /// 7 | public abstract class ModuleSourceIndicator : ModuleEmissiveController where T : PartModule 8 | { 9 | private T sourceModule = null; 10 | 11 | /// 12 | /// Called when the module is starting up. 13 | /// 14 | /// 15 | public override void OnStart(StartState state) 16 | { 17 | base.OnStart(state); 18 | 19 | sourceModule = ChooseSource(); 20 | if (sourceModule == null) 21 | { 22 | Logging.Warn("No " + typeof(T).Name + " found for " + part.GetTitle()); 23 | } 24 | } 25 | 26 | public override bool HasColor 27 | { 28 | get { return sourceModule != null; } 29 | } 30 | 31 | protected T SourceModule 32 | { 33 | get { return sourceModule; } 34 | } 35 | 36 | /// 37 | /// Choose the source module to use, or null if no suitable source module could be found. 38 | /// Subclasses can override to customize the choice. Default behavior is simply to pick 39 | /// the first one found. 40 | /// 41 | /// 42 | private T ChooseSource() 43 | { 44 | if (part == null) return null; 45 | for (int i = 0; i < part.Modules.Count; ++i) 46 | { 47 | T candidate = part.Modules[i] as T; 48 | if ((candidate != null) && IsSource(candidate)) return candidate; 49 | } 50 | return null; 51 | } 52 | 53 | /// 54 | /// Determine whether the specified module is usable as a source. Default implementation 55 | /// is to always return true, so the first one found will be chosen. 56 | /// 57 | /// 58 | /// 59 | protected virtual bool IsSource(T candidate) 60 | { 61 | return true; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /files/Parts/batteries/Z1KIndicator.cfg: -------------------------------------------------------------------------------- 1 | // Adds LED indicators to the 1000 EC battery. 2 | 3 | @PART[batteryBank]:FOR[IndicatorLights] { 4 | @description ^= :(.)$:$0 New, improved model now has status lights!: 5 | 6 | // We have to re-specify the model for the stock part, because this is 7 | // an older part that uses the "mesh =" syntax in its .cfg file instead 8 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 9 | // multiple models as part of the same part, which would prevent this mod 10 | // from adding meshes for the indicator lights. 11 | MODEL 12 | { 13 | model = Squad/Parts/Electrical/z-1kBattery/model 14 | } 15 | 16 | //------------------------------------------------------------------------- 17 | // INDICATOR MESHES 18 | //------------------------------------------------------------------------- 19 | 20 | MODEL 21 | { 22 | model = IndicatorLights/Meshes/squareLamp 23 | scale = 0.5, 0.5, 1 24 | position = -0.442, 0, 0.442 25 | rotation = 0, -45, 0 26 | } 27 | 28 | MODEL 29 | { 30 | model = IndicatorLights/Meshes/squareLamp 31 | scale = 0.5, 0.5, 1 32 | position = 0.442, 0, -0.442 33 | rotation = 0, 135, 0 34 | } 35 | 36 | MODEL 37 | { 38 | model = IndicatorLights/Meshes/squareLamp 39 | scale = 0.5, 0.5, 1 40 | position = 0.442, 0, 0.442 41 | rotation = 0, 45, 0 42 | } 43 | 44 | MODEL 45 | { 46 | model = IndicatorLights/Meshes/squareLamp 47 | scale = 0.5, 0.5, 1 48 | position = -0.442, 0, -0.442 49 | rotation = 0, 215, 0 50 | } 51 | 52 | 53 | //------------------------------------------------------------------------- 54 | // CONTROLLABLE EMISSIVES 55 | //------------------------------------------------------------------------- 56 | 57 | MODULE { 58 | name = ModuleControllableEmissive 59 | target = IndicatorLights/Meshes/squareLamp 60 | emissiveName = indicator 61 | } 62 | 63 | 64 | //------------------------------------------------------------------------- 65 | // CONTROLLERS 66 | //------------------------------------------------------------------------- 67 | 68 | MODULE { 69 | name = ModuleResourceLevelIndicator 70 | } 71 | 72 | MODULE { 73 | name = ModuleResourceEnabledIndicator 74 | enabledColor = ModuleResourceLevelIndicator 75 | disabledColor = blink(ModuleResourceLevelIndicator, 900, $Off, 300) 76 | emissiveName = indicator 77 | } 78 | } -------------------------------------------------------------------------------- /src/RenderType.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace IndicatorLights 4 | { 5 | /// 6 | /// These are the valid values for the renderType property on ModuleControllableEmissive. 7 | /// 8 | public enum RenderType 9 | { 10 | /// 11 | /// Controls the emissive color of the mesh. 12 | /// 13 | emissive, 14 | 15 | /// 16 | /// Controls the tint color of the mesh. 17 | /// 18 | tint, 19 | 20 | /// 21 | /// Controls the "main" color of the mesh (e.g. the diffuse color, for many shaders). 22 | /// 23 | main 24 | } 25 | 26 | /// 27 | /// Extension methods for RenderType. 28 | /// 29 | internal static class RenderTypes 30 | { 31 | /// 32 | /// The render type to use for everything, unless specified otherwise. 33 | /// 34 | public const RenderType Default = RenderType.emissive; 35 | 36 | private static readonly int EMISSIVE_COLOR_ID = Shader.PropertyToID("_EmissiveColor"); 37 | private static readonly int TINT_COLOR_ID = Shader.PropertyToID("_TintColor"); 38 | private static readonly int MAIN_COLOR_ID = Shader.PropertyToID("_Color"); 39 | 40 | /// 41 | /// Gets the Unity shader property ID for this render type. 42 | /// 43 | /// 44 | /// 45 | public static int GetShaderPropertyId(this RenderType renderType) 46 | { 47 | switch (renderType) 48 | { 49 | case RenderType.emissive: 50 | return EMISSIVE_COLOR_ID; 51 | 52 | case RenderType.tint: 53 | return TINT_COLOR_ID; 54 | 55 | case RenderType.main: 56 | return MAIN_COLOR_ID; 57 | 58 | default: 59 | // Should never happen-- this means there's a bug, i.e. a new RenderType was added 60 | // without updating here. 61 | Logging.Error("Unknown render type " + renderType + ", defaulting to " + Default); 62 | return EMISSIVE_COLOR_ID; 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/ModuleDockingStateIndicator.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace IndicatorLights 4 | { 5 | /// 6 | /// Controls color based on the docking state (docked, undocked, engaged, disengaging). 7 | /// 8 | class ModuleDockingStateIndicator : ModuleSourceIndicator, IToggle 9 | { 10 | private const string ACQUIRE = "Acquire"; 11 | private const string DISENGAGE = "Disengage"; 12 | 13 | [KSPField] 14 | [ColorSourceIDField] 15 | public string readyColor = string.Empty; 16 | 17 | [KSPField] 18 | [ColorSourceIDField] 19 | public string acquireColor = string.Empty; 20 | 21 | [KSPField] 22 | [ColorSourceIDField] 23 | public string disengageColor = string.Empty; 24 | 25 | private IColorSource ready = null; 26 | private IColorSource acquire = null; 27 | private IColorSource disengage = null; 28 | 29 | 30 | public override void ParseIDs() 31 | { 32 | base.ParseIDs(); 33 | ready = FindColorSource(readyColor); 34 | acquire = FindColorSource(acquireColor); 35 | disengage = FindColorSource(disengageColor); 36 | } 37 | 38 | public override Color OutputColor 39 | { 40 | get 41 | { 42 | if (SourceModule == null) return ready.OutputColor; 43 | string state = SourceModule.state; 44 | if (string.IsNullOrEmpty(state)) return ready.OutputColor; 45 | if (state.StartsWith(ACQUIRE)) return acquire.OutputColor; 46 | if (state.StartsWith(DISENGAGE)) return disengage.OutputColor; 47 | return ready.OutputColor; 48 | } 49 | } 50 | 51 | /// 52 | /// IToggle implementation. 53 | /// 54 | public bool ToggleStatus 55 | { 56 | get 57 | { 58 | // Toggle is considered "on" when we're engaging or disengaging, off at all other times. 59 | if (SourceModule == null) return false; 60 | string state = SourceModule.state; 61 | return string.IsNullOrEmpty(state) 62 | || state.StartsWith(ACQUIRE) 63 | || state.StartsWith(DISENGAGE); 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /files/Parts/crewable/mk1pod_v2.cfg: -------------------------------------------------------------------------------- 1 | // Adds an occupancy indicator to the Mk1 V2 command pod. 2 | 3 | @PART[mk1pod_v2]:FOR[IndicatorLights] { 4 | 5 | //------------------------------------------------------------------------- 6 | // INDICATOR MESHES 7 | //------------------------------------------------------------------------- 8 | 9 | MODEL 10 | { 11 | model = IndicatorLights/Meshes/squareLamp 12 | scale = 1.15, 0.25, 0.5 13 | position = 0, 0.173, -0.433 14 | rotation = -160, 0, 0 15 | } 16 | 17 | //------------------------------------------------------------------------- 18 | // CONTROLLABLE EMISSIVES 19 | //------------------------------------------------------------------------- 20 | 21 | MODULE { 22 | name = ModuleControllableEmissive 23 | target = IndicatorLights/Meshes/squareLamp 24 | emissiveName = indicator 25 | } 26 | 27 | //------------------------------------------------------------------------- 28 | // CONTROLLERS 29 | //------------------------------------------------------------------------- 30 | 31 | MODULE { 32 | name = ModuleCrewIndicatorToggle 33 | toggleName = indicatorToggle 34 | } 35 | 36 | MODULE { 37 | name = ModuleCrewIndicator 38 | toggleName = indicatorToggle 39 | } 40 | 41 | MODULE 42 | { 43 | name = ModuleScienceAvailabilityIndicator 44 | experimentID = crewReport 45 | lowValueColor = ModuleCrewIndicator 46 | mediumValueColor = highValueColor 47 | highValueColor = blink(lowValueColor, 200, $Off, 200) 48 | } 49 | 50 | MODULE { 51 | name = ModuleScienceDataIndicator 52 | experimentID = crewReport 53 | emissiveName = indicator 54 | dataColor = ModuleCrewIndicator 55 | emptyColor = ModuleScienceAvailabilityIndicator 56 | } 57 | 58 | //------------------------------------------------------------------------- 59 | // TWEAKS 60 | //------------------------------------------------------------------------- 61 | 62 | // This pod uses a ModuleColorChanger to handle its cabin lights, set up 63 | // so that by default it takes control of *all* emissives on the part. 64 | // Need to tell it not to tinker with the meshes that we're adding here; 65 | // otherwise, the crew indicator will just turn on and off with the cabin 66 | // lights instead of actually working as a crew indicator. 67 | @MODULE[ModuleColorChanger] { 68 | excludedRenderer = IndicatorLights/Meshes/squareLamp(Clone) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /files/Parts/crewable/kv1pod.cfg: -------------------------------------------------------------------------------- 1 | // Adds occupancy indicators to the "Shallot" command pod. 2 | 3 | @PART[kv1Pod]:NEEDS[SquadExpansion]:FOR[IndicatorLights] { 4 | 5 | //------------------------------------------------------------------------- 6 | // INDICATOR MESHES 7 | //------------------------------------------------------------------------- 8 | 9 | MODEL 10 | { 11 | model = IndicatorLights/Meshes/nubbinLamp 12 | scale = 0.7, 0.7, 0.5 13 | position = 0, 0.293, -0.87 14 | rotation = -172, 0, 0 15 | } 16 | 17 | //------------------------------------------------------------------------- 18 | // CONTROLLABLE EMISSIVES 19 | //------------------------------------------------------------------------- 20 | 21 | MODULE { 22 | name = ModuleControllableEmissive 23 | target = IndicatorLights/Meshes/nubbinLamp 24 | emissiveName = indicator0 25 | } 26 | 27 | //------------------------------------------------------------------------- 28 | // CONTROLLERS 29 | //------------------------------------------------------------------------- 30 | 31 | MODULE { 32 | name = ModuleCrewIndicatorToggle 33 | toggleName = indicatorToggle 34 | } 35 | 36 | MODULE { 37 | name = ModuleCrewIndicator 38 | controllerName = crewController0 39 | toggleName = indicatorToggle 40 | } 41 | 42 | MODULE 43 | { 44 | name = ModuleScienceAvailabilityIndicator 45 | controllerName = availability0 46 | experimentID = crewReport 47 | lowValueColor = crewController0 48 | mediumValueColor = highValueColor 49 | highValueColor = blink(lowValueColor, 200, $Off, 200) 50 | } 51 | 52 | MODULE { 53 | name = ModuleScienceDataIndicator 54 | experimentID = crewReport 55 | emissiveName = indicator0 56 | dataColor = crewController0 57 | emptyColor = availability0 58 | } 59 | 60 | 61 | //------------------------------------------------------------------------- 62 | // TWEAKS 63 | //------------------------------------------------------------------------- 64 | 65 | // This pod uses a ModuleColorChanger to handle its ablator, set up 66 | // so that by default it takes control of *all* emissives on the part. 67 | // Need to tell it not to tinker with the meshes that we're adding here; 68 | // otherwise, the crew indicator will end up getting colored with the 69 | // ablator instead of actually working as a crew indicator. 70 | @MODULE[ModuleColorChanger] { 71 | excludedRenderer = IndicatorLights/Meshes/nubbinLamp(Clone) 72 | } 73 | } -------------------------------------------------------------------------------- /src/ModuleBiomeScannerIndicator.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace IndicatorLights 4 | { 5 | /// 6 | /// Indicates whether the current biome can be surface-scanned or not. 7 | /// 8 | class ModuleBiomeScannerIndicator : ModuleSourceIndicator, IToggle 9 | { 10 | private IColorSource readySource; 11 | private IColorSource inactiveSource; 12 | 13 | /// 14 | /// The color to display when it's ready to take a biome scan. 15 | /// 16 | [KSPField] 17 | [ColorSourceIDField] 18 | public string readyColor = string.Empty; 19 | 20 | /// 21 | /// The color to display when it's inactive (either this biome has already been 22 | /// scanned, or else it's not in a usable situation). 23 | /// 24 | [KSPField] 25 | [ColorSourceIDField] 26 | public string inactiveColor = Colors.ToString(DefaultColor.Off); 27 | 28 | public override void ParseIDs() 29 | { 30 | base.ParseIDs(); 31 | readySource = FindColorSource(readyColor); 32 | inactiveSource = FindColorSource(inactiveColor); 33 | } 34 | 35 | public override bool HasColor 36 | { 37 | get 38 | { 39 | return CurrentSource.HasColor; 40 | } 41 | } 42 | 43 | public override Color OutputColor 44 | { 45 | get 46 | { 47 | return CurrentSource.OutputColor; 48 | } 49 | } 50 | 51 | private IColorSource CurrentSource 52 | { 53 | get 54 | { 55 | return ToggleStatus ? readySource : inactiveSource; 56 | } 57 | } 58 | 59 | /// 60 | /// IToggle implementation. 61 | /// 62 | public bool ToggleStatus 63 | { 64 | get 65 | { 66 | // If we're in the editor, or not landed, then it's inactive. 67 | if (!HighLogic.LoadedSceneIsFlight 68 | || (vessel == null) 69 | || !vessel.LandedOrSplashed) 70 | return false; 71 | 72 | // Figure out what biome we're in 73 | string biomeName = vessel.GetCurrentBiome() ?? vessel.situation.ToString(); 74 | return !ResourceMap.Instance.IsBiomeUnlocked(vessel.mainBody.flightGlobalsIndex, biomeName); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Logging.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace IndicatorLights 5 | { 6 | /// 7 | /// Utility wrapper for logging messages. 8 | /// 9 | static class Logging 10 | { 11 | public static void Log(object message) 12 | { 13 | Debug.Log("[IndicatorLights] " + message); 14 | } 15 | 16 | public static void Warn(object message) 17 | { 18 | Debug.LogWarning("[IndicatorLights] " + message); 19 | } 20 | 21 | public static void Error(object message) 22 | { 23 | Debug.LogError("[IndicatorLights] " + message); 24 | } 25 | 26 | public static void Exception(string message, Exception e) 27 | { 28 | Error(message + " (" + e.GetType().Name + ") " + e.Message + ": " + e.StackTrace); 29 | } 30 | 31 | public static void Exception(Exception e) 32 | { 33 | Error("(" + e.GetType().Name + ") " + e.Message + ": " + e.StackTrace); 34 | } 35 | 36 | public static string GetTitle(this Part part) 37 | { 38 | return (part == null) ? "" : ((part.partInfo == null) ? part.partName : part.partInfo.title); 39 | } 40 | 41 | public static string GetIdentifier(object obj) 42 | { 43 | Identifiers.IIdentifiable identifiable = obj as Identifiers.IIdentifiable; 44 | return (identifiable == null) ? obj.GetType().Name : identifiable.Identifier; 45 | } 46 | 47 | /// 48 | /// Useful for debugging per-update-frame events without spamming the log to uselessness. 49 | /// 50 | public class Throttled 51 | { 52 | private readonly string label; 53 | private readonly RateLimiter cooldown; 54 | 55 | public Throttled(string label, long milliseconds) 56 | { 57 | this.label = label; 58 | this.cooldown = new RateLimiter(TimeSpan.FromMilliseconds(milliseconds)); 59 | } 60 | 61 | public bool Log(object message) 62 | { 63 | DateTime now = DateTime.Now; 64 | if (!cooldown.Check()) return false; 65 | if (string.IsNullOrEmpty(label)) 66 | { 67 | Logging.Log(message); 68 | } 69 | else 70 | { 71 | Logging.Log(label + ": " + message); 72 | } 73 | return true; 74 | } 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /files/Parts/indicatorLightSmall/indicatorLightSmall.cfg: -------------------------------------------------------------------------------- 1 | PART 2 | { 3 | name = indicatorLightSmall 4 | module = Part 5 | author = Snark 6 | mesh = model.mu 7 | rescaleFactor = 1 8 | node_attach = 0.0, 0.0, 0.0, 0.0, -1.0, 0.0 9 | TechRequired = engineering101 10 | entryCost = 500 11 | cost = 50 12 | category = Utility 13 | title = BL-01 Indicator Light 14 | manufacturer = Blinkenlights LLC 15 | description = Doesn't seem to achieve much, but we'll probably find out what it's for later on. 16 | attachRules = 0,1,0,0,1 17 | mass = 0.001 18 | dragModelType = default 19 | maximum_drag = 0.2 20 | minimum_drag = 0.2 21 | angularDrag = 1 22 | crashTolerance = 8 23 | maxTemp = 2400 24 | PhysicsSignificance = 1 25 | bulkheadProfiles = srf 26 | tags = snark indicator light blinken led 27 | 28 | MODULE { 29 | name = ModuleControllableEmissive 30 | 31 | // this identifies the mesh in the model that we're controlling 32 | target = light 33 | 34 | // this is the ID that ModuleToggleLED uses to find this 35 | emissiveName = light 36 | } 37 | 38 | // The main controller (identifiable as such because it specifies an 39 | // emissiveName, meaning that it's actually controlling something. 40 | // 41 | // Order is important. This module appears before the ModuleCustomColoredEmissive 42 | // modules before, because we want this module's UI to be above theirs in the 43 | // KSP vehicle editor. 44 | MODULE { 45 | name = ModuleToggleLED 46 | 47 | // the light is initially turned off 48 | status = false 49 | 50 | // this is the ModuleControllableEmissive we want to control 51 | emissiveName = light 52 | 53 | // this is where we get the color to show when the toggle is "on" 54 | activeColor = ModuleCustomBlink 55 | 56 | // this is where we get the color to show when the toggle is "off" 57 | inactiveColor = inactiveColorSource 58 | 59 | // make it operate with the Light action group by default 60 | defaultActionGroup = Light 61 | } 62 | 63 | MODULE { 64 | name = ModuleCustomColoredEmissive 65 | 66 | // This is the ID that ModuleCustomBlink uses to find this 67 | controllerName = activeColorSource 68 | 69 | label = On 70 | color = $ToggleLED 71 | 72 | // UI is initially turned off, since the light is initially turned on 73 | isUiEnabled = false 74 | } 75 | 76 | MODULE { 77 | name = ModuleCustomColoredEmissive 78 | 79 | // This is the ID that ModuleToggleLED and ModuleCustomBlink use to find this 80 | controllerName = inactiveColorSource 81 | 82 | label = Off 83 | color = $Off 84 | } 85 | 86 | MODULE { 87 | name = ModuleCustomBlink 88 | uiToggle = Editor 89 | onColor = activeColorSource 90 | offColor = inactiveColorSource 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /examples/RandomLights.cfg: -------------------------------------------------------------------------------- 1 | // Example of using the "random" color syntax. Adds three lights to 2 | // the Mk1 structural fuselage which, when turned on, blink in random 3 | // patterns. 4 | @PART[Mk1FuselageStructural] { 5 | @description ^= :(.)$:$0 With random lights!: 6 | 7 | //------------------------------------------------------------------------- 8 | // INDICATOR MESHES 9 | //------------------------------------------------------------------------- 10 | 11 | MODEL 12 | { 13 | model = IndicatorLights/Meshes/squareLamp 14 | scale = 1, 0.5, 1 15 | position = 0, 0.3, -0.625 16 | rotation = 0, 180, 0 17 | } 18 | 19 | MODEL 20 | { 21 | model = IndicatorLights/Meshes/squareLamp 22 | scale = 1, 0.5, 1 23 | position = 0, 0, -0.625 24 | rotation = 0, 180, 0 25 | } 26 | 27 | MODEL 28 | { 29 | model = IndicatorLights/Meshes/squareLamp 30 | scale = 1, 0.5, 1 31 | position = 0, -0.3, -0.625 32 | rotation = 0, 180, 0 33 | } 34 | 35 | 36 | //------------------------------------------------------------------------- 37 | // CONTROLLABLE EMISSIVES 38 | //------------------------------------------------------------------------- 39 | 40 | MODULE { 41 | name = ModuleControllableEmissive 42 | target = IndicatorLights/Meshes/squareLamp:0 43 | emissiveName = indicator1 44 | } 45 | 46 | MODULE { 47 | name = ModuleControllableEmissive 48 | target = IndicatorLights/Meshes/squareLamp:1 49 | emissiveName = indicator2 50 | } 51 | 52 | MODULE { 53 | name = ModuleControllableEmissive 54 | target = IndicatorLights/Meshes/squareLamp:2 55 | emissiveName = indicator3 56 | } 57 | 58 | 59 | //------------------------------------------------------------------------- 60 | // CONTROLLERS 61 | //------------------------------------------------------------------------- 62 | 63 | // This turns everybody on and off. 64 | MODULE { 65 | name = ModuleToggleLED 66 | } 67 | 68 | MODULE { 69 | name = ModuleBooleanIndicator 70 | input = ModuleToggleLED 71 | // Bias of +0.6 means it's on most of the time. 72 | activeColor = random($ToggleLED, $Off, 50, 0.6) 73 | inactiveColor = $Off 74 | emissiveName = indicator1 75 | } 76 | 77 | MODULE { 78 | name = ModuleBooleanIndicator 79 | input = ModuleToggleLED 80 | // Fun with compound color sources! :-) 81 | activeColor = random(random($ToggleLED, $Off, 50, 0.6), random(#FF0000, $Off, 50, -0.3), 300) 82 | inactiveColor = $Off 83 | emissiveName = indicator2 84 | } 85 | 86 | MODULE { 87 | name = ModuleBooleanIndicator 88 | input = ModuleToggleLED 89 | // Bias of -0.5 means it's off most of the time. 90 | activeColor = random($ToggleLED, $Off, 50, -0.5) 91 | inactiveColor = $Off 92 | emissiveName = indicator3 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /files/Parts/antennas/hg55.cfg: -------------------------------------------------------------------------------- 1 | // Adds an indicator to the HG-55 antenna. 2 | 3 | @PART[HighGainAntenna]:FOR[IndicatorLights] { 4 | 5 | //------------------------------------------------------------------------- 6 | // INDICATOR MESHES 7 | //------------------------------------------------------------------------- 8 | 9 | MODEL 10 | { 11 | model = IndicatorLights/Meshes/squareLamp2 12 | position = 0, 0.080, 0.144 13 | scale = 2.5, 1, 0.8 14 | } 15 | 16 | 17 | //------------------------------------------------------------------------- 18 | // CONTROLLABLE EMISSIVES 19 | //------------------------------------------------------------------------- 20 | 21 | MODULE { 22 | name = ModuleControllableEmissive 23 | target = IndicatorLights/Meshes/squareLamp2 24 | emissiveName = indicator 25 | } 26 | 27 | 28 | //------------------------------------------------------------------------- 29 | // CONTROLLERS 30 | //------------------------------------------------------------------------- 31 | 32 | MODULE { 33 | name = ModuleDataTransmitterIndicator 34 | emissiveName = indicator 35 | // Get really fancy with busy color. This module type exposes one static 36 | // field, dataRate. We'll use a random flicker for the animation (like 37 | // an old-time modem), and we'll plug the data rate both into the flicker 38 | // period (so higher-speed antennas flicker faster), and into the bias 39 | // (so higher-speed antennas spend a greater percentage of their time 40 | // in the "on" state rather than "off"). 41 | // 42 | // We don't actually have to use the static syntax here (we could just 43 | // take the data rate for this antenna, do the math, and plug in the 44 | // literal numbers here). However, using the actual static expression 45 | // comes with a few benefits. First, it's more maintainable: we 46 | // can just use the same static expression for all the antennas, rather 47 | // than having to come up with a different expression with different math 48 | // for each one. Second, it's more explicit to anyone reading this just 49 | // *why* the number is what it is. Third, it's more robust: if Squad ever 50 | // tinkers with the data rates, or someone uses ModuleManager to tweak 51 | // them, this will automatically follow suit. Fourth, it's friendlier 52 | // to modders: if you have your own antenna and you want to set it up 53 | // for IndicatorLights compatibility, you can just copy the following 54 | // line verbatim and you'll get behavior that's consistent with what 55 | // IndicatorLights does with the stock antennas. 56 | busyColor = random($ToggleLED, $Off, divide(400, static(dataRate)), between(subtract(multiply(sqrt(static(dataRate)), 1.1), 2.3), -0.4, 0.5)) 57 | } 58 | } -------------------------------------------------------------------------------- /files/Parts/antennas/smallRelayDish.cfg: -------------------------------------------------------------------------------- 1 | // Adds an indicator to the small relay dish. 2 | 3 | @PART[RelayAntenna5]:FOR[IndicatorLights] { 4 | 5 | //------------------------------------------------------------------------- 6 | // INDICATOR MESHES 7 | //------------------------------------------------------------------------- 8 | 9 | MODEL 10 | { 11 | model = IndicatorLights/Meshes/nubbinLamp 12 | position = 0, 0.389, 0 13 | scale = 2.2, 2.2, 1 14 | rotation = -90, 0, 0 15 | } 16 | 17 | 18 | //------------------------------------------------------------------------- 19 | // CONTROLLABLE EMISSIVES 20 | //------------------------------------------------------------------------- 21 | 22 | MODULE { 23 | name = ModuleControllableEmissive 24 | target = IndicatorLights/Meshes/nubbinLamp 25 | emissiveName = indicator 26 | } 27 | 28 | 29 | //------------------------------------------------------------------------- 30 | // CONTROLLERS 31 | //------------------------------------------------------------------------- 32 | 33 | MODULE { 34 | name = ModuleDataTransmitterIndicator 35 | emissiveName = indicator 36 | // Get really fancy with busy color. This module type exposes one static 37 | // field, dataRate. We'll use a random flicker for the animation (like 38 | // an old-time modem), and we'll plug the data rate both into the flicker 39 | // period (so higher-speed antennas flicker faster), and into the bias 40 | // (so higher-speed antennas spend a greater percentage of their time 41 | // in the "on" state rather than "off"). 42 | // 43 | // We don't actually have to use the static syntax here (we could just 44 | // take the data rate for this antenna, do the math, and plug in the 45 | // literal numbers here). However, using the actual static expression 46 | // comes with a few benefits. First, it's more maintainable: we 47 | // can just use the same static expression for all the antennas, rather 48 | // than having to come up with a different expression with different math 49 | // for each one. Second, it's more explicit to anyone reading this just 50 | // *why* the number is what it is. Third, it's more robust: if Squad ever 51 | // tinkers with the data rates, or someone uses ModuleManager to tweak 52 | // them, this will automatically follow suit. Fourth, it's friendlier 53 | // to modders: if you have your own antenna and you want to set it up 54 | // for IndicatorLights compatibility, you can just copy the following 55 | // line verbatim and you'll get behavior that's consistent with what 56 | // IndicatorLights does with the stock antennas. 57 | busyColor = random($ToggleLED, $Off, divide(400, static(dataRate)), between(subtract(multiply(sqrt(static(dataRate)), 1.1), 2.3), -0.4, 0.5)) 58 | } 59 | } -------------------------------------------------------------------------------- /files/Parts/antennas/mediumRelayDish.cfg: -------------------------------------------------------------------------------- 1 | // Adds an indicator to the medium relay dish. 2 | 3 | @PART[RelayAntenna50]:FOR[IndicatorLights] { 4 | 5 | //------------------------------------------------------------------------- 6 | // INDICATOR MESHES 7 | //------------------------------------------------------------------------- 8 | 9 | MODEL 10 | { 11 | model = IndicatorLights/Meshes/nubbinLamp 12 | position = 0, 1.079, 0 13 | scale = 5, 5, 1.5 14 | rotation = -90, 0, 0 15 | } 16 | 17 | 18 | //------------------------------------------------------------------------- 19 | // CONTROLLABLE EMISSIVES 20 | //------------------------------------------------------------------------- 21 | 22 | MODULE { 23 | name = ModuleControllableEmissive 24 | target = IndicatorLights/Meshes/nubbinLamp 25 | emissiveName = indicator 26 | } 27 | 28 | 29 | //------------------------------------------------------------------------- 30 | // CONTROLLERS 31 | //------------------------------------------------------------------------- 32 | 33 | MODULE { 34 | name = ModuleDataTransmitterIndicator 35 | emissiveName = indicator 36 | // Get really fancy with busy color. This module type exposes one static 37 | // field, dataRate. We'll use a random flicker for the animation (like 38 | // an old-time modem), and we'll plug the data rate both into the flicker 39 | // period (so higher-speed antennas flicker faster), and into the bias 40 | // (so higher-speed antennas spend a greater percentage of their time 41 | // in the "on" state rather than "off"). 42 | // 43 | // We don't actually have to use the static syntax here (we could just 44 | // take the data rate for this antenna, do the math, and plug in the 45 | // literal numbers here). However, using the actual static expression 46 | // comes with a few benefits. First, it's more maintainable: we 47 | // can just use the same static expression for all the antennas, rather 48 | // than having to come up with a different expression with different math 49 | // for each one. Second, it's more explicit to anyone reading this just 50 | // *why* the number is what it is. Third, it's more robust: if Squad ever 51 | // tinkers with the data rates, or someone uses ModuleManager to tweak 52 | // them, this will automatically follow suit. Fourth, it's friendlier 53 | // to modders: if you have your own antenna and you want to set it up 54 | // for IndicatorLights compatibility, you can just copy the following 55 | // line verbatim and you'll get behavior that's consistent with what 56 | // IndicatorLights does with the stock antennas. 57 | busyColor = random($ToggleLED, $Off, divide(400, static(dataRate)), between(subtract(multiply(sqrt(static(dataRate)), 1.1), 2.3), -0.4, 0.5)) 58 | } 59 | } -------------------------------------------------------------------------------- /src/PartSearchStrategy.cs: -------------------------------------------------------------------------------- 1 | namespace IndicatorLights 2 | { 3 | /// 4 | /// Determines how IndicatorLights modules search the ship's parts when 5 | /// looking for target modules to interoperate with. 6 | /// 7 | public enum PartSearchStrategy 8 | { 9 | /// 10 | /// Search the host part only (i.e. the part where the module lives). 11 | /// 12 | host, 13 | 14 | /// 15 | /// Search the parent part only (i.e. the direct parent of the part where the module lives). 16 | /// 17 | parent 18 | } 19 | 20 | /// 21 | /// Extension methods for PartSearchStrategy. 22 | /// 23 | internal static class PartSearchStrategies 24 | { 25 | /// 26 | /// The search strategy to use for everything, unless specified otherwise. 27 | /// 28 | public const PartSearchStrategy Default = PartSearchStrategy.host; 29 | 30 | /// 31 | /// Choose the part to use. Returns null if not found. 32 | /// 33 | /// 34 | /// 35 | /// 36 | public static Part ChoosePart(this PartSearchStrategy strategy, PartModule module) 37 | { 38 | if (module == null) return null; 39 | switch (strategy) 40 | { 41 | case PartSearchStrategy.host: 42 | return module.part; 43 | 44 | case PartSearchStrategy.parent: 45 | return (module.part == null) ? null : module.part.parent; 46 | 47 | default: 48 | return null; 49 | } 50 | } 51 | 52 | /// 53 | /// Gets whether the specified candidate part is the chosen part for the specified module. 54 | /// 55 | /// 56 | /// 57 | /// 58 | /// 59 | public static bool IsChoice(this PartSearchStrategy strategy, PartModule module, Part candidatePart) 60 | { 61 | if (module == null) return candidatePart == null; 62 | switch (strategy) 63 | { 64 | case PartSearchStrategy.host: 65 | return false; 66 | case PartSearchStrategy.parent: 67 | return object.ReferenceEquals(candidatePart, ChoosePart(strategy, module)); 68 | default: 69 | return false; 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /files/Parts/antennas/communotron16S.cfg: -------------------------------------------------------------------------------- 1 | // Adds an indicator to the Communotron-16S antenna. 2 | 3 | @PART[SurfAntenna]:FOR[IndicatorLights] { 4 | 5 | //------------------------------------------------------------------------- 6 | // INDICATOR MESHES 7 | //------------------------------------------------------------------------- 8 | 9 | MODEL 10 | { 11 | model = IndicatorLights/Meshes/squareLamp2 12 | position = 0, 0.056, -0.003 13 | scale = 1.6, 0.5, 0.7 14 | rotation = 0, 0, 0 15 | } 16 | 17 | 18 | //------------------------------------------------------------------------- 19 | // CONTROLLABLE EMISSIVES 20 | //------------------------------------------------------------------------- 21 | 22 | MODULE { 23 | name = ModuleControllableEmissive 24 | target = IndicatorLights/Meshes/squareLamp2 25 | emissiveName = indicator 26 | } 27 | 28 | 29 | //------------------------------------------------------------------------- 30 | // CONTROLLERS 31 | //------------------------------------------------------------------------- 32 | 33 | MODULE { 34 | name = ModuleDataTransmitterIndicator 35 | emissiveName = indicator 36 | // Get really fancy with busy color. This module type exposes one static 37 | // field, dataRate. We'll use a random flicker for the animation (like 38 | // an old-time modem), and we'll plug the data rate both into the flicker 39 | // period (so higher-speed antennas flicker faster), and into the bias 40 | // (so higher-speed antennas spend a greater percentage of their time 41 | // in the "on" state rather than "off"). 42 | // 43 | // We don't actually have to use the static syntax here (we could just 44 | // take the data rate for this antenna, do the math, and plug in the 45 | // literal numbers here). However, using the actual static expression 46 | // comes with a few benefits. First, it's more maintainable: we 47 | // can just use the same static expression for all the antennas, rather 48 | // than having to come up with a different expression with different math 49 | // for each one. Second, it's more explicit to anyone reading this just 50 | // *why* the number is what it is. Third, it's more robust: if Squad ever 51 | // tinkers with the data rates, or someone uses ModuleManager to tweak 52 | // them, this will automatically follow suit. Fourth, it's friendlier 53 | // to modders: if you have your own antenna and you want to set it up 54 | // for IndicatorLights compatibility, you can just copy the following 55 | // line verbatim and you'll get behavior that's consistent with what 56 | // IndicatorLights does with the stock antennas. 57 | busyColor = random($ToggleLED, $Off, divide(400, static(dataRate)), between(subtract(multiply(sqrt(static(dataRate)), 1.1), 2.3), -0.4, 0.5)) 58 | } 59 | } -------------------------------------------------------------------------------- /src/ModuleCrewIndicatorToggle.cs: -------------------------------------------------------------------------------- 1 | namespace IndicatorLights 2 | { 3 | /// 4 | /// A specialized toggle for turning crew indicators on/off. 5 | /// 6 | public class ModuleCrewIndicatorToggle : PartModule, Identifiers.IIdentifiable, IToggle 7 | { 8 | [KSPField(guiName = "Crew LEDs", isPersistant = true, guiActive = true, guiActiveEditor = true), 9 | UI_Toggle(affectSymCounterparts = UI_Scene.Editor, controlEnabled = true, enabledText = "On", disabledText = "Off")] 10 | public bool status = true; 11 | private BaseField StatusField { get { return Fields["status"]; } } 12 | 13 | [KSPField] 14 | public string toggleName = null; 15 | 16 | /// 17 | /// Determines where the toggle UI is visible. 18 | /// 19 | [KSPField] 20 | public UI_Scene uiToggle = UI_Scene.Editor; 21 | 22 | /// 23 | /// Action-group method for toggling status. 24 | /// 25 | /// 26 | [KSPAction("Toggle Crew LEDs")] 27 | public void OnToggleAction(KSPActionParam actionParam) 28 | { 29 | status = actionParam.type != KSPActionType.Deactivate; 30 | } 31 | private BaseAction ToggleAction { get { return Actions["OnToggleAction"]; } } 32 | 33 | /// 34 | /// Action-group method for setting status to true. 35 | /// 36 | /// 37 | [KSPAction("Activate Crew LEDs")] 38 | public void OnActivateAction(KSPActionParam actionParam) 39 | { 40 | status = true; 41 | } 42 | private BaseAction ActivateAction { get { return Actions["OnActivateAction"]; } } 43 | 44 | /// 45 | /// Action-group method for setting status to false. 46 | /// 47 | /// 48 | [KSPAction("Deactivate Crew LEDs")] 49 | public void OnDeactivateAction(KSPActionParam actionParam) 50 | { 51 | status = false; 52 | } 53 | private BaseAction DeactivateAction { get { return Actions["OnDeactivateAction"]; } } 54 | 55 | public string Identifier 56 | { 57 | get { return toggleName; } 58 | } 59 | 60 | public bool ToggleStatus 61 | { 62 | get { return status; } 63 | } 64 | 65 | public override void OnStart(StartState state) 66 | { 67 | base.OnStart(state); 68 | StatusField.guiActive = uiToggle.IsFlightEnabled(); 69 | StatusField.guiActiveEditor = uiToggle.IsEditorEnabled(); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /files/Parts/crewable/mk1cabin.cfg: -------------------------------------------------------------------------------- 1 | // Adds occupancy indicators to the Mk1 crew cabin. 2 | 3 | @PART[MK1CrewCabin]:FOR[IndicatorLights] { 4 | 5 | //------------------------------------------------------------------------- 6 | // INDICATOR MESHES 7 | //------------------------------------------------------------------------- 8 | 9 | MODEL 10 | { 11 | model = IndicatorLights/Meshes/squareLamp 12 | scale = 0.6, 0.6, 0.5 13 | position = 0, 0.469, -0.625 14 | rotation = 0, 180, 0 15 | } 16 | 17 | MODEL 18 | { 19 | model = IndicatorLights/Meshes/squareLamp 20 | scale = 0.6, 0.6, 0.5 21 | position = 0, -0.469, -0.625 22 | rotation = 0, 180, 0 23 | } 24 | 25 | //------------------------------------------------------------------------- 26 | // CONTROLLABLE EMISSIVES 27 | //------------------------------------------------------------------------- 28 | 29 | MODULE { 30 | name = ModuleControllableEmissive 31 | target = IndicatorLights/Meshes/squareLamp:0 32 | emissiveName = indicator0 33 | } 34 | 35 | MODULE { 36 | name = ModuleControllableEmissive 37 | target = IndicatorLights/Meshes/squareLamp:1 38 | emissiveName = indicator1 39 | } 40 | 41 | //------------------------------------------------------------------------- 42 | // CONTROLLERS 43 | //------------------------------------------------------------------------- 44 | 45 | MODULE { 46 | name = ModuleCrewIndicatorToggle 47 | toggleName = indicatorToggle 48 | } 49 | 50 | MODULE { 51 | name = ModuleCrewIndicator 52 | controllerName = crewController0 53 | toggleName = indicatorToggle 54 | } 55 | 56 | MODULE { 57 | name = ModuleCrewIndicator 58 | controllerName = crewController1 59 | toggleName = indicatorToggle 60 | } 61 | 62 | MODULE 63 | { 64 | name = ModuleScienceAvailabilityIndicator 65 | controllerName = availability0 66 | experimentID = crewReport 67 | lowValueColor = crewController0 68 | mediumValueColor = highValueColor 69 | highValueColor = blink(lowValueColor, 200, $Off, 200) 70 | } 71 | 72 | MODULE 73 | { 74 | name = ModuleScienceAvailabilityIndicator 75 | controllerName = availability1 76 | experimentID = crewReport 77 | lowValueColor = crewController1 78 | mediumValueColor = highValueColor 79 | highValueColor = blink(lowValueColor, 200, $Off, 200) 80 | } 81 | 82 | MODULE { 83 | name = ModuleScienceDataIndicator 84 | experimentID = crewReport 85 | emissiveName = indicator0 86 | dataColor = crewController0 87 | emptyColor = availability0 88 | } 89 | 90 | MODULE { 91 | name = ModuleScienceDataIndicator 92 | experimentID = crewReport 93 | emissiveName = indicator1 94 | dataColor = crewController1 95 | emptyColor = availability1 96 | } 97 | } -------------------------------------------------------------------------------- /files/Parts/dockingPorts/smallPort.cfg: -------------------------------------------------------------------------------- 1 | // Adds LED indicators to the small docking port. 2 | 3 | @PART[dockingPort3]:FOR[IndicatorLights] { 4 | @description ^= :(.)$:$0 Indicator lights display crossfeed status.: 5 | 6 | // We have to re-specify the model for the stock part, because this is 7 | // an older part that uses the "mesh =" syntax in its .cfg file instead 8 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 9 | // multiple models as part of the same part, which would prevent this mod 10 | // from adding meshes for the indicator lights. 11 | MODEL 12 | { 13 | model = Squad/Parts/Utility/dockingPortJr/dockingPortJr 14 | } 15 | 16 | //------------------------------------------------------------------------- 17 | // INDICATOR MESHES 18 | //------------------------------------------------------------------------- 19 | MODEL 20 | { 21 | model = IndicatorLights/Meshes/squareLamp2 22 | scale = 0.3, 0.6, 0.3 23 | position = -0.32, 0.06, 0 24 | rotation = 0, 0, 8 25 | } 26 | 27 | MODEL 28 | { 29 | model = IndicatorLights/Meshes/squareLamp2 30 | scale = 0.3, 0.6, 0.3 31 | position = 0.32, 0.06, 0 32 | rotation = 0, 0, -8 33 | } 34 | 35 | 36 | //------------------------------------------------------------------------- 37 | // CONTROLLABLE EMISSIVES 38 | //------------------------------------------------------------------------- 39 | MODULE { 40 | name = ModuleControllableEmissive 41 | target = IndicatorLights/Meshes/squareLamp2 42 | emissiveName = indicator 43 | } 44 | 45 | 46 | //------------------------------------------------------------------------- 47 | // CONTROLLERS 48 | //------------------------------------------------------------------------- 49 | 50 | // Controls the light manually. 51 | MODULE { 52 | name = ModuleToggleLED 53 | activeColor = ModuleDockingCrossfeedIndicator 54 | inactiveColor = $Off 55 | defaultActionGroup = Light 56 | } 57 | 58 | // This provides detection of the docking port state. We don't specify an 59 | // emissiveName here because it's not controlling the light directly (we're 60 | // just using it as an input to other modules). 61 | MODULE { 62 | name = ModuleDockingCrossfeedIndicator 63 | } 64 | 65 | // This detects the docking state. We make this the "root" controller that 66 | // actually drives the emissive color, because we want the indicator to blink 67 | // when it's acquiring or disengaging, regardless of the toggle setting. 68 | // If we're in the "ready" (or docked) state, then we'll take our input 69 | // from the toggle. 70 | MODULE { 71 | name = ModuleDockingStateIndicator 72 | emissiveName = indicator 73 | readyColor = ModuleToggleLED 74 | acquireColor = blink(ModuleDockingCrossfeedIndicator, 100, $Off, 100) 75 | disengageColor = blink(ModuleDockingCrossfeedIndicator, 120, $Off, 1080) 76 | } 77 | } -------------------------------------------------------------------------------- /examples/VesselSituation.cfg: -------------------------------------------------------------------------------- 1 | // Demonstrate the use of situation() syntax. 2 | // 3 | // This example adds lights to the HECS2 probe core, which light up in 4 | // various vessel situations (see comments below). 5 | 6 | @PART[HECS2_ProbeCore] { 7 | 8 | //------------------------------------------------------------------------- 9 | // INDICATOR MESHES 10 | //------------------------------------------------------------------------- 11 | 12 | MODEL 13 | { 14 | model = IndicatorLights/Meshes/nubbinLamp 15 | scale = 1.5, 1.5, 0.6 16 | position = -0.1, 0.02, -0.73 17 | rotation = 0, 180, 0 18 | } 19 | 20 | MODEL 21 | { 22 | model = IndicatorLights/Meshes/nubbinLamp 23 | scale = 1.5, 1.5, 0.6 24 | position = 0, 0.02, -0.73 25 | rotation = 0, 180, 0 26 | } 27 | 28 | MODEL 29 | { 30 | model = IndicatorLights/Meshes/nubbinLamp 31 | scale = 1.5, 1.5, 0.6 32 | position = 0.1, 0.02, -0.73 33 | rotation = 0, 180, 0 34 | } 35 | 36 | 37 | //------------------------------------------------------------------------- 38 | // CONTROLLABLE EMISSIVES 39 | //------------------------------------------------------------------------- 40 | 41 | // This emissive shows status when we're on the surface. 42 | MODULE { 43 | name = ModuleControllableEmissive 44 | target = IndicatorLights/Meshes/nubbinLamp:0 45 | emissiveName = surface 46 | } 47 | 48 | // This emissive shows status when we're flying (not on the surface, 49 | // but not in space). 50 | MODULE { 51 | name = ModuleControllableEmissive 52 | target = IndicatorLights/Meshes/nubbinLamp:1 53 | emissiveName = airborne 54 | } 55 | 56 | // This emissive shows status when we're in space. 57 | MODULE { 58 | name = ModuleControllableEmissive 59 | target = IndicatorLights/Meshes/nubbinLamp:2 60 | emissiveName = space 61 | } 62 | 63 | 64 | //------------------------------------------------------------------------- 65 | // CONTROLLERS 66 | //------------------------------------------------------------------------- 67 | 68 | // This controls the surface indicator. Green when on dry land, blue 69 | // when splashed. 70 | MODULE { 71 | name = ModuleBooleanIndicator 72 | input = situation(LANDED, SPLASHED) 73 | emissiveName = surface 74 | activeColor = if(situation(LANDED), $ToggleLED, #0040FF) 75 | } 76 | 77 | // This controls the indicator for when we're flying. 78 | MODULE { 79 | name = ModuleBooleanIndicator 80 | input = situation(FLYING) 81 | emissiveName = airborne 82 | } 83 | 84 | // Rapid blinking for suborbital. Pulsating for escape. Solid for orbit. 85 | MODULE { 86 | name = ModuleBooleanIndicator 87 | input = situation(SUB_ORBITAL, ORBITING, ESCAPING) 88 | emissiveName = space 89 | activeColor = if(situation(ORBITING), $ToggleLED, if(situation(SUB_ORBITAL), blink($ToggleLED, 200, $Off, 200), pulsate($ToggleLED, 1000, 0.5))) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /files/Parts/crewable/cupola.cfg: -------------------------------------------------------------------------------- 1 | // Adds an occupancy indicator to the crew cupola. 2 | 3 | @PART[cupola]:FOR[IndicatorLights] { 4 | 5 | // We have to re-specify the model for the stock part, because this is 6 | // an older part that uses the "mesh =" syntax in its .cfg file instead 7 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 8 | // multiple models as part of the same part, which would prevent this mod 9 | // from adding meshes for the indicator lights. 10 | MODEL 11 | { 12 | model = Squad/Parts/Command/cupola/model 13 | } 14 | 15 | //------------------------------------------------------------------------- 16 | // INDICATOR MESHES 17 | //------------------------------------------------------------------------- 18 | 19 | MODEL 20 | { 21 | model = IndicatorLights/Meshes/squareLamp 22 | scale = 1, 0.25, 0.5 23 | position = 0, -0.157, -1.353 24 | rotation = 0, 180, 0 25 | } 26 | 27 | //------------------------------------------------------------------------- 28 | // CONTROLLABLE EMISSIVES 29 | //------------------------------------------------------------------------- 30 | 31 | MODULE { 32 | name = ModuleControllableEmissive 33 | target = IndicatorLights/Meshes/squareLamp 34 | emissiveName = indicator 35 | } 36 | 37 | //------------------------------------------------------------------------- 38 | // CONTROLLERS 39 | //------------------------------------------------------------------------- 40 | 41 | MODULE { 42 | name = ModuleCrewIndicatorToggle 43 | toggleName = indicatorToggle 44 | } 45 | 46 | MODULE { 47 | name = ModuleCrewIndicator 48 | toggleName = indicatorToggle 49 | } 50 | 51 | MODULE 52 | { 53 | name = ModuleScienceAvailabilityIndicator 54 | experimentID = crewReport 55 | lowValueColor = ModuleCrewIndicator 56 | mediumValueColor = highValueColor 57 | highValueColor = blink(lowValueColor, 200, $Off, 200) 58 | } 59 | 60 | MODULE { 61 | name = ModuleScienceDataIndicator 62 | experimentID = crewReport 63 | emissiveName = indicator 64 | dataColor = ModuleCrewIndicator 65 | emptyColor = ModuleScienceAvailabilityIndicator 66 | } 67 | 68 | 69 | //------------------------------------------------------------------------- 70 | // TWEAKS 71 | //------------------------------------------------------------------------- 72 | 73 | // This pod uses a ModuleColorChanger to handle its cabin lights, set up 74 | // so that by default it takes control of *all* emissives on the part. 75 | // Need to tell it not to tinker with the meshes that we're adding here; 76 | // otherwise, the crew indicator will just turn on and off with the cabin 77 | // lights instead of actually working as a crew indicator. 78 | @MODULE[ModuleColorChanger] { 79 | excludedRenderer = IndicatorLights/Meshes/squareLamp(Clone) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Loader.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace IndicatorLights 4 | { 5 | /// 6 | /// Runs once when the game hits the main menu on startup. Loads custom IndicatorLights 7 | /// config used in various places. 8 | /// 9 | [KSPAddon(KSPAddon.Startup.MainMenu, true)] 10 | public class Loader : MonoBehaviour 11 | { 12 | private const string MASTER_NODE_NAME = "IndicatorLights"; 13 | 14 | private delegate void ConfigLoader(ConfigNode node); 15 | 16 | /// 17 | /// Here when the script starts up. 18 | /// 19 | public void Start() 20 | { 21 | Configuration.Initialize(); 22 | UrlDir.UrlConfig[] configs = GameDatabase.Instance.GetConfigs(MASTER_NODE_NAME); 23 | if (configs.Length < 1) 24 | { 25 | Logging.Error("Can't find main " + MASTER_NODE_NAME + " config node! Some features will be inoperable."); 26 | return; 27 | } 28 | ConfigNode masterNode = configs[0].config; 29 | ProcessMasterNode(masterNode); 30 | 31 | // Community Trait Icons integration. If initCTIWrapper returns true, 32 | // it means that Community Trait Icons has been identified as being 33 | // loaded, and therefore we can load its colors. 34 | if (Compatibility.CTIWrapper.initCTIWrapper()) 35 | { 36 | StartCoroutine(ModuleCrewIndicator.LoadCommunityTraitIconColors()); 37 | } 38 | } 39 | 40 | /// 41 | /// Process the main IndicatorLights config node. 42 | /// 43 | /// 44 | private static void ProcessMasterNode(ConfigNode masterNode) 45 | { 46 | TryProcessChildNode(masterNode, ModuleCrewIndicator.CONFIG_NODE_NAME, ModuleCrewIndicator.LoadConfig); 47 | } 48 | 49 | /// 50 | /// Looks for a child with the specified name, and delegates to it if found. 51 | /// 52 | /// 53 | /// 54 | /// 55 | private static void TryProcessChildNode(ConfigNode masterNode, string childName, ConfigLoader loader) 56 | { 57 | ConfigNode child = masterNode.nodes.GetNode(childName); 58 | if (child == null) 59 | { 60 | Logging.Warn("Child node " + childName + " of master config node " + MASTER_NODE_NAME + " not found, skipping"); 61 | } 62 | else 63 | { 64 | Logging.Log("Loading " + masterNode.name + " config: " + child.name); 65 | loader(child); 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /files/Parts/crewable/mk1can.cfg: -------------------------------------------------------------------------------- 1 | // Adds an occupancy indicator to the Mk1 lander can. 2 | 3 | @PART[landerCabinSmall]:FOR[IndicatorLights] { 4 | 5 | // We have to re-specify the model for the stock part, because this is 6 | // an older part that uses the "mesh =" syntax in its .cfg file instead 7 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 8 | // multiple models as part of the same part, which would prevent this mod 9 | // from adding meshes for the indicator lights. 10 | MODEL 11 | { 12 | model = Squad/Parts/Command/mk1LanderCan/model 13 | } 14 | 15 | //------------------------------------------------------------------------- 16 | // INDICATOR MESHES 17 | //------------------------------------------------------------------------- 18 | 19 | MODEL 20 | { 21 | model = IndicatorLights/Meshes/squareLamp 22 | scale = 1, 0.25, 0.5 23 | position = 0, -0.2737, -0.8592 24 | rotation = 0, 180, 0 25 | } 26 | 27 | //------------------------------------------------------------------------- 28 | // CONTROLLABLE EMISSIVES 29 | //------------------------------------------------------------------------- 30 | 31 | MODULE { 32 | name = ModuleControllableEmissive 33 | target = IndicatorLights/Meshes/squareLamp 34 | emissiveName = indicator 35 | } 36 | 37 | //------------------------------------------------------------------------- 38 | // CONTROLLERS 39 | //------------------------------------------------------------------------- 40 | 41 | MODULE { 42 | name = ModuleCrewIndicatorToggle 43 | toggleName = indicatorToggle 44 | } 45 | 46 | MODULE { 47 | name = ModuleCrewIndicator 48 | toggleName = indicatorToggle 49 | } 50 | 51 | MODULE 52 | { 53 | name = ModuleScienceAvailabilityIndicator 54 | experimentID = crewReport 55 | lowValueColor = ModuleCrewIndicator 56 | mediumValueColor = highValueColor 57 | highValueColor = blink(lowValueColor, 200, $Off, 200) 58 | } 59 | 60 | MODULE { 61 | name = ModuleScienceDataIndicator 62 | experimentID = crewReport 63 | emissiveName = indicator 64 | dataColor = ModuleCrewIndicator 65 | emptyColor = ModuleScienceAvailabilityIndicator 66 | } 67 | 68 | 69 | //------------------------------------------------------------------------- 70 | // TWEAKS 71 | //------------------------------------------------------------------------- 72 | 73 | // This pod uses a ModuleColorChanger to handle its cabin lights, set up 74 | // so that by default it takes control of *all* emissives on the part. 75 | // Need to tell it not to tinker with the meshes that we're adding here; 76 | // otherwise, the crew indicator will just turn on and off with the cabin 77 | // lights instead of actually working as a crew indicator. 78 | @MODULE[ModuleColorChanger] { 79 | excludedRenderer = IndicatorLights/Meshes/squareLamp(Clone) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /files/Parts/antennas/largeRelayDish.cfg: -------------------------------------------------------------------------------- 1 | // Adds an indicator to the large relay dish. 2 | 3 | @PART[RelayAntenna100]:FOR[IndicatorLights] { 4 | 5 | //------------------------------------------------------------------------- 6 | // INDICATOR MESHES 7 | //------------------------------------------------------------------------- 8 | 9 | MODEL 10 | { 11 | model = IndicatorLights/Meshes/nubbinLamp 12 | position = 0, 1.25, 0 13 | scale = 5.7, 5.7, 5 14 | rotation = -90, 0, 0 15 | } 16 | 17 | MODEL 18 | { 19 | model = IndicatorLights/Meshes/nubbinLamp 20 | position = 0, 2.252, -0.07 21 | scale = 1.65, 1.65, 2 22 | rotation = -90, 0, 0 23 | } 24 | 25 | 26 | //------------------------------------------------------------------------- 27 | // CONTROLLABLE EMISSIVES 28 | //------------------------------------------------------------------------- 29 | 30 | MODULE { 31 | name = ModuleControllableEmissive 32 | target = IndicatorLights/Meshes/nubbinLamp 33 | emissiveName = indicator 34 | } 35 | 36 | 37 | //------------------------------------------------------------------------- 38 | // CONTROLLERS 39 | //------------------------------------------------------------------------- 40 | 41 | MODULE { 42 | name = ModuleDataTransmitterIndicator 43 | emissiveName = indicator 44 | // Get really fancy with busy color. This module type exposes one static 45 | // field, dataRate. We'll use a random flicker for the animation (like 46 | // an old-time modem), and we'll plug the data rate both into the flicker 47 | // period (so higher-speed antennas flicker faster), and into the bias 48 | // (so higher-speed antennas spend a greater percentage of their time 49 | // in the "on" state rather than "off"). 50 | // 51 | // We don't actually have to use the static syntax here (we could just 52 | // take the data rate for this antenna, do the math, and plug in the 53 | // literal numbers here). However, using the actual static expression 54 | // comes with a few benefits. First, it's more maintainable: we 55 | // can just use the same static expression for all the antennas, rather 56 | // than having to come up with a different expression with different math 57 | // for each one. Second, it's more explicit to anyone reading this just 58 | // *why* the number is what it is. Third, it's more robust: if Squad ever 59 | // tinkers with the data rates, or someone uses ModuleManager to tweak 60 | // them, this will automatically follow suit. Fourth, it's friendlier 61 | // to modders: if you have your own antenna and you want to set it up 62 | // for IndicatorLights compatibility, you can just copy the following 63 | // line verbatim and you'll get behavior that's consistent with what 64 | // IndicatorLights does with the stock antennas. 65 | busyColor = random($ToggleLED, $Off, divide(400, static(dataRate)), between(subtract(multiply(sqrt(static(dataRate)), 1.1), 2.3), -0.4, 0.5)) 66 | } 67 | } -------------------------------------------------------------------------------- /src/ModuleDataTransmitterIndicator.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace IndicatorLights 4 | { 5 | /// 6 | /// Module that shows when an antenna is transmitting data. 7 | /// 8 | class ModuleDataTransmitterIndicator : ModuleSourceIndicator, IToggle 9 | { 10 | private IColorSource busySource; 11 | private IColorSource inactiveSource; 12 | private ModuleDeployableAntenna deployable = null; 13 | 14 | /// 15 | /// The data rate of the transmitter, in mits/second. 16 | /// 17 | [StaticField] 18 | private double dataRate; 19 | 20 | /* 21 | To put things in perspective, here are the data rates of the stock KSP antennas, as of KSP 1.2: 22 | 23 | RA-2: 2.86 24 | Communotron 16: 3.33 25 | Communotron 16-S: 3.33 26 | DTS-M1: 5.71 27 | HG-5: 5.71 28 | RA-15: 5.71 29 | RA-100: 11.43 30 | 88-88: 20.00 31 | HG-55: 20.00 32 | */ 33 | 34 | /// 35 | /// The color to display when the transmitter is busy. 36 | /// 37 | [KSPField] 38 | [ColorSourceIDField] 39 | public string busyColor = ColorSources.Random( 40 | ColorSources.Constant(DefaultColor.ToggleLED), 41 | ColorSources.Constant(DefaultColor.Off), 42 | 100, 43 | 0.5) 44 | .ColorSourceID; 45 | 46 | /// 47 | /// The color to display when the transmitter is idle. 48 | /// 49 | [KSPField] 50 | [ColorSourceIDField] 51 | public string inactiveColor = ColorSources.Constant(DefaultColor.Off).ColorSourceID; 52 | 53 | public override void OnStart(StartState state) 54 | { 55 | base.OnStart(state); 56 | 57 | deployable = part.FindModuleImplementing(); // may be null 58 | } 59 | 60 | public override void ParseIDs() 61 | { 62 | base.ParseIDs(); 63 | dataRate = (SourceModule == null) ? 0.0 : SourceModule.DataRate; 64 | busySource = FindColorSource(busyColor); 65 | inactiveSource = FindColorSource(inactiveColor); 66 | } 67 | 68 | public override Color OutputColor 69 | { 70 | get 71 | { 72 | return (ToggleStatus ? busySource : inactiveSource).OutputColor; 73 | } 74 | } 75 | 76 | public bool ToggleStatus 77 | { 78 | get 79 | { 80 | return ((deployable == null) || (deployable.deployState == ModuleDeployablePart.DeployState.EXTENDED)) 81 | && (SourceModule != null) && SourceModule.IsBusy(); 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/ModuleBooleanIndicator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace IndicatorLights 5 | { 6 | /// 7 | /// A bi-state indicator whose output is controlled by other toggle controllers. 8 | /// 9 | class ModuleBooleanIndicator : ModuleEmissiveController, IToggle 10 | { 11 | /// 12 | /// Required. Specifies the input toggle that controls this module. Could be a simple 13 | /// identifier (such as a module or controller name), or it could be parseable toggle 14 | /// syntax such as "and(toggle1, !toggle2)". 15 | /// 16 | [KSPField] 17 | [ToggleIDField] 18 | public string input = string.Empty; 19 | 20 | /// 21 | /// Color used when in the "on" state. 22 | /// 23 | [KSPField] 24 | [ColorSourceIDField] 25 | public string activeColor = Colors.ToString(DefaultColor.ToggleLED); 26 | 27 | /// 28 | /// Color used when in the "off" state. 29 | /// 30 | [KSPField] 31 | [ColorSourceIDField] 32 | public string inactiveColor = Colors.ToString(DefaultColor.Off); 33 | 34 | bool isValid = false; 35 | private IToggle inputToggle = null; 36 | private IColorSource activeSource = null; 37 | private IColorSource inactiveSource = null; 38 | 39 | public override void ParseIDs() 40 | { 41 | base.ParseIDs(); 42 | 43 | try 44 | { 45 | inputToggle = RequireToggle(input); 46 | isValid = true; 47 | } 48 | catch (ArgumentException e) 49 | { 50 | Logging.Warn("Invalid input for " + Identifier + " on " + part.GetTitle() + ": " + e.Message); 51 | } 52 | activeSource = FindColorSource(activeColor); 53 | inactiveSource = FindColorSource(inactiveColor); 54 | } 55 | 56 | public override bool HasColor 57 | { 58 | get 59 | { 60 | return CurrentSource.HasColor; 61 | } 62 | } 63 | 64 | public override Color OutputColor 65 | { 66 | get 67 | { 68 | return CurrentSource.OutputColor; 69 | } 70 | } 71 | 72 | /// 73 | /// IToggle implementation. 74 | /// 75 | public bool ToggleStatus 76 | { 77 | get 78 | { 79 | return isValid ? inputToggle.ToggleStatus : false; 80 | } 81 | } 82 | 83 | private IColorSource CurrentSource 84 | { 85 | get 86 | { 87 | if (!isValid) return ColorSources.ERROR; 88 | return ToggleStatus ? activeSource : inactiveSource; 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/ModuleEmissiveController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace IndicatorLights 5 | { 6 | /// 7 | /// Base class for "simple" emissive controller modules that have only a single 8 | /// output color. 9 | /// 10 | /// Note that there may be multiple controllers targeting the same emissive. 11 | /// 12 | public abstract class ModuleEmissiveController : ModuleEmissiveControllerBase, IColorSource, Identifiers.IIdentifiable 13 | { 14 | /// 15 | /// This is used to uniquely identify a particular controller. Use the ControllerName 16 | /// property to access at run time. If not specified, ControllerName will simply 17 | /// return the name of the class. 18 | /// 19 | /// The use case for this property is if you want to have a controller that specifies 20 | /// an output color, but doesn't actually point at any emissiveName that it controls. 21 | /// This can be useful if you want to have "compound controllers", where the output 22 | /// from one controller serves as an input for another. 23 | /// 24 | [KSPField] 25 | public string controllerName = null; 26 | 27 | /// 28 | /// Call this at runtime to identify the controller. 29 | /// 30 | public string ColorSourceID 31 | { 32 | get 33 | { 34 | return string.IsNullOrEmpty(controllerName) ? GetType().Name : controllerName; 35 | } 36 | } 37 | 38 | /// 39 | /// Gets whether this controller has an output color available. Default implementation 40 | /// is to return true. The contract is that if this returns false, should not try to 41 | /// get the OutputColor. 42 | /// 43 | public virtual bool HasColor 44 | { 45 | get { return true; } 46 | } 47 | 48 | /// 49 | /// Gets the output color of this controller. Won't be called unless HasColor 50 | /// returns true. 51 | /// 52 | public abstract Color OutputColor { get; } 53 | 54 | /// 55 | /// Called on every frame when it's time to set colors on the controllable emissives. 56 | /// 57 | protected override void SetColors() 58 | { 59 | if (HasColor) 60 | { 61 | List controlledEmissives = Emissives; 62 | for (int i = 0; i < controlledEmissives.Count; ++i) 63 | { 64 | controlledEmissives[i].Color = OutputColor; 65 | } 66 | } 67 | } 68 | 69 | public string Identifier 70 | { 71 | get 72 | { 73 | return ColorSourceID; 74 | } 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /src/ColorSourceArray.cs: -------------------------------------------------------------------------------- 1 | namespace IndicatorLights 2 | { 3 | /// 4 | /// Lazy-loading placeholder for an array of color sources, useful for emissive 5 | /// controllers that work with arrays of meshes. 6 | /// 7 | internal class ColorSourceArray 8 | { 9 | private readonly string colorSourceID; 10 | private IColorSource[] sources = null; 11 | private bool isValid = false; 12 | private bool isInitialized = false; 13 | 14 | private ColorSourceArray(string colorSourceID) 15 | { 16 | this.colorSourceID = colorSourceID; 17 | } 18 | 19 | /// 20 | /// Get a ColorSourceArray for the specified ID. 21 | /// 22 | /// 23 | /// 24 | public static ColorSourceArray of(string colorSourceID) 25 | { 26 | return new ColorSourceArray(colorSourceID); 27 | } 28 | 29 | /// 30 | /// Try to set the colors of the meshes on the specified controller. 31 | /// 32 | /// 33 | public void SetColors(ModuleEmissiveControllerBase controller) 34 | { 35 | if (!TryInitialize(controller)) return; 36 | 37 | int sourceIndex = 0; 38 | for (int emissiveIndex = 0; emissiveIndex < controller.Emissives.Count; ++emissiveIndex) 39 | { 40 | ModuleControllableEmissive emissive = controller.Emissives[emissiveIndex]; 41 | for (int meshIndex = 0; meshIndex < emissive.Count; ++meshIndex) 42 | { 43 | if (sourceIndex >= sources.Length) return; 44 | IColorSource source = sources[sourceIndex++]; 45 | if (source.HasColor) emissive.SetColorAt(source.OutputColor, meshIndex); 46 | } 47 | } 48 | } 49 | 50 | private bool TryInitialize(ModuleEmissiveControllerBase controller) 51 | { 52 | if (isInitialized) return isValid; 53 | if ((controller == null) || (controller.Emissives == null) || (controller.Emissives.Count == 0)) return DoneInitializing(false); 54 | int total = 0; 55 | for (int emissiveIndex = 0; emissiveIndex < controller.Emissives.Count; ++emissiveIndex) 56 | { 57 | int count = controller.Emissives[emissiveIndex].Count; 58 | if (count < 0) return false; // not yet initialized, we'll try again later 59 | total += count; 60 | } 61 | if (total < 1) return DoneInitializing(false); 62 | 63 | sources = ColorSources.FindWithIndex(controller, colorSourceID, total); 64 | return DoneInitializing(true); 65 | } 66 | 67 | private bool DoneInitializing(bool success) 68 | { 69 | isValid = success; 70 | isInitialized = true; 71 | return isValid; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /files/Parts/science/scienceJr.cfg: -------------------------------------------------------------------------------- 1 | @PART[science_module]:FOR[IndicatorLights] 2 | { 3 | // We have to re-specify the model for the stock part, because this is 4 | // an older part that uses the "mesh =" syntax in its .cfg file instead 5 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 6 | // multiple models as part of the same part, which would prevent this mod 7 | // from adding meshes for the indicator lights. 8 | MODEL 9 | { 10 | model = Squad/Parts/Science/MaterialBay/science_module_small 11 | } 12 | 13 | 14 | //------------------------------------------------------------------------- 15 | // INDICATOR MESHES 16 | //------------------------------------------------------------------------- 17 | 18 | MODEL 19 | { 20 | model = IndicatorLights/Meshes/squareLamp 21 | scale = 1, 0.4, 0.5 22 | position = 0, -0.34833, -0.49839 23 | rotation = 0, 180, 0 24 | } 25 | 26 | MODEL 27 | { 28 | model = IndicatorLights/Meshes/squareLamp 29 | scale = 1, 0.4, 0.5 30 | position = 0, -0.34833, 0.49839 31 | rotation = 0, 0, 0 32 | } 33 | 34 | 35 | //------------------------------------------------------------------------- 36 | // CONTROLLABLE EMISSIVES 37 | //------------------------------------------------------------------------- 38 | 39 | // The add-on meshes that we use to show science content, availability, etc. 40 | MODULE { 41 | name = ModuleControllableEmissive 42 | target = IndicatorLights/Meshes/squareLamp 43 | emissiveName = indicator 44 | } 45 | 46 | // The bluish-glowing center part (from the original model) 47 | MODULE { 48 | name = ModuleControllableEmissive 49 | target = Glass 50 | emissiveName = frontPanel 51 | } 52 | 53 | // The orangey-glowing side parts (from the original model) 54 | MODULE { 55 | name = ModuleControllableEmissive 56 | target = DoorAssembly 57 | emissiveName = sidePanels 58 | } 59 | 60 | 61 | //------------------------------------------------------------------------- 62 | // CONTROLLERS 63 | //------------------------------------------------------------------------- 64 | 65 | // These first two controllers take care of the add-on meshes. 66 | MODULE { 67 | name = ModuleScienceDataIndicator 68 | emissiveName = indicator 69 | dataColor = $HighScience 70 | partialDataColor = $MediumScience 71 | lowDataColor = $LowScience 72 | emptyColor = ModuleScienceAvailabilityIndicator 73 | lowScienceThreshold = 0.25 74 | highScienceThreshold = 0.8 75 | } 76 | 77 | MODULE { 78 | name = ModuleScienceAvailabilityIndicator 79 | lowScienceThreshold = 0.25 80 | highScienceThreshold = 0.8 81 | } 82 | 83 | // Make the blue glowy part pulsate slowly when it has science. 84 | MODULE { 85 | name = ModuleScienceDataIndicator 86 | emissiveName = frontPanel 87 | dataColor = pulsate(#00FFFF, 2800, 1, 0.4) 88 | emptyColor = #004040 89 | } 90 | 91 | // Make the orange glowy part pulsate quickly when it has science. 92 | MODULE { 93 | name = ModuleScienceDataIndicator 94 | emissiveName = sidePanels 95 | dataColor = pulsate(#FF8000, 777, 1, 0.7) 96 | emptyColor = #804500 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /files/Parts/dockingPorts/mediumPort.cfg: -------------------------------------------------------------------------------- 1 | // Adds LED indicators to the medium docking port. 2 | 3 | @PART[dockingPort2]:FOR[IndicatorLights] { 4 | @description ^= :(.)$:$0 Indicator lights display crossfeed status.: 5 | 6 | // We have to re-specify the model for the stock part, because this is 7 | // an older part that uses the "mesh =" syntax in its .cfg file instead 8 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 9 | // multiple models as part of the same part, which would prevent this mod 10 | // from adding meshes for the indicator lights. 11 | MODEL 12 | { 13 | model = Squad/Parts/Utility/dockingPort/dockingPort 14 | } 15 | 16 | //------------------------------------------------------------------------- 17 | // INDICATOR MESHES 18 | //------------------------------------------------------------------------- 19 | MODEL 20 | { 21 | model = IndicatorLights/Meshes/squareLamp2 22 | scale = 1, 1, 0.3 23 | position = -0.5331, 0.10, 0 24 | rotation = 0, 0, 20 25 | } 26 | 27 | MODEL 28 | { 29 | model = IndicatorLights/Meshes/squareLamp2 30 | scale = 1, 1, 0.3 31 | position = 0.5331, 0.10, 0 32 | rotation = 0, 0, -20 33 | } 34 | 35 | MODEL 36 | { 37 | model = IndicatorLights/Meshes/squareLamp2 38 | scale = 0.3, 1, 1 39 | position = 0, 0.08, -0.5331 40 | rotation = -20, 0, 0 41 | } 42 | 43 | MODEL 44 | { 45 | model = IndicatorLights/Meshes/squareLamp2 46 | scale = 0.3, 1, 1 47 | position = 0, 0.08, 0.5331 48 | rotation = 20, 0, 0 49 | } 50 | 51 | 52 | //------------------------------------------------------------------------- 53 | // CONTROLLABLE EMISSIVES 54 | //------------------------------------------------------------------------- 55 | MODULE { 56 | name = ModuleControllableEmissive 57 | target = IndicatorLights/Meshes/squareLamp2 58 | emissiveName = indicator 59 | } 60 | 61 | 62 | //------------------------------------------------------------------------- 63 | // CONTROLLERS 64 | //------------------------------------------------------------------------- 65 | 66 | // Controls the light manually. 67 | MODULE { 68 | name = ModuleToggleLED 69 | activeColor = ModuleDockingCrossfeedIndicator 70 | inactiveColor = $Off 71 | defaultActionGroup = Light 72 | } 73 | 74 | // This provides detection of the docking port state. We don't specify an 75 | // emissiveName here because it's not controlling the light directly (we're 76 | // just using it as an input to other modules). 77 | MODULE { 78 | name = ModuleDockingCrossfeedIndicator 79 | } 80 | 81 | // This detects the docking state. We make this the "root" controller that 82 | // actually drives the emissive color, because we want the indicator to blink 83 | // when it's acquiring or disengaging, regardless of the toggle setting. 84 | // If we're in the "ready" (or docked) state, then we'll take our input 85 | // from the toggle. 86 | MODULE { 87 | name = ModuleDockingStateIndicator 88 | emissiveName = indicator 89 | readyColor = ModuleToggleLED 90 | acquireColor = blink(ModuleDockingCrossfeedIndicator, 100, $Off, 100) 91 | disengageColor = blink(ModuleDockingCrossfeedIndicator, 120, $Off, 1080) 92 | } 93 | } -------------------------------------------------------------------------------- /files/Parts/antennas/88-88.cfg: -------------------------------------------------------------------------------- 1 | // Adds an indicator to the 88-88 dish antenna. 2 | 3 | @PART[commDish]:FOR[IndicatorLights] { 4 | 5 | // We have to re-specify the model for the stock part, because this is 6 | // an older part that uses the "mesh =" syntax in its .cfg file instead 7 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 8 | // multiple models as part of the same part, which would prevent this mod 9 | // from adding meshes for the indicator lights. 10 | MODEL 11 | { 12 | model = Squad/Parts/Utility/commDish88-88/model 13 | } 14 | 15 | //------------------------------------------------------------------------- 16 | // INDICATOR MESHES 17 | //------------------------------------------------------------------------- 18 | 19 | MODEL 20 | { 21 | model = IndicatorLights/Meshes/nubbinLamp 22 | position = 0, -0.143, 0 23 | scale = 5.7, 5.7, 6 24 | rotation = -90, 0, 0 25 | } 26 | 27 | 28 | //------------------------------------------------------------------------- 29 | // CONTROLLABLE EMISSIVES 30 | //------------------------------------------------------------------------- 31 | 32 | MODULE { 33 | name = ModuleControllableEmissive 34 | target = IndicatorLights/Meshes/nubbinLamp 35 | emissiveName = indicator 36 | } 37 | 38 | 39 | //------------------------------------------------------------------------- 40 | // CONTROLLERS 41 | //------------------------------------------------------------------------- 42 | 43 | MODULE { 44 | name = ModuleDataTransmitterIndicator 45 | emissiveName = indicator 46 | // Get really fancy with busy color. This module type exposes one static 47 | // field, dataRate. We'll use a random flicker for the animation (like 48 | // an old-time modem), and we'll plug the data rate both into the flicker 49 | // period (so higher-speed antennas flicker faster), and into the bias 50 | // (so higher-speed antennas spend a greater percentage of their time 51 | // in the "on" state rather than "off"). 52 | // 53 | // We don't actually have to use the static syntax here (we could just 54 | // take the data rate for this antenna, do the math, and plug in the 55 | // literal numbers here). However, using the actual static expression 56 | // comes with a few benefits. First, it's more maintainable: we 57 | // can just use the same static expression for all the antennas, rather 58 | // than having to come up with a different expression with different math 59 | // for each one. Second, it's more explicit to anyone reading this just 60 | // *why* the number is what it is. Third, it's more robust: if Squad ever 61 | // tinkers with the data rates, or someone uses ModuleManager to tweak 62 | // them, this will automatically follow suit. Fourth, it's friendlier 63 | // to modders: if you have your own antenna and you want to set it up 64 | // for IndicatorLights compatibility, you can just copy the following 65 | // line verbatim and you'll get behavior that's consistent with what 66 | // IndicatorLights does with the stock antennas. 67 | busyColor = random($ToggleLED, $Off, divide(400, static(dataRate)), between(subtract(multiply(sqrt(static(dataRate)), 1.1), 2.3), -0.4, 0.5)) 68 | } 69 | } -------------------------------------------------------------------------------- /files/Parts/antennas/communotron16.cfg: -------------------------------------------------------------------------------- 1 | // Adds an indicator to the Communotron-16 antenna. 2 | 3 | @PART[longAntenna]:FOR[IndicatorLights] { 4 | 5 | // We have to re-specify the model for the stock part, because this is 6 | // an older part that uses the "mesh =" syntax in its .cfg file instead 7 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 8 | // multiple models as part of the same part, which would prevent this mod 9 | // from adding meshes for the indicator lights. 10 | MODEL 11 | { 12 | model = Squad/Parts/Utility/commsDish16/model 13 | } 14 | 15 | //------------------------------------------------------------------------- 16 | // INDICATOR MESHES 17 | //------------------------------------------------------------------------- 18 | 19 | MODEL 20 | { 21 | model = IndicatorLights/Meshes/nubbinLamp 22 | position = 0, 0, 0 23 | scale = 1.6, 1.6, 2.5 24 | rotation = -90, 0, 0 25 | } 26 | 27 | 28 | //------------------------------------------------------------------------- 29 | // CONTROLLABLE EMISSIVES 30 | //------------------------------------------------------------------------- 31 | 32 | MODULE { 33 | name = ModuleControllableEmissive 34 | target = IndicatorLights/Meshes/nubbinLamp 35 | emissiveName = indicator 36 | } 37 | 38 | 39 | //------------------------------------------------------------------------- 40 | // CONTROLLERS 41 | //------------------------------------------------------------------------- 42 | 43 | MODULE { 44 | name = ModuleDataTransmitterIndicator 45 | emissiveName = indicator 46 | // Get really fancy with busy color. This module type exposes one static 47 | // field, dataRate. We'll use a random flicker for the animation (like 48 | // an old-time modem), and we'll plug the data rate both into the flicker 49 | // period (so higher-speed antennas flicker faster), and into the bias 50 | // (so higher-speed antennas spend a greater percentage of their time 51 | // in the "on" state rather than "off"). 52 | // 53 | // We don't actually have to use the static syntax here (we could just 54 | // take the data rate for this antenna, do the math, and plug in the 55 | // literal numbers here). However, using the actual static expression 56 | // comes with a few benefits. First, it's more maintainable: we 57 | // can just use the same static expression for all the antennas, rather 58 | // than having to come up with a different expression with different math 59 | // for each one. Second, it's more explicit to anyone reading this just 60 | // *why* the number is what it is. Third, it's more robust: if Squad ever 61 | // tinkers with the data rates, or someone uses ModuleManager to tweak 62 | // them, this will automatically follow suit. Fourth, it's friendlier 63 | // to modders: if you have your own antenna and you want to set it up 64 | // for IndicatorLights compatibility, you can just copy the following 65 | // line verbatim and you'll get behavior that's consistent with what 66 | // IndicatorLights does with the stock antennas. 67 | busyColor = random($ToggleLED, $Off, divide(400, static(dataRate)), between(subtract(multiply(sqrt(static(dataRate)), 1.1), 2.3), -0.4, 0.5)) 68 | } 69 | } -------------------------------------------------------------------------------- /files/Parts/antennas/dtsM1.cfg: -------------------------------------------------------------------------------- 1 | // Adds an indicator to the DTS-M1 antenna. 2 | 3 | @PART[mediumDishAntenna]:FOR[IndicatorLights] { 4 | 5 | // We have to re-specify the model for the stock part, because this is 6 | // an older part that uses the "mesh =" syntax in its .cfg file instead 7 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 8 | // multiple models as part of the same part, which would prevent this mod 9 | // from adding meshes for the indicator lights. 10 | MODEL 11 | { 12 | model = Squad/Parts/Utility/commsAntennaDTS-M1/mediumDishAntenna 13 | } 14 | 15 | //------------------------------------------------------------------------- 16 | // INDICATOR MESHES 17 | //------------------------------------------------------------------------- 18 | 19 | MODEL 20 | { 21 | model = IndicatorLights/Meshes/squareLamp2 22 | position = 0.008, -0.0037, -0.4955 23 | scale = 1.8, 0.5, 0.5 24 | rotation = -40, 0, 0 25 | } 26 | 27 | 28 | //------------------------------------------------------------------------- 29 | // CONTROLLABLE EMISSIVES 30 | //------------------------------------------------------------------------- 31 | 32 | MODULE { 33 | name = ModuleControllableEmissive 34 | target = IndicatorLights/Meshes/squareLamp2 35 | emissiveName = indicator 36 | } 37 | 38 | 39 | //------------------------------------------------------------------------- 40 | // CONTROLLERS 41 | //------------------------------------------------------------------------- 42 | 43 | MODULE { 44 | name = ModuleDataTransmitterIndicator 45 | emissiveName = indicator 46 | // Get really fancy with busy color. This module type exposes one static 47 | // field, dataRate. We'll use a random flicker for the animation (like 48 | // an old-time modem), and we'll plug the data rate both into the flicker 49 | // period (so higher-speed antennas flicker faster), and into the bias 50 | // (so higher-speed antennas spend a greater percentage of their time 51 | // in the "on" state rather than "off"). 52 | // 53 | // We don't actually have to use the static syntax here (we could just 54 | // take the data rate for this antenna, do the math, and plug in the 55 | // literal numbers here). However, using the actual static expression 56 | // comes with a few benefits. First, it's more maintainable: we 57 | // can just use the same static expression for all the antennas, rather 58 | // than having to come up with a different expression with different math 59 | // for each one. Second, it's more explicit to anyone reading this just 60 | // *why* the number is what it is. Third, it's more robust: if Squad ever 61 | // tinkers with the data rates, or someone uses ModuleManager to tweak 62 | // them, this will automatically follow suit. Fourth, it's friendlier 63 | // to modders: if you have your own antenna and you want to set it up 64 | // for IndicatorLights compatibility, you can just copy the following 65 | // line verbatim and you'll get behavior that's consistent with what 66 | // IndicatorLights does with the stock antennas. 67 | busyColor = random($ToggleLED, $Off, divide(400, static(dataRate)), between(subtract(multiply(sqrt(static(dataRate)), 1.1), 2.3), -0.4, 0.5)) 68 | } 69 | } -------------------------------------------------------------------------------- /src/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace IndicatorLights 5 | { 6 | /// 7 | /// Convenient place for putting extension logic that doesn't particularly have an 8 | /// obvious home elsewhere. 9 | /// 10 | internal static class Extensions 11 | { 12 | /// 13 | /// Gets whether the given UI_Scene value should be enabled in the flight scene. 14 | /// 15 | /// 16 | /// 17 | public static bool IsFlightEnabled(this UI_Scene scene) 18 | { 19 | switch (scene) 20 | { 21 | case UI_Scene.Editor: 22 | case UI_Scene.None: 23 | return false; 24 | default: 25 | return true; 26 | } 27 | } 28 | 29 | /// 30 | /// Gets whether the given UI_Scene value should be enabled in the flight scene. 31 | /// 32 | /// 33 | /// 34 | public static bool IsEditorEnabled(this UI_Scene scene) 35 | { 36 | switch (scene) 37 | { 38 | case UI_Scene.Flight: 39 | case UI_Scene.None: 40 | return false; 41 | default: 42 | return true; 43 | } 44 | } 45 | 46 | /// 47 | /// Get a sub-array of the provided array, starting at the specified index. 48 | /// 49 | /// 50 | /// 51 | /// 52 | /// 53 | public static T[] SubArray(this T[] array, int startIndex) 54 | { 55 | if (startIndex == 0) return array; 56 | if ((startIndex < 0) || (startIndex > array.Length)) 57 | { 58 | throw new ArgumentOutOfRangeException( 59 | "startIndex", 60 | startIndex, 61 | "Array index out of range"); 62 | } 63 | 64 | T[] result = new T[array.Length - startIndex]; 65 | for (int i = 0; i < result.Length; ++i) 66 | { 67 | result[i] = array[i + startIndex]; 68 | } 69 | return result; 70 | } 71 | 72 | /// 73 | /// Joins an array of strings into a single string, using the specified joiner. 74 | /// 75 | /// 76 | /// 77 | /// 78 | public static string Join(this string[] array, string joiner) 79 | { 80 | if (array.Length < 1) return string.Empty; 81 | if (array.Length < 2) return array[0]; 82 | StringBuilder builder = new StringBuilder(array[0]); 83 | for (int i = 1; i < array.Length; ++i) 84 | { 85 | builder.Append(joiner).Append(array[i]); 86 | } 87 | return builder.ToString(); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /files/Parts/crewable/scienceLab.cfg: -------------------------------------------------------------------------------- 1 | // Adds occupancy indicators to the large science lab. 2 | 3 | @PART[Large_Crewed_Lab]:FOR[IndicatorLights] { 4 | 5 | // We have to re-specify the model for the stock part, because this is 6 | // an older part that uses the "mesh =" syntax in its .cfg file instead 7 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 8 | // multiple models as part of the same part, which would prevent this mod 9 | // from adding meshes for the indicator lights. 10 | MODEL 11 | { 12 | model = Squad/Parts/Science/LargeCrewedLab/large_crewed_lab 13 | } 14 | 15 | //------------------------------------------------------------------------- 16 | // INDICATOR MESHES 17 | //------------------------------------------------------------------------- 18 | 19 | MODEL 20 | { 21 | model = IndicatorLights/Meshes/squareLamp 22 | scale = 1, 0.25, 0.5 23 | position = 0, -0.26, -1.274 24 | rotation = 0, 180, 0 25 | } 26 | 27 | MODEL 28 | { 29 | model = IndicatorLights/Meshes/squareLamp 30 | scale = 1, 0.25, 0.5 31 | position = 0, -0.26, 1.285 32 | rotation = 0, 0, 0 33 | } 34 | 35 | MODEL 36 | { 37 | model = IndicatorLights/Meshes/squareLamp 38 | scale = 1, 0.25, 0.5 39 | position = 0, 0.26, -1.274 40 | rotation = 0, 180, 0 41 | } 42 | 43 | MODEL 44 | { 45 | model = IndicatorLights/Meshes/squareLamp 46 | scale = 1, 0.25, 0.5 47 | position = 0, 0.26, 1.285 48 | rotation = 0, 0, 0 49 | } 50 | 51 | //------------------------------------------------------------------------- 52 | // CONTROLLABLE EMISSIVES 53 | //------------------------------------------------------------------------- 54 | 55 | MODULE { 56 | name = ModuleControllableEmissive 57 | target = IndicatorLights/Meshes/squareLamp:0,1 58 | emissiveName = indicator0 59 | } 60 | 61 | MODULE { 62 | name = ModuleControllableEmissive 63 | target = IndicatorLights/Meshes/squareLamp:2,3 64 | emissiveName = indicator1 65 | } 66 | 67 | //------------------------------------------------------------------------- 68 | // CONTROLLERS 69 | //------------------------------------------------------------------------- 70 | 71 | // Master switch that turns all the crew indicators on/off. 72 | MODULE { 73 | name = ModuleCrewIndicatorToggle 74 | toggleName = indicatorToggle 75 | } 76 | 77 | // Evaluates to true when research is happening. 78 | MODULE { 79 | name = ModuleConverterIndicator 80 | converterName = Research 81 | } 82 | 83 | // Indicator for slot 0. 84 | MODULE { 85 | name = ModuleCrewIndicator 86 | controllerName = crew0 87 | } 88 | MODULE { 89 | name = ModuleBooleanIndicator 90 | emissiveName = indicator0 91 | input = hasCrewEffect(ScienceSkill, 0) 92 | activeColor = if(ModuleConverterIndicator, pulsate(crew0, 600, 0.5), crew0) 93 | inactiveColor = blink(dim(crew0, 0.7), 150, $Off, 650) 94 | } 95 | 96 | // Indicator for slot 1. 97 | MODULE { 98 | name = ModuleCrewIndicator 99 | controllerName = crew1 100 | } 101 | MODULE { 102 | name = ModuleBooleanIndicator 103 | emissiveName = indicator1 104 | input = hasCrewEffect(ScienceSkill, 1) 105 | activeColor = if(ModuleConverterIndicator, pulsate(crew1, 600, 0.5), crew1) 106 | inactiveColor = blink(dim(crew1, 0.7), 150, $Off, 650) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /files/Parts/crewable/mk2cockpit.cfg: -------------------------------------------------------------------------------- 1 | // Adds occupancy indicators to the Mk2 cockpit. 2 | 3 | @PART[mk2Cockpit_Standard]:FOR[IndicatorLights] { 4 | 5 | // We have to re-specify the model for the stock part, because this is 6 | // an older part that uses the "mesh =" syntax in its .cfg file instead 7 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 8 | // multiple models as part of the same part, which would prevent this mod 9 | // from adding meshes for the indicator lights. 10 | MODEL 11 | { 12 | model = Squad/Parts/Command/mk2CockpitStandard/model 13 | } 14 | 15 | //------------------------------------------------------------------------- 16 | // INDICATOR MESHES 17 | //------------------------------------------------------------------------- 18 | 19 | MODEL 20 | { 21 | model = IndicatorLights/Meshes/squareLamp 22 | scale = 1, 0.25, 0.5 23 | position = 0.7605, -0.9928, -0.44 24 | rotation = 0, 150, 0 25 | } 26 | 27 | MODEL 28 | { 29 | model = IndicatorLights/Meshes/squareLamp 30 | scale = 1, 0.25, 0.5 31 | position = 0.7605, -0.6728, -0.428 32 | rotation = 0, 150, 0 33 | } 34 | 35 | //------------------------------------------------------------------------- 36 | // CONTROLLABLE EMISSIVES 37 | //------------------------------------------------------------------------- 38 | 39 | MODULE { 40 | name = ModuleControllableEmissive 41 | target = IndicatorLights/Meshes/squareLamp:0 42 | emissiveName = indicator0 43 | } 44 | 45 | MODULE { 46 | name = ModuleControllableEmissive 47 | target = IndicatorLights/Meshes/squareLamp:1 48 | emissiveName = indicator1 49 | } 50 | 51 | //------------------------------------------------------------------------- 52 | // CONTROLLERS 53 | //------------------------------------------------------------------------- 54 | 55 | MODULE { 56 | name = ModuleCrewIndicatorToggle 57 | toggleName = indicatorToggle 58 | } 59 | 60 | MODULE { 61 | name = ModuleCrewIndicator 62 | controllerName = crewController0 63 | toggleName = indicatorToggle 64 | } 65 | 66 | MODULE { 67 | name = ModuleCrewIndicator 68 | controllerName = crewController1 69 | toggleName = indicatorToggle 70 | } 71 | 72 | MODULE 73 | { 74 | name = ModuleScienceAvailabilityIndicator 75 | controllerName = availability0 76 | experimentID = crewReport 77 | lowValueColor = crewController0 78 | mediumValueColor = highValueColor 79 | highValueColor = blink(lowValueColor, 200, $Off, 200) 80 | } 81 | 82 | MODULE 83 | { 84 | name = ModuleScienceAvailabilityIndicator 85 | controllerName = availability1 86 | experimentID = crewReport 87 | lowValueColor = crewController1 88 | mediumValueColor = highValueColor 89 | highValueColor = blink(lowValueColor, 200, $Off, 200) 90 | } 91 | 92 | MODULE { 93 | name = ModuleScienceDataIndicator 94 | experimentID = crewReport 95 | emissiveName = indicator0 96 | dataColor = crewController0 97 | emptyColor = availability0 98 | } 99 | 100 | MODULE { 101 | name = ModuleScienceDataIndicator 102 | experimentID = crewReport 103 | emissiveName = indicator1 104 | dataColor = crewController1 105 | emptyColor = availability1 106 | } 107 | } -------------------------------------------------------------------------------- /files/Parts/crewable/mk2cockpitInline.cfg: -------------------------------------------------------------------------------- 1 | // Adds occupancy indicators to the Mk2 inline cockpit. 2 | // Thanks to Dominiquini in the KSP forums for supplying the config! 3 | 4 | @PART[mk2Cockpit_Inline]:FOR[IndicatorLights] { 5 | 6 | // We have to re-specify the model for the stock part, because this is 7 | // an older part that uses the "mesh =" syntax in its .cfg file instead 8 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 9 | // multiple models as part of the same part, which would prevent this mod 10 | // from adding meshes for the indicator lights. 11 | MODEL 12 | { 13 | model = Squad/Parts/Command/mk2CockpitInline/model 14 | } 15 | 16 | //------------------------------------------------------------------------- 17 | // INDICATOR MESHES 18 | //------------------------------------------------------------------------- 19 | MODEL 20 | { 21 | model = IndicatorLights/Meshes/nubbinLamp 22 | scale = 0.65, 0.65, 0.5 23 | position = 0, 0.551, -1.086 24 | rotation = -10, 180, 0 25 | } 26 | 27 | MODEL 28 | { 29 | model = IndicatorLights/Meshes/nubbinLamp 30 | scale = 0.65, 0.65, 0.5 31 | position = 0, 0.471, -1.101 32 | rotation = -5, 180, 0 33 | } 34 | 35 | //------------------------------------------------------------------------- 36 | // CONTROLLABLE EMISSIVES 37 | //------------------------------------------------------------------------- 38 | MODULE { 39 | name = ModuleControllableEmissive 40 | target = IndicatorLights/Meshes/nubbinLamp:0 41 | emissiveName = indicator0 42 | } 43 | 44 | MODULE { 45 | name = ModuleControllableEmissive 46 | target = IndicatorLights/Meshes/nubbinLamp:1 47 | emissiveName = indicator1 48 | } 49 | 50 | //------------------------------------------------------------------------- 51 | // CONTROLLERS 52 | //------------------------------------------------------------------------- 53 | MODULE { 54 | name = ModuleCrewIndicatorToggle 55 | toggleName = indicatorToggle 56 | } 57 | 58 | MODULE { 59 | name = ModuleCrewIndicator 60 | controllerName = crewController0 61 | toggleName = indicatorToggle 62 | } 63 | 64 | MODULE { 65 | name = ModuleCrewIndicator 66 | controllerName = crewController1 67 | toggleName = indicatorToggle 68 | } 69 | 70 | MODULE 71 | { 72 | name = ModuleScienceAvailabilityIndicator 73 | controllerName = availability0 74 | experimentID = crewReport 75 | lowValueColor = crewController0 76 | mediumValueColor = highValueColor 77 | highValueColor = blink(lowValueColor, 200, $Off, 200) 78 | } 79 | 80 | MODULE 81 | { 82 | name = ModuleScienceAvailabilityIndicator 83 | controllerName = availability1 84 | experimentID = crewReport 85 | lowValueColor = crewController1 86 | mediumValueColor = highValueColor 87 | highValueColor = blink(lowValueColor, 200, $Off, 200) 88 | } 89 | 90 | MODULE { 91 | name = ModuleScienceDataIndicator 92 | experimentID = crewReport 93 | emissiveName = indicator0 94 | dataColor = crewController0 95 | emptyColor = availability0 96 | } 97 | 98 | MODULE { 99 | name = ModuleScienceDataIndicator 100 | experimentID = crewReport 101 | emissiveName = indicator1 102 | dataColor = crewController1 103 | emptyColor = availability1 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/ModuleResourceIndicator.cs: -------------------------------------------------------------------------------- 1 | namespace IndicatorLights 2 | { 3 | /// 4 | /// Base class for modules that work with resources. 5 | /// 6 | abstract class ModuleResourceIndicator : ModuleEmissiveController 7 | { 8 | private PartResource resource = null; 9 | 10 | /// 11 | /// Determines which part is searched for the specified resource. 12 | /// 13 | [KSPField] 14 | public PartSearchStrategy searchStrategy = PartSearchStrategies.Default; 15 | 16 | /// 17 | /// The name of the resource which this controller tracks. If left null, the controller will 18 | /// pick the first resource it finds. 19 | /// 20 | [KSPField] 21 | public string resourceName = null; 22 | 23 | /// 24 | /// Called when the module is starting up. 25 | /// 26 | /// 27 | public override void OnStart(StartState state) 28 | { 29 | base.OnStart(state); 30 | 31 | resource = FindResource(); 32 | if ((resource == null) && (searchStrategy == PartSearchStrategy.host)) 33 | { 34 | Logging.Warn("ModuleResourceIndicator is inactive"); 35 | return; 36 | } 37 | } 38 | 39 | protected PartResource Resource 40 | { 41 | get 42 | { 43 | if (searchStrategy != PartSearchStrategy.host) 44 | { 45 | Part sourcePart = (resource == null) ? null : resource.part; 46 | if (!searchStrategy.IsChoice(this, sourcePart)) 47 | { 48 | resource = FindResource(); 49 | } 50 | } 51 | return resource; 52 | } 53 | } 54 | 55 | /// 56 | /// Picks a resource to track. 57 | /// 58 | /// 59 | private PartResource FindResource() 60 | { 61 | Part sourcePart = searchStrategy.ChoosePart(this); 62 | if (sourcePart == null) return null; 63 | if ((sourcePart.Resources == null) || (sourcePart.Resources.Count == 0)) 64 | { 65 | Logging.Warn(sourcePart.GetTitle() + " has no resources, can't track"); 66 | return null; 67 | } 68 | if ((resourceName == null) || (resourceName.Length == 0)) 69 | { 70 | if (sourcePart.Resources.Count > 1) 71 | { 72 | Logging.Log(sourcePart.GetTitle() + " has multiple resources; indicator is defaulting to " + sourcePart.Resources[0].resourceName); 73 | } 74 | return sourcePart.Resources[0]; 75 | } 76 | for (int i = 0; i < sourcePart.Resources.Count; ++i) 77 | { 78 | PartResource resource = sourcePart.Resources[i]; 79 | if (resourceName.Equals(resource.resourceName) && (resource.maxAmount > 0)) 80 | { 81 | return resource; 82 | } 83 | } 84 | Logging.Warn("No resource '" + resourceName + "' found in " + sourcePart.GetTitle() + ", can't track"); 85 | return null; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /files/Parts/antennas/hg5.cfg: -------------------------------------------------------------------------------- 1 | // Adds an indicator to the HG-5 antenna. 2 | 3 | @PART[HighGainAntenna5_v2]:FOR[IndicatorLights] { 4 | 5 | //------------------------------------------------------------------------- 6 | // INDICATOR MESHES 7 | //------------------------------------------------------------------------- 8 | 9 | MODEL 10 | { 11 | model = IndicatorLights/Meshes/squareLamp2 12 | position = 0, 0.05, 0.1 13 | scale = 1.05, 0.7, 0.8 14 | rotation = 90, 0, 0 15 | } 16 | 17 | 18 | //------------------------------------------------------------------------- 19 | // CONTROLLABLE EMISSIVES 20 | //------------------------------------------------------------------------- 21 | 22 | MODULE { 23 | name = ModuleControllableEmissive 24 | target = IndicatorLights/Meshes/squareLamp2 25 | emissiveName = indicator 26 | } 27 | 28 | 29 | //------------------------------------------------------------------------- 30 | // CONTROLLERS 31 | //------------------------------------------------------------------------- 32 | 33 | MODULE { 34 | name = ModuleDataTransmitterIndicator 35 | emissiveName = indicator 36 | // Get really fancy with busy color. This module type exposes one static 37 | // field, dataRate. We'll use a random flicker for the animation (like 38 | // an old-time modem), and we'll plug the data rate both into the flicker 39 | // period (so higher-speed antennas flicker faster), and into the bias 40 | // (so higher-speed antennas spend a greater percentage of their time 41 | // in the "on" state rather than "off"). 42 | // 43 | // We don't actually have to use the static syntax here (we could just 44 | // take the data rate for this antenna, do the math, and plug in the 45 | // literal numbers here). However, using the actual static expression 46 | // comes with a few benefits. First, it's more maintainable: we 47 | // can just use the same static expression for all the antennas, rather 48 | // than having to come up with a different expression with different math 49 | // for each one. Second, it's more explicit to anyone reading this just 50 | // *why* the number is what it is. Third, it's more robust: if Squad ever 51 | // tinkers with the data rates, or someone uses ModuleManager to tweak 52 | // them, this will automatically follow suit. Fourth, it's friendlier 53 | // to modders: if you have your own antenna and you want to set it up 54 | // for IndicatorLights compatibility, you can just copy the following 55 | // line verbatim and you'll get behavior that's consistent with what 56 | // IndicatorLights does with the stock antennas. 57 | busyColor = random($ToggleLED, $Off, divide(400, static(dataRate)), between(subtract(multiply(sqrt(static(dataRate)), 1.1), 2.3), -0.4, 0.5)) 58 | } 59 | } 60 | 61 | 62 | //---------------------------------------------------------------------------- 63 | // Legacy HG-5 model from older KSP versions 64 | @PART[HighGainAntenna5]:FOR[IndicatorLights] { 65 | MODEL 66 | { 67 | model = IndicatorLights/Meshes/squareLamp2 68 | position = -0.162, 0.036, -0.007 69 | scale = 1, 0.5, 1.15 70 | rotation = 0, 0, 90 71 | } 72 | 73 | MODULE { 74 | name = ModuleControllableEmissive 75 | target = IndicatorLights/Meshes/squareLamp2 76 | emissiveName = indicator 77 | } 78 | 79 | MODULE { 80 | name = ModuleDataTransmitterIndicator 81 | emissiveName = indicator 82 | busyColor = random($ToggleLED, $Off, divide(400, static(dataRate)), between(subtract(multiply(sqrt(static(dataRate)), 1.1), 2.3), -0.4, 0.5)) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /examples/ArbitraryFieldInputs.cfg: -------------------------------------------------------------------------------- 1 | // Demonstrate the use of field@module syntax. 2 | // 3 | // This example adds a light to the TR-2L Ruggedized Vehicular Wheel to show 4 | // its motor status. To do this, it works with three fields from ModuleWheelMotor: 5 | // 6 | // - motorEnabled ("is the motor turned on") 7 | // - motorInverted ("is the motor set up in reverse mode") 8 | // - driveOutput ("how much power is the motor currently putting out") 9 | // 10 | // The wheel is handy for demonstrating the usefulness of this syntax because 11 | // there aren't any IndicatorLights controllers that are specifically designed 12 | // to work with wheels. Therefore, to work with fields such as the above requires 13 | // the ability to target arbitrary fields, which is what the field@module syntax 14 | // is all about. Search this file for the "@" character to see how it's used. 15 | 16 | 17 | @PART[wheelMed] { 18 | @description ^= :(.)$:$0 Example wheel has indicator light showing motor status.: 19 | 20 | // We have to re-specify the model for the stock part, because this is 21 | // an older part that uses the "mesh =" syntax in its .cfg file instead 22 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 23 | // multiple models as part of the same part, which would prevent this mod 24 | // from adding meshes for the indicator lights. 25 | MODEL 26 | { 27 | model = Squad/Parts/Wheel/roverWheelTR-2L/model 28 | } 29 | 30 | 31 | //------------------------------------------------------------------------- 32 | // INDICATOR MESHES 33 | //------------------------------------------------------------------------- 34 | 35 | MODEL 36 | { 37 | model = IndicatorLights/Meshes/squareLamp2 38 | position = -0.194, 0.16, 0 39 | } 40 | 41 | 42 | //------------------------------------------------------------------------- 43 | // CONTROLLABLE EMISSIVES 44 | //------------------------------------------------------------------------- 45 | 46 | MODULE { 47 | name = ModuleControllableEmissive 48 | target = IndicatorLights/Meshes/squareLamp2 49 | emissiveName = indicator 50 | } 51 | 52 | 53 | //------------------------------------------------------------------------- 54 | // CONTROLLERS 55 | //------------------------------------------------------------------------- 56 | 57 | // A toggle that's on when the motor's active, off when it's not. When it's 58 | // on, emit green if it's in "normal" mode, red if the motor is inverted. 59 | // If motor output is under 60%, it's displayed dim. 60 | MODULE { 61 | name = ModuleBooleanIndicator 62 | controllerName = motorStatus 63 | // The input (i.e. deciding on versus off) is based on whether the 64 | // motor is enabled. 65 | input = motorEnabled@ModuleWheelMotor 66 | // When it's enabled, the color will be green or red depending on whether 67 | // it's in "forward" or "reverse" mode. 68 | activeColor = if(motorInverted@ModuleWheelMotor, #FF0000, #00FF00) 69 | // When the motor is not enabled, the light is simply off. 70 | inactiveColor = $Off 71 | } 72 | 73 | // Take the output of the toggles above, then adjust the brightness based 74 | // on the motor's drive output. 75 | MODULE { 76 | name = ModuleBooleanIndicator 77 | controllerName = motorPower 78 | input = motorStatus 79 | emissiveName = indicator 80 | // If the drive output of the motor is below 30%, display at half brightness. 81 | // Otherwise, display at full brightness. 82 | activeColor = if(lt(driveOutput@ModuleWheelMotor, 30), dim(motorStatus, 0.5), motorStatus) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /files/Parts/batteries/Z4KIndicator.cfg: -------------------------------------------------------------------------------- 1 | // Adds LED indicators to the large, 4000 EC battery. 2 | 3 | @PART[batteryBankLarge]:FOR[IndicatorLights] { 4 | @description ^= :(.)$:$0 New, improved model now has status lights!: 5 | 6 | // We have to re-specify the model for the stock part, because this is 7 | // an older part that uses the "mesh =" syntax in its .cfg file instead 8 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 9 | // multiple models as part of the same part, which would prevent this mod 10 | // from adding meshes for the indicator lights. 11 | MODEL 12 | { 13 | model = Squad/Parts/Electrical/z-4kBattery/model 14 | } 15 | 16 | //------------------------------------------------------------------------- 17 | // INDICATOR MESHES 18 | //------------------------------------------------------------------------- 19 | 20 | MODEL 21 | { 22 | model = IndicatorLights/Meshes/squareLamp 23 | scale = 0.8, 0.8, 1 24 | position = -1.082, -0.02, 0.625 25 | rotation = 0, -60, 0 26 | } 27 | 28 | MODEL 29 | { 30 | model = IndicatorLights/Meshes/squareLamp 31 | scale = 0.8, 0.8, 1 32 | position = 1.082, -0.02, -0.625 33 | rotation = 0, 120, 0 34 | } 35 | 36 | MODEL 37 | { 38 | model = IndicatorLights/Meshes/squareLamp 39 | scale = 0.8, 0.8, 1 40 | position = 1.082, -0.02, 0.625 41 | rotation = 0, 60, 0 42 | } 43 | 44 | MODEL 45 | { 46 | model = IndicatorLights/Meshes/squareLamp 47 | scale = 0.79, 0.8, 1 48 | position = -1.082, -0.02, -0.625 49 | rotation = 0, 240, 0 50 | } 51 | 52 | // This one covers up an apparent (dummy) green-LED "light" that's present 53 | // on the stock model, and can confuse the player by appearing to be an 54 | // indicator when it isn't. 55 | MODEL 56 | { 57 | model = IndicatorLights/Meshes/squareLamp 58 | scale = 1.21, 0.62, 0.1 59 | position = -0.08, 0.016, -1.177 60 | rotation = 0, 180, 0 61 | } 62 | 63 | 64 | //------------------------------------------------------------------------- 65 | // CONTROLLABLE EMISSIVES 66 | //------------------------------------------------------------------------- 67 | 68 | MODULE { 69 | name = ModuleControllableEmissive 70 | target = IndicatorLights/Meshes/squareLamp:0,1,2,3 71 | emissiveName = indicator 72 | } 73 | 74 | MODULE { 75 | name = ModuleControllableEmissive 76 | target = IndicatorLights/Meshes/squareLamp:4 77 | emissiveName = patch 78 | } 79 | 80 | //------------------------------------------------------------------------- 81 | // CONTROLLERS 82 | //------------------------------------------------------------------------- 83 | 84 | MODULE { 85 | name = ModuleResourceLevelIndicator 86 | controllerName = mainResourceLevel 87 | } 88 | 89 | MODULE { 90 | name = ModuleResourceEnabledIndicator 91 | enabledColor = mainResourceLevel 92 | disabledColor = blink(ModuleResourceLevelIndicator, 900, $Off, 300) 93 | emissiveName = indicator 94 | } 95 | 96 | // This is for the patch that covers up the "dummy" LED on the stock model. 97 | // It doesn't really fit with the design of the rest of the indicators 98 | // we're adding here, so it'll be off most of the time. Light it up dimly 99 | // when we're out of juice. 100 | MODULE { 101 | name = ModuleResourceLevelIndicator 102 | emissiveName = patch 103 | highColor = $Off 104 | mediumColor = $Off 105 | lowColor = $Off 106 | criticalColor = $Off 107 | emptyColor = dim($LowResource, 0.5) 108 | } 109 | } -------------------------------------------------------------------------------- /files/Parts/obsolete/mk1pod.cfg: -------------------------------------------------------------------------------- 1 | // Adds an occupancy indicator to the Mk1 command pod. 2 | 3 | @PART[mk1pod]:FOR[IndicatorLights] { 4 | 5 | // We have to re-specify the model for the stock part, because this is 6 | // an older part that uses the "mesh =" syntax in its .cfg file instead 7 | // of the newer "MODEL" syntax. The "mesh =" syntax doesn't allow having 8 | // multiple models as part of the same part, which would prevent this mod 9 | // from adding meshes for the indicator lights. 10 | MODEL 11 | { 12 | model = Squad/Parts/Command/mk1pod/model 13 | } 14 | 15 | //------------------------------------------------------------------------- 16 | // INDICATOR MESHES 17 | //------------------------------------------------------------------------- 18 | 19 | MODEL 20 | { 21 | model = IndicatorLights/Meshes/squareLamp 22 | scale = 1, 0.25, 0.5 23 | position = 0, 0, -0.5138 24 | rotation = -160, 0, 0 25 | } 26 | 27 | //------------------------------------------------------------------------- 28 | // CONTROLLABLE EMISSIVES 29 | //------------------------------------------------------------------------- 30 | 31 | MODULE { 32 | name = ModuleControllableEmissive 33 | target = IndicatorLights/Meshes/squareLamp 34 | emissiveName = indicator 35 | } 36 | 37 | //------------------------------------------------------------------------- 38 | // CONTROLLERS 39 | //------------------------------------------------------------------------- 40 | 41 | MODULE { 42 | name = ModuleCrewIndicatorToggle 43 | toggleName = indicatorToggle 44 | } 45 | 46 | MODULE { 47 | name = ModuleCrewIndicator 48 | toggleName = indicatorToggle 49 | } 50 | 51 | MODULE 52 | { 53 | name = ModuleScienceAvailabilityIndicator 54 | experimentID = crewReport 55 | lowValueColor = ModuleCrewIndicator 56 | mediumValueColor = highValueColor 57 | highValueColor = blink(lowValueColor, 200, $Off, 200) 58 | } 59 | 60 | MODULE { 61 | name = ModuleScienceDataIndicator 62 | experimentID = crewReport 63 | emissiveName = indicator 64 | dataColor = ModuleCrewIndicator 65 | emptyColor = ModuleScienceAvailabilityIndicator 66 | } 67 | 68 | //------------------------------------------------------------------------- 69 | // TWEAKS 70 | //------------------------------------------------------------------------- 71 | 72 | // This pod uses a ModuleColorChanger to handle its cabin lights, set up 73 | // so that by default it takes control of *all* emissives on the part. 74 | // Need to tell it not to tinker with the meshes that we're adding here; 75 | // otherwise, the crew indicator will just turn on and off with the cabin 76 | // lights instead of actually working as a crew indicator. 77 | @MODULE[ModuleColorChanger] { 78 | excludedRenderer = IndicatorLights/Meshes/squareLamp(Clone) 79 | 80 | // As long as we're tinkering here anyay... let's fix a 1.2 bug. :-) 81 | // ModuleColorChanger breaks the thermal overlay for the crewed parts 82 | // that use it for cabin lights. We can fix this by telling it 83 | // to ignore various meshes *other* than the window. Can remove this 84 | // hack once Squad gets around to fixing ModuleColorChanger. In the 85 | // meantime, this makes it better, and I can enjoy waiting to see whether 86 | // anyone actually notices! 87 | excludedRenderer = capsule 88 | excludedRenderer = hatch 89 | excludedRenderer = flagTransform 90 | excludedRenderer = rung 91 | // ...Can remove the hack once Squad fixes this part's config so it doesn't 92 | // break the thermal overlay. 93 | } 94 | } 95 | --------------------------------------------------------------------------------