├── mote.png ├── smoke.jpg ├── toolbar.png ├── Flags └── Kethane 1.png ├── AssetBundles ├── .gitignore ├── kethane.ksp └── Makefile ├── Plugin ├── Kethane │ ├── Kethane-LICENSE.txt │ ├── PartModules │ │ ├── IDetectorAnimator.cs │ │ ├── IExtractorAnimator.cs │ │ ├── KethaneWetMassIndicator.cs │ │ ├── KethaneKerbalBlender.cs │ │ ├── PartExtensions.cs │ │ ├── KethaneDetectorAnimatorUnity.cs │ │ ├── OrthogonalIntake.cs │ │ ├── TimedMovingAverage.cs │ │ ├── KethaneParticleDynamics.cs │ │ ├── KethaneDetectorAnimator.cs │ │ ├── KethaneParticleEmitter.cs │ │ ├── KethaneDrillAnimator.cs │ │ ├── KethaneDetector.cs │ │ ├── KethaneGenerator.cs │ │ ├── HeatSinkAnimator.cs │ │ ├── KethaneDrillAnimatorLegacy.cs │ │ ├── KethaneConverter.cs │ │ └── KethaneExtractor.cs │ ├── Generators │ │ ├── RandomExtensions.cs │ │ └── CellularResourceGenerator.cs │ ├── VesselModules │ │ ├── IKethaneBattery.cs │ │ ├── KethaneBattery.cs │ │ ├── KethaneProtoBattery.cs │ │ └── KethaneProtoDetector.cs │ ├── Utilities │ │ ├── InstallCleanup.cs │ │ ├── TutorialInstaller.cs │ │ ├── LicenseSentinel.cs │ │ ├── InstallChecker.cs │ │ ├── ShaderLoader.cs │ │ └── KopernicusWrapper.cs │ ├── ResourceDefinition.cs │ ├── IResourceGenerator.cs │ ├── UserInterface │ │ ├── TerrainData.cs │ │ ├── KerbNetModeKethane.cs │ │ ├── MainMenuOverlay.cs │ │ └── OverlayRenderer.cs │ ├── SettingsManager.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── BodyResourceData.cs │ ├── KethaneController.cs │ ├── KethaneData.cs │ ├── Particles │ │ ├── ParticleManager.cs │ │ └── ParticlePhysics.cs │ ├── Makefile │ ├── ResourceData.cs │ ├── Misc.cs │ └── Kethane.csproj ├── Kethane.sln └── Particles │ ├── GradientCfg.cs │ ├── Makefile │ ├── MinMaxGradientCfg.cs │ ├── MinMaxCurveCfg.cs │ └── Utils.cs ├── Tutorials └── banners │ └── kethane_scanning.png ├── Shaders ├── ProjectSettings │ ├── TagManager.asset │ ├── TimeManager.asset │ ├── VFXManager.asset │ ├── AudioManager.asset │ ├── InputManager.asset │ ├── NavMeshAreas.asset │ ├── PresetManager.asset │ ├── ProjectVersion.txt │ ├── DynamicsManager.asset │ ├── EditorSettings.asset │ ├── GraphicsSettings.asset │ ├── NetworkManager.asset │ ├── ProjectSettings.asset │ ├── QualitySettings.asset │ ├── Physics2DSettings.asset │ ├── ClusterInputManager.asset │ ├── EditorBuildSettings.asset │ ├── UnityConnectSettings.asset │ └── XRSettings.asset ├── .gitignore └── Assets │ ├── XML.meta │ ├── XML │ ├── kethane_bundle.xml.meta │ └── kethane_bundle.xml │ ├── Editor.meta │ ├── Shaders.meta │ ├── Shaders │ ├── AlphaUnlitVertexColored.shader.meta │ ├── UnlitAdditiveFade.shader.meta │ ├── AlphaUnlitVertexColored.shader │ └── UnlitAdditiveFade.shader │ └── Editor │ ├── BuildABs.cs.meta │ └── BuildABs.cs ├── Parts ├── copy-files ├── files ├── Makefile ├── kethane_tank2mLarge │ └── part.cfg ├── kethane_tank2mMedium │ └── part.cfg ├── kethane_tank2mSmall │ └── part.cfg ├── kethane_tank2mExtralarge │ └── part.cfg ├── kethane_tankExternal │ └── part.cfg ├── kethane_tank1mLarge │ └── part.cfg ├── kethane_kerbalBlender │ └── part.cfg ├── kethane_tank1mStandard │ └── part.cfg ├── kethane_sensor_1m │ └── part.cfg ├── kethane_highGain │ └── part.cfg ├── kethane_1m_converter │ └── part.cfg ├── kethane_2m_converter │ └── part.cfg ├── kethane_smallDrill │ └── part.cfg ├── kethane_generator │ └── part.cfg ├── kethane_turbine │ └── part.cfg ├── kethane_radialDrill │ └── part.cfg └── kethane_heavyDrill │ └── part.cfg ├── Grid.cfg ├── KethaneContract.cfg ├── Makefile ├── Resources └── Kethane.cfg ├── LICENSE.md ├── .gitignore ├── README.md └── Particles.cfg /mote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/mote.png -------------------------------------------------------------------------------- /smoke.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/smoke.jpg -------------------------------------------------------------------------------- /toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/toolbar.png -------------------------------------------------------------------------------- /Flags/Kethane 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Flags/Kethane 1.png -------------------------------------------------------------------------------- /AssetBundles/.gitignore: -------------------------------------------------------------------------------- 1 | AssetBundles 2 | AssetBundles.manifest 3 | kethane 4 | kethane.manifest 5 | -------------------------------------------------------------------------------- /AssetBundles/kethane.ksp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/AssetBundles/kethane.ksp -------------------------------------------------------------------------------- /Plugin/Kethane/Kethane-LICENSE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Plugin/Kethane/Kethane-LICENSE.txt -------------------------------------------------------------------------------- /Tutorials/banners/kethane_scanning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Tutorials/banners/kethane_scanning.png -------------------------------------------------------------------------------- /Shaders/ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/TagManager.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/TimeManager.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/VFXManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/VFXManager.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/AudioManager.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/InputManager.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/NavMeshAreas.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/PresetManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/PresetManager.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 2019.2.12f1 2 | m_EditorVersionWithRevision: 2019.2.12f1 (b1a7e1fb4fa5) 3 | -------------------------------------------------------------------------------- /Shaders/.gitignore: -------------------------------------------------------------------------------- 1 | Assembly-CSharp-Editor.csproj 2 | Library/ 3 | Packages/ 4 | Shaders.sln 5 | Shaders.userprefs 6 | UnityPackageManager/ 7 | -------------------------------------------------------------------------------- /Shaders/ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/DynamicsManager.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/EditorSettings.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/GraphicsSettings.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/NetworkManager.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/ProjectSettings.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/QualitySettings.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/Physics2DSettings.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/ClusterInputManager.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/EditorBuildSettings.asset -------------------------------------------------------------------------------- /Shaders/ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taniwha/Kethane/HEAD/Shaders/ProjectSettings/UnityConnectSettings.asset -------------------------------------------------------------------------------- /Parts/copy-files: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | list="$1" 4 | dst="$2" 5 | 6 | cat "$list" | while read file; do 7 | dir=`dirname "$file"` 8 | mkdir -p "$dst"/"$dir" 9 | cp "$file" "$dst"/"$dir" 10 | done 11 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/IDetectorAnimator.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Kethane.PartModules 3 | { 4 | public interface IDetectorAnimator 5 | { 6 | bool IsDetecting { set; } 7 | float PowerRatio { set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Shaders/ProjectSettings/XRSettings.asset: -------------------------------------------------------------------------------- 1 | { 2 | "m_SettingKeys": [ 3 | "VR Device Disabled", 4 | "VR Device User Alert" 5 | ], 6 | "m_SettingValues": [ 7 | "False", 8 | "False" 9 | ] 10 | } -------------------------------------------------------------------------------- /Shaders/Assets/XML.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 478737ef49daa48b99c857c4ff9417e0 3 | folderAsset: yes 4 | timeCreated: 1477316543 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Shaders/Assets/XML/kethane_bundle.xml.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7431eee09f0b04a73b328a0635a08853 3 | timeCreated: 1477316204 4 | licenseType: Free 5 | TextScriptImporter: 6 | userData: 7 | assetBundleName: kethane 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Shaders/Assets/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1152a130ccf58418dbaac06e208efbe7 3 | folderAsset: yes 4 | timeCreated: 1477311550 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Shaders/Assets/Shaders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2cad1d36371c24611bf3a5da804d5662 3 | folderAsset: yes 4 | timeCreated: 1477311550 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Shaders/Assets/Shaders/AlphaUnlitVertexColored.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7721a353e2a34447bb3fab245c1fc5c5 3 | timeCreated: 1477311552 4 | licenseType: Free 5 | ShaderImporter: 6 | defaultTextures: [] 7 | userData: 8 | assetBundleName: kethane 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Shaders/Assets/Shaders/UnlitAdditiveFade.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3d37f553498b69b97a1f190022bb3bca 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: kethane 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Grid.cfg: -------------------------------------------------------------------------------- 1 | Sun = 0 2 | 3 | Moho = 1.012 4 | 5 | Eve = 1.012 6 | Gilly = 0.091 7 | 8 | Kerbin = 1.012 9 | Mun = 1.012 10 | Minmus = 1.004 11 | 12 | Duna = 1.012 13 | Ike = 1.012 14 | 15 | Dres = 1.012 16 | 17 | Jool = 1.012 18 | Laythe = 1.012 19 | Vall = 0.1012 20 | Tylo = 1.012 21 | Bop = 0.099 22 | Pol = 0.10 23 | 24 | Eeloo = 1.012 -------------------------------------------------------------------------------- /Shaders/Assets/Editor/BuildABs.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6d494fb42fa0f4c1a81892c48142c67c 3 | timeCreated: 1477311552 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/IExtractorAnimator.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Kethane.PartModules 3 | { 4 | public enum ExtractorState 5 | { 6 | Deployed, 7 | Deploying, 8 | Retracted, 9 | Retracting, 10 | } 11 | 12 | public interface IExtractorAnimator 13 | { 14 | ExtractorState CurrentState { get; } 15 | void Deploy(); 16 | void Retract(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Plugin/Kethane/Generators/RandomExtensions.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Kethane.Generators 3 | { 4 | internal static class RandomExtensions 5 | { 6 | public static float Range(this System.Random random, float min, float max) 7 | { 8 | return (float)random.Range((double)min, max); 9 | } 10 | 11 | public static double Range(this System.Random random, double min, double max) 12 | { 13 | return random.NextDouble() * (max - min) + min; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Plugin/Kethane/VesselModules/IKethaneBattery.cs: -------------------------------------------------------------------------------- 1 | using Kethane.UserInterface; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | 7 | using Kethane.PartModules; 8 | 9 | namespace Kethane.VesselModules 10 | { 11 | public interface IKethaneBattery 12 | { 13 | uint flightID { get; set; } 14 | 15 | double amount { get; set; } 16 | 17 | // no plan on setting at this stage 18 | double maxAmount { get; /*set;*/ } 19 | 20 | // no plan on setting at this stage 21 | bool flowState { get; /*set;*/ } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/KethaneWetMassIndicator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Kethane.PartModules 5 | { 6 | public class KethaneWetMassIndicator : PartModule 7 | { 8 | [KSPField(isPersistant = false)] 9 | public String Label; 10 | 11 | public override string GetInfo() 12 | { 13 | return String.Format("{0}: {1}", Label ?? "Wet Mass", (float)this.part.Resources.Cast().Sum(r => r.maxAmount * PartResourceLibrary.Instance.GetDefinition(r.resourceName).density) + this.part.mass); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /KethaneContract.cfg: -------------------------------------------------------------------------------- 1 | @Contracts:NEEDS[Kethane] 2 | { 3 | @Base 4 | { 5 | @PART_REQUEST:HAS[#Part[ISRU]] 6 | { 7 | Part = kethane_1m_converter 8 | Part = kethane_2m_converter 9 | Part = kethane_smallDrill 10 | Part = kethane_heavyDrill 11 | } 12 | } 13 | @Station 14 | { 15 | @PART_REQUEST:HAS[#Part[ISRU]] 16 | { 17 | Part = kethane_1m_converter 18 | Part = kethane_2m_converter 19 | } 20 | } 21 | @Satellite 22 | { 23 | @PART_REQUEST:HAS[#Part[SurveyScanner]] 24 | { 25 | Part = kethane_highGain 26 | Part = kethane_sensor_1m 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Parts/files: -------------------------------------------------------------------------------- 1 | ../KethaneContract.cfg 2 | ../Particles.cfg 3 | kethane_1m_converter/part.cfg 4 | kethane_2m_converter/part.cfg 5 | kethane_generator/part.cfg 6 | kethane_heavyDrill/part.cfg 7 | kethane_highGain/part.cfg 8 | kethane_kerbalBlender/part.cfg 9 | kethane_radialDrill/part.cfg 10 | kethane_sensor_1m/part.cfg 11 | kethane_smallDrill/part.cfg 12 | kethane_tank1mLarge/part.cfg 13 | kethane_tank1mStandard/part.cfg 14 | kethane_tank2mExtralarge/part.cfg 15 | kethane_tank2mLarge/part.cfg 16 | kethane_tank2mMedium/part.cfg 17 | kethane_tank2mSmall/part.cfg 18 | kethane_tankExternal/part.cfg 19 | kethane_turbine/part.cfg 20 | -------------------------------------------------------------------------------- /Shaders/Assets/XML/kethane_bundle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Plugin/Kethane/Utilities/InstallCleanup.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using UnityEngine; 3 | 4 | namespace Kethane.Utilities 5 | { 6 | [KSPAddon(KSPAddon.Startup.Instantly, true)] 7 | internal class InstallCleanup: MonoBehaviour 8 | { 9 | public void Start() 10 | { 11 | File.Delete(KSPUtil.ApplicationRootPath + "GameData" + Path.DirectorySeparatorChar + "Kethane" + Path.DirectorySeparatorChar + "Plugins" + Path.DirectorySeparatorChar + "MMI_Kethane.dll"); 12 | File.Delete(KSPUtil.ApplicationRootPath + "GameData" + Path.DirectorySeparatorChar + "Kethane" + Path.DirectorySeparatorChar + "Plugins" + Path.DirectorySeparatorChar + "KethaneToolbar.dll"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Parts/Makefile: -------------------------------------------------------------------------------- 1 | KSPDIR := ${HOME}/ksp/KSP_linux 2 | MANAGED := ${KSPDIR}/KSP_Data/Managed 3 | GAMEDATA := ${KSPDIR}/GameData 4 | KGAMEDATA := ${GAMEDATA}/Kethane 5 | PLUGINDIR := ${KGAMEDATA}/Plugins 6 | APIEXTDATA := ${PLUGINDIR} 7 | 8 | RESGEN2 := resgen2 9 | GMCS := gmcs 10 | GIT := git 11 | TAR := tar 12 | ZIP := zip 13 | 14 | all: 15 | 16 | clean: 17 | 18 | info: 19 | @echo "Extraplanetary Launchpads Build Information" 20 | @echo " resgen2: ${RESGEN2}" 21 | @echo " gmcs: ${GMCS}" 22 | @echo " git: ${GIT}" 23 | @echo " tar: ${TAR}" 24 | @echo " zip: ${ZIP}" 25 | @echo " KSP Data: ${KSPDIR}" 26 | @echo " Plugin: ${PLUGINDIR}" 27 | 28 | install: all 29 | ./copy-files files "${KGAMEDATA}/Parts" 30 | 31 | .PHONY: all clean info install 32 | -------------------------------------------------------------------------------- /AssetBundles/Makefile: -------------------------------------------------------------------------------- 1 | KSPDIR := ${HOME}/ksp/KSP_linux 2 | MANAGED := ${KSPDIR}/KSP_Data/Managed 3 | GAMEDATA := ${KSPDIR}/GameData 4 | KGAMEDATA := ${GAMEDATA}/Kethane 5 | PLUGINDIR := ${KGAMEDATA}/Plugins 6 | APIEXTDATA := ${PLUGINDIR} 7 | 8 | RESGEN2 := resgen2 9 | GMCS := gmcs 10 | GIT := git 11 | TAR := tar 12 | ZIP := zip 13 | 14 | all: 15 | 16 | clean: 17 | 18 | info: 19 | @echo "Extraplanetary Launchpads Build Information" 20 | @echo " resgen2: ${RESGEN2}" 21 | @echo " gmcs: ${GMCS}" 22 | @echo " git: ${GIT}" 23 | @echo " tar: ${TAR}" 24 | @echo " zip: ${ZIP}" 25 | @echo " KSP Data: ${KSPDIR}" 26 | @echo " Plugin: ${PLUGINDIR}" 27 | 28 | install: all 29 | mkdir -p "${KGAMEDATA}" 30 | cp kethane.ksp "${KGAMEDATA}" 31 | 32 | .PHONY: all clean info install 33 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/KethaneKerbalBlender.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Kethane.PartModules 7 | { 8 | public class KethaneKerbalBlender : PartModule 9 | { 10 | [KSPEvent(guiName = "Blend Kerbal", externalToEVAOnly = true, guiActiveUnfocused = true, unfocusedRange = 1.5f)] 11 | public void ConsumeKerbal() 12 | { 13 | var vessel = FlightGlobals.ActiveVessel; 14 | if (!vessel.isEVA) { return; } 15 | if (vessel.GetVesselCrew()[0].isBadass) 16 | { 17 | vessel.rootPart.explosionPotential = 10000; 18 | } 19 | FlightGlobals.ForceSetActiveVessel(this.vessel); 20 | vessel.rootPart.explode(); 21 | this.part.RequestResource("Kethane", -150d); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Plugin/Kethane/Utilities/TutorialInstaller.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using UnityEngine; 4 | 5 | namespace Kethane.Utilities 6 | { 7 | [KSPAddon(KSPAddon.Startup.Instantly, true)] 8 | internal class TutorialInstaller : MonoBehaviour 9 | { 10 | public void Start() 11 | { 12 | var sourcePath = Path.GetFullPath(KSPUtil.ApplicationRootPath) + "GameData/Kethane/Tutorials/"; 13 | var sourceUri = new Uri(sourcePath); 14 | var destinationUri = new Uri(Path.GetFullPath(KSPUtil.ApplicationRootPath) + "saves/training/"); 15 | 16 | foreach (var file in new DirectoryInfo(sourcePath).GetFiles("*", SearchOption.AllDirectories)) 17 | { 18 | file.CopyTo(new Uri(destinationUri, sourceUri.MakeRelativeUri(new Uri(file.FullName))).LocalPath, true); 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Plugin/Kethane/ResourceDefinition.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Kethane 4 | { 5 | public class ResourceDefinition 6 | { 7 | public string Resource { get; private set; } 8 | public Color ColorFull { get; private set; } 9 | public Color ColorEmpty { get; private set; } 10 | public ConfigNode Generator { get; private set; } 11 | 12 | public ResourceDefinition(ConfigNode node) 13 | { 14 | Resource = node.GetValue("Resource"); 15 | var colorFull = node.GetValue("ColorFull"); 16 | ColorFull = colorFull != null ? ConfigNode.ParseColor(colorFull) : Color.white; 17 | var colorEmpty = node.GetValue("ColorEmpty"); 18 | ColorEmpty = colorEmpty != null ? ConfigNode.ParseColor(colorEmpty) : Color.white; 19 | 20 | Generator = node.GetNode("Generator") ?? new ConfigNode(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Plugin/Kethane/Utilities/LicenseSentinel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using UnityEngine; 5 | 6 | namespace Kethane.Utilities 7 | { 8 | [KSPAddon(KSPAddon.Startup.Instantly, true)] 9 | internal class LicenseSentinel : MonoBehaviour 10 | { 11 | protected void Start() 12 | { 13 | var assembly = Assembly.GetExecutingAssembly(); 14 | var path = assembly.Location; 15 | if (!Path.GetFileName(path).Equals("Kethane.dll", StringComparison.OrdinalIgnoreCase)) 16 | { 17 | path += "-Kethane"; 18 | } 19 | else 20 | { 21 | path = Path.ChangeExtension(path, null); 22 | } 23 | var text = new StreamReader(assembly.GetManifestResourceStream("Kethane.Kethane-LICENSE.txt")).ReadToEnd(); 24 | File.WriteAllText(path + "-LICENSE.txt", text); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Plugin/Kethane/VesselModules/KethaneBattery.cs: -------------------------------------------------------------------------------- 1 | using Kethane.UserInterface; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | 7 | using Kethane.PartModules; 8 | 9 | namespace Kethane.VesselModules 10 | { 11 | public class KethaneBattery: IKethaneBattery 12 | { 13 | public uint flightID { get; set; } 14 | 15 | PartResource partResource; 16 | 17 | public KethaneBattery (PartResource res) 18 | { 19 | partResource = res; 20 | } 21 | 22 | public double amount 23 | { 24 | get { return partResource.amount; } 25 | set { partResource.amount = value; } 26 | } 27 | 28 | public double maxAmount 29 | { 30 | get { return partResource.maxAmount; } 31 | // no plan on setting at this stage 32 | //set { partResource.maxAmount = value; } 33 | } 34 | 35 | public bool flowState 36 | { 37 | get { return partResource.flowState; } 38 | // no plan on setting at this stage 39 | //set { partResource.flowState = value; } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Shaders/Assets/Editor/BuildABs.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine; 3 | 4 | class BuildABs { 5 | [MenuItem("Assets/Build Asset Bundles")] 6 | static void BuildAssetBundles() 7 | { 8 | // Put the bundles in a folder called "ABs" within the 9 | // Assets folder. 10 | var outDir = "../AssetBundles"; 11 | var opts = BuildAssetBundleOptions.DeterministicAssetBundle 12 | | BuildAssetBundleOptions.ForceRebuildAssetBundle; 13 | BuildTarget[] platforms = { BuildTarget.StandaloneWindows, }; 14 | string[] platformExts = { "", "-windows", "-macosx", "-linux" }; 15 | for (var i = 0; i < platforms.Length; ++i) { 16 | Debug.Log($"AB: {platforms[i]}"); 17 | BuildPipeline.BuildAssetBundles(outDir, opts, platforms[i]); 18 | var outFile = outDir + "/kethane"+ platformExts[i]+".ksp"; 19 | FileUtil.ReplaceFile(outDir + "/kethane", outFile); 20 | Debug.Log($"AB: {outFile}"); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | KSPDIR := ${HOME}/ksp/KSP_linux 2 | MANAGED := ${KSPDIR}/KSP_Data/Managed 3 | GAMEDATA := ${KSPDIR}/GameData 4 | KGAMEDATA := ${GAMEDATA}/Kethane 5 | PLUGINDIR := ${KGAMEDATA}/Plugins 6 | APIEXTDATA := ${PLUGINDIR} 7 | 8 | RESGEN2 := resgen2 9 | GMCS := gmcs 10 | GIT := git 11 | TAR := tar 12 | ZIP := zip 13 | 14 | .PHONY: all clean info install 15 | 16 | SUBDIRS=AssetBundles Parts Plugin/Particles Plugin/Kethane 17 | 18 | all clean: 19 | @for dir in ${SUBDIRS}; do \ 20 | make -C $$dir $@ || exit 1; \ 21 | done 22 | 23 | install: 24 | mkdir -p ${KGAMEDATA}/Resources 25 | cp Resources/Kethane.cfg ${KGAMEDATA}/Resources 26 | @for dir in ${SUBDIRS}; do \ 27 | make -C $$dir $@ || exit 1; \ 28 | done 29 | 30 | info: 31 | @echo "Extraplanetary Launchpads Build Information" 32 | @echo " resgen2: ${RESGEN2}" 33 | @echo " gmcs: ${GMCS}" 34 | @echo " git: ${GIT}" 35 | @echo " tar: ${TAR}" 36 | @echo " zip: ${ZIP}" 37 | @echo " KSP Data: ${KSPDIR}" 38 | @echo " Plugin: ${PLUGINDIR}" 39 | -------------------------------------------------------------------------------- /Plugin/Kethane/VesselModules/KethaneProtoBattery.cs: -------------------------------------------------------------------------------- 1 | using Kethane.UserInterface; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | 7 | using Kethane.PartModules; 8 | 9 | namespace Kethane.VesselModules 10 | { 11 | public class KethaneProtoBattery: IKethaneBattery 12 | { 13 | public uint flightID { get; set; } 14 | 15 | ProtoPartResourceSnapshot protoResource; 16 | 17 | public KethaneProtoBattery (ProtoPartResourceSnapshot res) 18 | { 19 | protoResource = res; 20 | } 21 | 22 | public double amount 23 | { 24 | get { return protoResource.amount; } 25 | set { protoResource.amount = value; } 26 | } 27 | 28 | public double maxAmount 29 | { 30 | get { return protoResource.maxAmount; } 31 | // no plan on setting at this stage 32 | //set { protoResource.maxAmount = value; } 33 | } 34 | 35 | public bool flowState 36 | { 37 | get { return protoResource.flowState; } 38 | // no plan on setting at this stage 39 | //set { protoResource.flowState = value; } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Plugin/Kethane.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.21005.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kethane", "Kethane\Kethane.csproj", "{8D1501AC-6C11-459B-9561-6CC5E3EC15FE}" 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 | {8D1501AC-6C11-459B-9561-6CC5E3EC15FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {8D1501AC-6C11-459B-9561-6CC5E3EC15FE}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {8D1501AC-6C11-459B-9561-6CC5E3EC15FE}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {8D1501AC-6C11-459B-9561-6CC5E3EC15FE}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /Parts/kethane_tank2mLarge/part.cfg: -------------------------------------------------------------------------------- 1 | PART 2 | { 3 | name = kethane_tank2mLarge 4 | module = Part 5 | author = Keptin 6 | 7 | mesh = model.mu 8 | rescaleFactor = 1.2 9 | 10 | node_stack_top = 0.0, 1.385, 0.0, 0.0, 1.0, 0.0, 2 11 | node_stack_bottom = 0.0, -1.37, 0.0, 0.0, -1.0, 0.0, 2 12 | node_attach = 1.0756, 0.0, 0.0, 1.0, 0.0, 0.0, 1 13 | 14 | cost = 5880 15 | category = Propulsion 16 | subcategory = 0 17 | title = KE-TL40 Kethane Storage Apparatus 18 | manufacturer = Organization of Kethane Equipment Producers 19 | description = Mining foremen celebrated the arrival of the Kethane TL series. According to the engineers, its overflow venting system reduces disastrous consequences by a considerable 30%. 20 | 21 | TechRequired = heavierRocketry 22 | entryCost = 50 23 | 24 | attachRules = 1,1,1,1,0 25 | bulkheadProfiles = size2, srf 26 | 27 | mass = 1.75 28 | dragModelType = default 29 | maximum_drag = 0.2 30 | minimum_drag = 0.3 31 | angularDrag = 2 32 | crashTolerance = 6 33 | breakingForce = 200 34 | breakingTorque = 200 35 | maxTemp = 2000 36 | fuelCrossFeed = True 37 | 38 | RESOURCE 39 | { 40 | name = Kethane 41 | amount = 0 42 | maxAmount = 8000 43 | } 44 | 45 | MODULE 46 | { 47 | name = KethaneWetMassIndicator 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Parts/kethane_tank2mMedium/part.cfg: -------------------------------------------------------------------------------- 1 | PART 2 | { 3 | name = kethane_tank2mMedium 4 | module = Part 5 | author = Keptin 6 | 7 | mesh = model.mu 8 | rescaleFactor = 1.2 9 | 10 | node_stack_top = 0.0, 0.7905, 0.0, 0.0, 1.0, 0.0, 2 11 | node_stack_bottom = 0.0, -0.721, 0.0, 0.0, -1.0, 0.0, 2 12 | node_attach = 1.0756, 0.0, 0.0, 1.0, 0.0, 0.0, 1 13 | 14 | cost = 2990 15 | category = Propulsion 16 | subcategory = 0 17 | title = KE-TL20 Kethane Storage Apparatus 18 | manufacturer = Organization of Kethane Equipment Producers 19 | description = Mining foremen celebrated the arrival of the Kethane TL series. According to the engineers, its overflow venting system reduces disastrous consequences by a considerable 30%. 20 | 21 | TechRequired = fuelSystems 22 | entryCost = 50 23 | 24 | attachRules = 1,1,1,1,0 25 | bulkheadProfiles = size2, srf 26 | 27 | mass = 1 28 | dragModelType = default 29 | maximum_drag = 0.2 30 | minimum_drag = 0.3 31 | angularDrag = 2 32 | crashTolerance = 6 33 | breakingForce = 200 34 | breakingTorque = 200 35 | maxTemp = 2000 36 | fuelCrossFeed = True 37 | 38 | RESOURCE 39 | { 40 | name = Kethane 41 | amount = 0 42 | maxAmount = 4000 43 | } 44 | 45 | MODULE 46 | { 47 | name = KethaneWetMassIndicator 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Parts/kethane_tank2mSmall/part.cfg: -------------------------------------------------------------------------------- 1 | PART 2 | { 3 | name = kethane_tank2mSmall 4 | module = Part 5 | author = Keptin 6 | 7 | mesh = model.mu 8 | rescaleFactor = 1.2 9 | 10 | node_stack_top = 0.0, 0.291, 0.0, 0.0, 1.0, 0.0, 2 11 | node_stack_bottom = 0.0, -0.285, 0.0, 0.0, -1.0, 0.0, 2 12 | node_attach = 1.0756, 0.0, 0.0, 1.0, 0.0, 0.0, 1 13 | 14 | cost = 1520 15 | category = Propulsion 16 | subcategory = 0 17 | title = KE-TL10 Kethane Storage Apparatus 18 | manufacturer = Organization of Kethane Equipment Producers 19 | description = Mining foremen celebrated the arrival of the Kethane TL series. Despite lacking an overflow venting system, the TL10 is no more prone to exploding than any other tank in the series. 20 | 21 | TechRequired = fuelSystems 22 | entryCost = 50 23 | 24 | attachRules = 1,1,1,1,0 25 | bulkheadProfiles = size2, srf 26 | 27 | mass = 0.625 28 | dragModelType = default 29 | maximum_drag = 0.2 30 | minimum_drag = 0.3 31 | angularDrag = 2 32 | crashTolerance = 6 33 | breakingForce = 200 34 | breakingTorque = 200 35 | maxTemp = 2000 36 | fuelCrossFeed = True 37 | 38 | RESOURCE 39 | { 40 | name = Kethane 41 | amount = 0 42 | maxAmount = 2000 43 | } 44 | 45 | MODULE 46 | { 47 | name = KethaneWetMassIndicator 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Parts/kethane_tank2mExtralarge/part.cfg: -------------------------------------------------------------------------------- 1 | PART 2 | { 3 | name = kethane_tank2mExtralarge 4 | module = Part 5 | author = Keptin 6 | 7 | mesh = model.mu 8 | rescaleFactor = 1.2 9 | 10 | node_stack_top = 0.0, 2.832, 0.0, 0.0, 1.0, 0.0, 2 11 | node_stack_bottom = 0.0, -2.65, 0.0, 0.0, -1.0, 0.0, 2 12 | node_attach = 1.0756, 0.0, 0.0, 1.0, 0.0, 0.0, 1 13 | 14 | cost = 11560 15 | category = Propulsion 16 | subcategory = 0 17 | title = KE-TL80 Kethane Storage Apparatus 18 | manufacturer = Organization of Kethane Equipment Producers 19 | description = Mining foremen celebrated the arrival of the Kethane TL series. According to the engineers, its overflow venting system reduces disastrous consequences by a considerable 30%. 20 | 21 | TechRequired = heavierRocketry 22 | entryCost = 50 23 | 24 | attachRules = 1,1,1,1,0 25 | bulkheadProfiles = size2, srf 26 | 27 | mass = 3.25 28 | dragModelType = default 29 | maximum_drag = 0.2 30 | minimum_drag = 0.3 31 | angularDrag = 2 32 | crashTolerance = 6 33 | breakingForce = 200 34 | breakingTorque = 200 35 | maxTemp = 2000 36 | fuelCrossFeed = True 37 | 38 | RESOURCE 39 | { 40 | name = Kethane 41 | amount = 0 42 | maxAmount = 16000 43 | } 44 | 45 | MODULE 46 | { 47 | name = KethaneWetMassIndicator 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Parts/kethane_tankExternal/part.cfg: -------------------------------------------------------------------------------- 1 | // MMI.K FuelTank - External 2 | // 3 | 4 | PART 5 | { 6 | // --- general parameters --- 7 | name = kethane_tankExternal 8 | module = Part 9 | author = Dani-Sang 10 | 11 | // --- asset parameters --- 12 | mesh = model.mu 13 | scale = 0.1 14 | 15 | 16 | // --- node definitions --- 17 | node_attach = 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1 18 | 19 | 20 | // --- editor parameters --- 21 | cost = 175 22 | category = Propulsion 23 | subcategory = 0 24 | title = KE-TE15 External Kethane Manifold 25 | manufacturer = Mechanical Mouse Industries 26 | description = External Kethane Fuel Tank 27 | 28 | TechRequired = precisionEngineering 29 | entryCost = 50 30 | 31 | // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision 32 | attachRules = 0,1,0,0,0 33 | bulkheadProfiles = srf 34 | 35 | // --- standard part parameters --- 36 | mass = 0.1 37 | dragModelType = default 38 | maximum_drag = 0.1 39 | minimum_drag = 0.1 40 | angularDrag = 2 41 | crashTolerance = 6 42 | breakingForce = 50 43 | breakingTorque = 50 44 | maxTemp = 2000 45 | 46 | // --- Kethane Tank parameters --- 47 | RESOURCE 48 | { 49 | name = Kethane 50 | amount = 0 51 | maxAmount = 150 52 | } 53 | 54 | MODULE 55 | { 56 | name = KethaneWetMassIndicator 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Plugin/Kethane/IResourceGenerator.cs: -------------------------------------------------------------------------------- 1 | using GeodesicGrid; 2 | using System; 3 | 4 | namespace Kethane 5 | { 6 | public interface IResourceGenerator 7 | { 8 | IBodyResources Load(CelestialBody body, ConfigNode node); 9 | } 10 | 11 | public interface IBodyResources 12 | { 13 | ConfigNode Save(); 14 | double MaxQuantity { get; } 15 | double? GetQuantity(Cell cell); 16 | double Extract(Cell cell, double amount); 17 | } 18 | 19 | internal class EmptyResourceGenerator : IResourceGenerator 20 | { 21 | private static readonly IBodyResources bodyResources = new BodyResources(); 22 | 23 | public EmptyResourceGenerator() { } 24 | public EmptyResourceGenerator(ConfigNode node) { } 25 | 26 | public IBodyResources Load(CelestialBody body, ConfigNode node) { return bodyResources; } 27 | 28 | private class BodyResources : IBodyResources 29 | { 30 | public double MaxQuantity { get { return 0; } } 31 | public ConfigNode Save() { return new ConfigNode(); } 32 | public double? GetQuantity(Cell cell) { return null; } 33 | public double Extract(Cell cell, double amount) { throw new Exception("No deposit here"); } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Plugin/Kethane/Utilities/InstallChecker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Reflection; 5 | using UnityEngine; 6 | 7 | namespace Kethane.Utilities 8 | { 9 | [KSPAddon(KSPAddon.Startup.MainMenu, true)] 10 | internal class InstallChecker : MonoBehaviour 11 | { 12 | protected void Start() 13 | { 14 | var assemblies = AssemblyLoader.loadedAssemblies.Where(a => a.assembly.GetName().Name == Assembly.GetExecutingAssembly().GetName().Name).Where(a => a.url != "Kethane/Plugins"); 15 | if (assemblies.Any()) 16 | { 17 | var badPaths = assemblies.Select(a => a.path).Select(p => Uri.UnescapeDataString(new Uri(Path.GetFullPath(KSPUtil.ApplicationRootPath)).MakeRelativeUri(new Uri(p)).ToString().Replace('/', Path.DirectorySeparatorChar))); 18 | PopupDialog.SpawnPopupDialog(new Vector2(0, 0), new Vector2(0, 0), "InstallChecker", "Incorrect Kethane Installation", "Kethane has been installed incorrectly and will not function properly. All Kethane files should be located in KSP/GameData/Kethane. Do not move any files from inside the Kethane folder.\n\nIncorrect path(s):\n" + String.Join("\n", badPaths.ToArray()), "OK", false, HighLogic.UISkin); 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/PartExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace Kethane.PartModules 6 | { 7 | internal static class PartExtensions 8 | { 9 | public static void GetConnectedResourceTotals(this Part part, String resourceName, out double amount, out double maxAmount, bool pulling = true) 10 | { 11 | var resourceDef = PartResourceLibrary.Instance.GetDefinition(resourceName); 12 | part.GetConnectedResourceTotals(resourceDef.id, resourceDef.resourceFlowMode, out amount, out maxAmount, pulling); 13 | } 14 | 15 | public static AnimationState[] SetUpAnimation(this Part part, string animationName) 16 | { 17 | var states = new List(); 18 | foreach (var animation in part.FindModelAnimators(animationName)) 19 | { 20 | var animationState = animation[animationName]; 21 | animationState.speed = 0; 22 | animationState.enabled = true; 23 | animationState.wrapMode = WrapMode.ClampForever; 24 | animation.Blend(animationName); 25 | states.Add(animationState); 26 | } 27 | return states.ToArray(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Parts/kethane_tank1mLarge/part.cfg: -------------------------------------------------------------------------------- 1 | PART 2 | { 3 | // --- general parameters --- 4 | name = kethane_tank1mLarge 5 | module = Part 6 | author = Dani-Sang 7 | 8 | // --- asset parameters --- 9 | mesh = model.mu 10 | scale = 0.01 11 | 12 | 13 | // --- node definitions --- 14 | node_stack_top = 0.0, 75.0, 0.0, 0.0, 1.0, 0.0 15 | node_stack_bottom = 0.0, -76.0, 0.0, 0.0, -1.0, 0.0 16 | node_attach = 0.0, 0.0, -51.0, 0.0, 0.0, 1.0, 1 17 | 18 | 19 | 20 | // --- editor parameters --- 21 | cost = 710 22 | category = -1 23 | TechHidden = True 24 | subcategory = 0 25 | title = KE-TM30 Kethane Storage Apparatus 26 | manufacturer = Mechanical Mouse Industries 27 | description = Regular 1m Kethane Fuel Tank 28 | 29 | // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision 30 | attachRules = 1,1,1,1,0 31 | bulkheadProfiles = size1, srf 32 | 33 | // --- standard part parameters --- 34 | mass = 0.25 35 | dragModelType = default 36 | maximum_drag = 0.2 37 | minimum_drag = 0.3 38 | angularDrag = 2 39 | crashTolerance = 70 40 | breakingForce = 50 41 | breakingTorque = 50 42 | maxTemp = 2000 43 | fuelCrossFeed = True 44 | 45 | // --- Kethane Tank parameters --- 46 | RESOURCE 47 | { 48 | name = Kethane 49 | amount = 0 50 | maxAmount = 1000 51 | } 52 | 53 | MODULE 54 | { 55 | name = KethaneWetMassIndicator 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Plugin/Kethane/UserInterface/TerrainData.cs: -------------------------------------------------------------------------------- 1 | using GeodesicGrid; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace Kethane.UserInterface 6 | { 7 | public class TerrainData 8 | { 9 | private static readonly Dictionary bodies = new Dictionary(); 10 | 11 | public static void Clear() 12 | { 13 | bodies.Clear(); 14 | } 15 | 16 | public static TerrainData ForBody(CelestialBody body) 17 | { 18 | if (body == null) { throw new ArgumentException("Body may not be null"); } 19 | if (!bodies.ContainsKey(body.name)) 20 | { 21 | bodies[body.name] = new TerrainData(body); 22 | } 23 | return bodies[body.name]; 24 | } 25 | 26 | private readonly CellMap heightRatios; 27 | 28 | private TerrainData(CelestialBody body) 29 | { 30 | if (body.pqsController == null) { throw new ArgumentException("Body doesn't have a PQS controller"); } 31 | heightRatios = new CellMap(KethaneData.GridLevel, c => (float)(body.pqsController.GetSurfaceHeight(c.Position) / body.pqsController.radius)); 32 | } 33 | 34 | public float GetHeightRatio(Cell cell) 35 | { 36 | return heightRatios[cell]; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Resources/Kethane.cfg: -------------------------------------------------------------------------------- 1 | RESOURCE_DEFINITION 2 | { 3 | name = Kethane 4 | density = 0.002 5 | unitCost = 0.16 6 | flowMode = ALL_VESSEL 7 | transfer = PUMP 8 | volume = 2 9 | } 10 | 11 | RESOURCE_DEFINITION 12 | { 13 | name = KIntakeAir 14 | density = 0.001 15 | flowMode = NO_FLOW 16 | transfer = NONE 17 | } 18 | 19 | @TANK_DEFINITION[Default] { 20 | TANK { 21 | name = Kethane 22 | fillable = false 23 | } 24 | } 25 | 26 | KethaneResource 27 | { 28 | Resource = Kethane 29 | ColorFull = 0.08235294, 0.6901961, 0.1019608 30 | ColorEmpty = 0.1858824, 0.3105883, 0.1929412 31 | Generator 32 | { 33 | name = LegacyResourceGenerator 34 | MinRadius = 7.29 35 | MaxRadius = 16.2 36 | MinQuantity = 50000 37 | MaxQuantity = 2500000 38 | MinVertices = 20 39 | MaxVertices = 50 40 | RadiusVariance = 0.45 41 | DepositCount = 17 42 | NumberOfTries = 30 43 | Body 44 | { 45 | name = Kerbin 46 | MinRadius = 4.05 47 | DepositCount = 12 48 | } 49 | Body 50 | { 51 | name = Mun 52 | DepositCount = 27 53 | } 54 | Body 55 | { 56 | name = Minmus 57 | MaxRadius = 12.96 58 | } 59 | Body 60 | { 61 | name = Sun 62 | DepositCount = 0 63 | } 64 | Body 65 | { 66 | name = Jool 67 | DepositCount = 0 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Parts/kethane_kerbalBlender/part.cfg: -------------------------------------------------------------------------------- 1 | PART 2 | { 3 | 4 | // --- general parameters --- 5 | name = kethane_kerbalBlender 6 | module = Part 7 | author = Dani-Sang 8 | 9 | // --- asset parameters --- 10 | mesh = model.mu 11 | scale = 0.1 12 | 13 | 14 | // --- node definitions --- 15 | node_attach = 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 16 | 17 | 18 | // --- editor parameters --- 19 | cost = 666 20 | category = Science 21 | subcategory = 0 22 | title = KE-WAITNONOSTOP-01 Kerbal Unreconstitutionator 23 | manufacturer = Organization of Kethane Equipment Producers 24 | description = In an effort to "go green", OKEP engineers devised a method to extract Kethane from a readily available and fully renewable resource. While low in mass, these (sometimes) brave Kerbonauts have a high energy density, yielding a not insignificant quantity of Kethane. Warranty void if used on Jebediah. 25 | 26 | TechRequired = spaceExploration 27 | entryCost = 50 28 | 29 | // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision 30 | attachRules = 0,1,0,0,1 31 | bulkheadProfiles = srf 32 | 33 | // --- standard part parameters --- 34 | mass = 0.03 35 | dragModelType = default 36 | maximum_drag = 0.001 37 | minimum_drag = 0.001 38 | angularDrag = 2 39 | crashTolerance = 6 40 | breakingForce = 50 41 | breakingTorque = 50 42 | maxTemp = 2000 43 | fuelCrossFeed = False 44 | 45 | MODULE 46 | { 47 | name = KethaneKerbalBlender 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/KethaneDetectorAnimatorUnity.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Kethane.PartModules 4 | { 5 | public class KethaneDetectorAnimatorUnity : PartModule, IDetectorAnimator 6 | { 7 | public bool IsDetecting { private get; set; } 8 | public float PowerRatio { private get; set; } 9 | 10 | [KSPField(isPersistant = false)] 11 | public string DeployAnimation; 12 | 13 | [KSPField(isPersistant = false)] 14 | public string RunningAnimation; 15 | 16 | private AnimationState[] deployStates; 17 | private AnimationState[] runningStates; 18 | 19 | public override void OnStart(PartModule.StartState state) 20 | { 21 | deployStates = this.part.SetUpAnimation(DeployAnimation); 22 | runningStates = this.part.SetUpAnimation(RunningAnimation); 23 | 24 | foreach (var runningState in runningStates) 25 | { 26 | runningState.wrapMode = WrapMode.Loop; 27 | } 28 | } 29 | 30 | public override void OnUpdate() 31 | { 32 | foreach (var state in deployStates) 33 | { 34 | state.normalizedTime = Mathf.Clamp01(state.normalizedTime); 35 | state.speed = IsDetecting ? PowerRatio : -1; 36 | } 37 | 38 | foreach (var state in runningStates) 39 | { 40 | state.speed = IsDetecting ? PowerRatio : 0; 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Plugin/Kethane/SettingsManager.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Kethane 4 | { 5 | [KSPAddon(KSPAddon.Startup.Instantly, true)] 6 | internal class SettingsManager : MonoBehaviour 7 | { 8 | private static ConfigNode node; 9 | 10 | public static string GetValue(string key) 11 | { 12 | load(); 13 | return node.GetValue(key); 14 | } 15 | 16 | public static void SetValue(string key, object value) 17 | { 18 | load(); 19 | if (node.HasValue(key)) 20 | { 21 | node.RemoveValue(key); 22 | } 23 | node.AddValue(key, value); 24 | } 25 | 26 | public static void Save() 27 | { 28 | load(); 29 | Debug.LogWarning("[Kethane] Saving settings"); 30 | node.Save(settingsFile); 31 | } 32 | 33 | private static void load() 34 | { 35 | if (node != null) { return; } 36 | Debug.LogWarning("[Kethane] Loading settings"); 37 | node = ConfigNode.Load(settingsFile) ?? new ConfigNode(); 38 | } 39 | 40 | private static string settingsFile 41 | { 42 | get { return KSPUtil.ApplicationRootPath + "GameData/Kethane/settings.cfg"; } 43 | } 44 | 45 | public void Awake() 46 | { 47 | MonoBehaviour.DontDestroyOnLoad(this); 48 | } 49 | 50 | public void Destroy() 51 | { 52 | Save(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Plugin/Particles/GradientCfg.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace KethaneParticles 5 | { 6 | 7 | public class GradientCfg 8 | { 9 | public Gradient gradient { get; private set; } 10 | 11 | public Color this [int index] 12 | { 13 | get { 14 | Color c = gradient.colorKeys[index].color; 15 | c.a = gradient.alphaKeys[index].alpha; 16 | return c; 17 | } 18 | set { 19 | gradient.colorKeys[index].color = value; 20 | gradient.alphaKeys[index].alpha = value.a; 21 | } 22 | } 23 | 24 | public GradientCfg () 25 | { 26 | gradient = new Gradient (); 27 | } 28 | 29 | public void Load (ConfigNode node) 30 | { 31 | var keys = node.GetValues ("key"); 32 | var colors = new GradientColorKey[keys.Length]; 33 | var alphas = new GradientAlphaKey[keys.Length]; 34 | for (int i = 0; i < keys.Length; i++) { 35 | var vals = Utils.ParseFloatArray (keys[i]); 36 | if (vals.Length < 4) { 37 | Debug.Log ($"[GradientCfg] key {i}: need at least 4 values: x, r, g, b[, a]: {vals}"); 38 | } else { 39 | var c = new Color (vals[1], vals[2], vals[3]); 40 | float a = 1; 41 | if (vals.Length > 4) { 42 | a = vals[4]; 43 | } 44 | if (vals.Length > 5) { 45 | Debug.Log ($"[GradientCfg] key {i}: ignoring excess values: {vals}"); 46 | } 47 | colors[i] = new GradientColorKey (c, vals[0]); 48 | alphas[i] = new GradientAlphaKey (a, vals[0]); 49 | } 50 | } 51 | gradient.colorKeys = colors; 52 | gradient.alphaKeys = alphas; 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Plugin/Kethane/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Kethane")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("MMI, Majiir")] 12 | [assembly: AssemblyProduct("Kethane")] 13 | [assembly: AssemblyCopyright("Copyright © Majiir 2012-2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("e6926b84-0a8a-415a-97f8-5d79fc00c749")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.*")] 36 | [assembly: AssemblyInformationalVersion("0.11.0")] 37 | -------------------------------------------------------------------------------- /Parts/kethane_tank1mStandard/part.cfg: -------------------------------------------------------------------------------- 1 | PART 2 | { 3 | // --- general parameters --- 4 | name = kethane_tank1mStandard 5 | module = Part 6 | author = Keptin 7 | 8 | // --- asset parameters --- 9 | mesh = model.mu 10 | rescaleFactor = 0.0125 11 | 12 | 13 | // --- node definitions --- 14 | node_stack_top = 0.0, 74.48, 0.0, 0.0, 1.0, 0.0 15 | node_stack_bottom = 0.0, -74.48, 0.0, 0.0, -1.0, 0.0 16 | node_attach = 0.0, 0.0, -47.4, 0.0, 0.0, 1.0, 1 17 | 18 | 19 | 20 | // --- editor parameters --- 21 | cost = 710 22 | category = Propulsion 23 | subcategory = 0 24 | title = KE-TM30 Kethane Storage Apparatus 25 | manufacturer = Organization of Kethane Equipment Producers 26 | description = After a series of incidents, these standard-sized Kethane tanks were upgraded with pressure release systems similar to those on their larger counterparts. Unlike those larger counterparts, these tanks don't seem any less likely to explode. 27 | 28 | TechRequired = generalConstruction 29 | entryCost = 50 30 | 31 | // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision 32 | attachRules = 1,1,1,1,0 33 | bulkheadProfiles = size1, srf 34 | 35 | // --- standard part parameters --- 36 | mass = 0.25 37 | dragModelType = default 38 | maximum_drag = 0.2 39 | minimum_drag = 0.3 40 | angularDrag = 2 41 | crashTolerance = 70 42 | breakingForce = 50 43 | breakingTorque = 50 44 | maxTemp = 2000 45 | fuelCrossFeed = True 46 | 47 | // --- Kethane Tank parameters --- 48 | RESOURCE 49 | { 50 | name = Kethane 51 | amount = 0 52 | maxAmount = 1000 53 | } 54 | 55 | MODULE 56 | { 57 | name = KethaneWetMassIndicator 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Shaders/Assets/Shaders/AlphaUnlitVertexColored.shader: -------------------------------------------------------------------------------- 1 | // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' 2 | 3 | Shader "Kethane/AlphaUnlitVertexColored" { 4 | Properties { 5 | _Color ("Main Color", Color) = (1,1,1,1) 6 | _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {} 7 | } 8 | 9 | SubShader { 10 | //Tags {"Queue"="Transparent+100" "IgnoreProjector"="True" "RenderType"="Transparent"} 11 | Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } 12 | LOD 100 13 | 14 | ZWrite Off 15 | Blend SrcAlpha OneMinusSrcAlpha 16 | 17 | BindChannels { 18 | Bind "Color", color 19 | Bind "Vertex", vertex 20 | } 21 | 22 | Pass { 23 | CGPROGRAM 24 | #pragma vertex vert 25 | #pragma fragment frag 26 | 27 | #include "UnityCG.cginc" 28 | 29 | struct appdata 30 | { 31 | float4 vertex : POSITION; 32 | float4 color: COLOR; 33 | float2 uv : TEXCOORD0; 34 | }; 35 | 36 | struct v2f 37 | { 38 | float2 uv : TEXCOORD0; 39 | float4 vertex : SV_POSITION; 40 | float4 color: COLOR; 41 | }; 42 | 43 | float4 _Color; 44 | float4 _MainTex_ST; 45 | sampler2D _MainTex; 46 | 47 | v2f vert (appdata v) 48 | { 49 | v2f o; 50 | o.vertex = UnityObjectToClipPos(v.vertex); 51 | o.uv = TRANSFORM_TEX(v.uv, _MainTex); 52 | o.color = v.color; 53 | return o; 54 | } 55 | fixed4 frag (v2f i) : SV_Target 56 | { 57 | fixed4 col = tex2D(_MainTex, i.uv); 58 | return col * i.color * _Color; 59 | } 60 | ENDCG 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/OrthogonalIntake.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Kethane.PartModules 5 | { 6 | public class OrthogonalIntake : PartModule 7 | { 8 | [KSPField(isPersistant = false)] 9 | public string Resource; 10 | 11 | [KSPField(isPersistant = false)] 12 | public float BaseFlowRate; 13 | 14 | [KSPField(isPersistant = false)] 15 | public float PowerFlowRate; 16 | 17 | [KSPField(isPersistant = false)] 18 | public float SpeedFlowRate; 19 | 20 | [KSPField(isPersistant = false, guiActive = true, guiName = "Intake Flow Rate", guiFormat = "F1", guiUnits = "L/s")] 21 | public float AirFlow; 22 | 23 | public void FixedUpdate() 24 | { 25 | if (!HighLogic.LoadedSceneIsFlight) { return; } 26 | 27 | double resourceDensity = PartResourceLibrary.Instance.GetDefinition(Resource).density; 28 | double airDensity = this.part.vessel.atmDensity; 29 | 30 | double amount = BaseFlowRate; 31 | 32 | var engine = this.part.Modules.OfType().Single(); 33 | double throttle = engine.finalThrust / engine.maxThrust; 34 | amount += throttle * PowerFlowRate; 35 | 36 | double airSpeed = this.part.vessel.srf_velocity.magnitude; 37 | amount += airSpeed * SpeedFlowRate; 38 | 39 | amount = Math.Max(amount, 0); 40 | AirFlow = (float)(amount * 1000); 41 | 42 | amount *= TimeWarp.fixedDeltaTime; 43 | amount *= airDensity / resourceDensity; 44 | this.part.RequestResource(Resource, -amount); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Plugin/Particles/Makefile: -------------------------------------------------------------------------------- 1 | MODNAME := Kethane 2 | KSPDIR := ${HOME}/ksp/KSP_linux 3 | MANAGED := ${KSPDIR}/KSP_Data/Managed 4 | GAMEDATA := ${KSPDIR}/GameData 5 | KGAMEDATA := ${GAMEDATA}/${MODNAME} 6 | PLUGINDIR := ${KGAMEDATA}/Plugins 7 | 8 | DLLNAME := ${MODNAME}Particles.dll 9 | 10 | TARGETS := ${DLLNAME} 11 | 12 | KP_FILES := \ 13 | GradientCfg.cs \ 14 | MinMaxCurveCfg.cs \ 15 | MinMaxGradientCfg.cs \ 16 | ParticleSystemCfg.cs \ 17 | Utils.cs \ 18 | $e 19 | 20 | RESGEN2 := resgen2 21 | CSC := csc 22 | CSCFLAGS := -highentropyva- -noconfig -nostdlib+ -t:library -optimize -optimize -debug -warnaserror 23 | GIT := git 24 | TAR := tar 25 | ZIP := zip 26 | 27 | #all: version ${TARGETS} 28 | all: ${TARGETS} 29 | 30 | .PHONY: version 31 | version: 32 | @./tools/git-version.sh 33 | 34 | info: 35 | @echo "${MODNAME} Build Information" 36 | @echo " resgen2: ${RESGEN2}" 37 | @echo " csc: ${CSC}" 38 | @echo " gmcs flags: ${CSCFLAGS}" 39 | @echo " git: ${GIT}" 40 | @echo " tar: ${TAR}" 41 | @echo " zip: ${ZIP}" 42 | @echo " KSP Data: ${KSPDIR}" 43 | 44 | SYSTEM := \ 45 | -lib:${MANAGED} \ 46 | -r:${MANAGED}/mscorlib.dll \ 47 | -r:${MANAGED}/System.dll \ 48 | -r:${MANAGED}/System.Core.dll 49 | 50 | KSP := \ 51 | -r:Assembly-CSharp.dll \ 52 | -r:Assembly-CSharp-firstpass.dll 53 | 54 | UNITY := \ 55 | -r:UnityEngine.CoreModule.dll \ 56 | -r:UnityEngine.PhysicsModule.dll \ 57 | -r:UnityEngine.ParticleSystemModule.dll \ 58 | $e 59 | 60 | ${DLLNAME}: ${KP_FILES} 61 | ${CSC} ${CSCFLAGS} ${SYSTEM} ${KSP} ${UNITY} -out:$@ $^ 62 | 63 | clean: 64 | rm -f ${TARGETS} *.mdb 65 | 66 | install: all 67 | mkdir -p "${PLUGINDIR}" 68 | cp ${TARGETS} "${PLUGINDIR}" 69 | 70 | .PHONY: all clean install 71 | -------------------------------------------------------------------------------- /Parts/kethane_sensor_1m/part.cfg: -------------------------------------------------------------------------------- 1 | // MMI.K Sensor 01 2 | 3 | PART 4 | { 5 | // --- general parameters --- 6 | name = kethane_sensor_1m 7 | module = Part 8 | author = Dani-Sang 9 | 10 | // --- asset parameters --- 11 | mesh = model.mu 12 | scale = 0.1 13 | 14 | 15 | // --- node definitions --- 16 | node_stack_top = 0.0, -0.25, 0.0, 0.0, -1.0, 0.0 17 | 18 | 19 | // --- editor parameters --- 20 | cost = 2400 21 | category = Science 22 | subcategory = 0 23 | title = KE-S110 Medium Survey Unit 24 | manufacturer = Mechanical Mouse Industries 25 | description = Kethane can be found everywhere in the universe, but to be found, it needs a dedicated sensor. This sensor scans for Kethane pockets in the ground of celestial objects. 26 | 27 | TechRequired = electronics 28 | entryCost = 50 29 | 30 | // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision 31 | attachRules = 1,0,1,0,1 32 | bulkheadProfiles = size1 33 | 34 | // --- standard part parameters --- 35 | mass = 0.5 36 | dragModelType = default 37 | maximum_drag = 0.001 38 | minimum_drag = 0.001 39 | angularDrag = 2 40 | crashTolerance = 6 41 | breakingForce = 50 42 | breakingTorque = 50 43 | maxTemp = 2000 44 | 45 | 46 | // --- Sensor parameters --- 47 | MODULE { 48 | name = KethaneDetector 49 | DetectingPeriod = 0.9 50 | DetectingHeight = 1200000 51 | PowerConsumption = 2.5 52 | Resource 53 | { 54 | Name = Kethane 55 | } 56 | } 57 | 58 | MODULE 59 | { 60 | name = KethaneDetectorAnimator 61 | BaseTransform = Kethane Sensor 62 | HeadingTransform = Horizontal Rotation 63 | ElevationTransform = Vertical Rotation 64 | } 65 | 66 | MODULE 67 | { 68 | name = ModuleKerbNetAccess 69 | MinimumFoV = 3 70 | MaximumFoV = 90 71 | AnomalyDetection = 0.34 72 | DISPLAY_MODES 73 | { 74 | Mode=Kethane,Kethane 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /Shaders/Assets/Shaders/UnlitAdditiveFade.shader: -------------------------------------------------------------------------------- 1 | Shader "Kethane/Unlit/AdditiveFade" 2 | { 3 | Properties 4 | { 5 | _MainTex ("Texture", 2D) = "white" {} 6 | } 7 | SubShader 8 | { 9 | Tags { "Queue"="Transparent" "RenderType"="Transparent" } 10 | Blend One One 11 | ZWrite Off 12 | LOD 100 13 | 14 | Pass 15 | { 16 | CGPROGRAM 17 | #pragma vertex vert 18 | #pragma fragment frag 19 | // make fog work 20 | #pragma multi_compile_fog 21 | 22 | #include "UnityCG.cginc" 23 | 24 | struct appdata 25 | { 26 | float4 vertex : POSITION; 27 | float4 color : COLOR; 28 | float3 uva : TEXCOORD0; 29 | }; 30 | 31 | struct v2f 32 | { 33 | float4 color : COLOR; 34 | float3 uva : TEXCOORD0; 35 | UNITY_FOG_COORDS(1) 36 | float4 vertex : SV_POSITION; 37 | }; 38 | 39 | sampler2D _MainTex; 40 | float4 _MainTex_ST; 41 | 42 | v2f vert (appdata v) 43 | { 44 | v2f o; 45 | o.vertex = UnityObjectToClipPos(v.vertex); 46 | o.uva.xy = TRANSFORM_TEX(v.uva.xy, _MainTex); 47 | o.uva.z = v.uva.z; 48 | o.color = v.color; 49 | UNITY_TRANSFER_FOG(o,o.vertex); 50 | return o; 51 | } 52 | 53 | fixed4 frag (v2f i) : SV_Target 54 | { 55 | // sample the texture 56 | float fade = (1 - i.uva.z) * i.color.a; 57 | fixed4 col = tex2D(_MainTex, i.uva.xy) * i.color * fade * sqrt(sqrt(fade)); 58 | // apply fog 59 | UNITY_APPLY_FOG(i.fogCoord, col); 60 | return col; 61 | } 62 | ENDCG 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Parts/kethane_highGain/part.cfg: -------------------------------------------------------------------------------- 1 | PART 2 | { 3 | // --- general parameters --- 4 | name = kethane_highGain 5 | module = Part 6 | author = Keptin 7 | 8 | // --- asset parameters --- 9 | mesh = model.mu 10 | scale = 1.0 11 | rescaleFactor = 1.5 12 | 13 | // --- node definitions --- 14 | // definition format is Position X, Position Y, Position Z, Up X, Up Y, Up Z 15 | node_attach = 0.0, -0.01, 0.0, 0.0, -1.0, 0.0, 0 16 | node_stack_bottom = 0.0, -0.01, 0.0, 0.0, -1.0, 0.0, 0 17 | 18 | // --- editor parameters --- 19 | cost = 850 20 | category = Science 21 | subcategory = 0 22 | title = KE-S210 Compact Survey Unit 23 | manufacturer = Organization of Kethane Equipment Producers 24 | description = The KE-S210 answered the call for a reliable light weight Kethane scanning unit. Its self-orienting design and radial mounting system make it a versatile Kethane detector with lower power demands. 25 | 26 | TechRequired = scienceTech 27 | entryCost = 50 28 | 29 | // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision 30 | attachRules = 1,1,1,0,0 31 | bulkheadProfiles = size1, srf 32 | 33 | // --- standard part parameters --- 34 | mass = 0.075 35 | dragModelType = default 36 | maximum_drag = 0.3 37 | minimum_drag = 0.3 38 | angularDrag = 0.5 39 | crashTolerance = 10 40 | maxTemp = 2000 41 | fuelCrossFeed = False 42 | 43 | MODULE 44 | { 45 | name = KethaneDetector 46 | DetectingPeriod = 1.5 47 | DetectingHeight = 250000 48 | PowerConsumption = 0.8 49 | Resource 50 | { 51 | Name = Kethane 52 | } 53 | } 54 | 55 | MODULE 56 | { 57 | name = KethaneDetectorAnimator 58 | PartTransform = highGain 59 | BaseTransform = geo_baseHighGain 60 | HeadingTransform = geo_armHighGain 61 | ElevationTransform = geo_dishHighGain 62 | } 63 | 64 | MODULE 65 | { 66 | name = ModuleKerbNetAccess 67 | MinimumFoV = 5 68 | MaximumFoV = 90 69 | AnomalyDetection = 0.2 70 | DISPLAY_MODES 71 | { 72 | Mode=Kethane,Kethane 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /Plugin/Particles/MinMaxGradientCfg.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace KethaneParticles 5 | { 6 | 7 | public class MinMaxGradientCfg 8 | { 9 | public ParticleSystem.MinMaxGradient gradient { get; private set; } 10 | 11 | public MinMaxGradientCfg () 12 | { 13 | gradient = new ParticleSystem.MinMaxGradient (Color.white); 14 | } 15 | 16 | Color ParseColor (string str) 17 | { 18 | var values = Utils.ParseFloatArray (str); 19 | 20 | if (values.Length == 3) { 21 | return new Color (values[0], values[1], values[2]); 22 | } else if (values.Length >= 4) { 23 | return new Color (values[0], values[1], values[2], values[3]); 24 | } 25 | return Color.black; 26 | } 27 | 28 | public void Load (ConfigNode node) 29 | { 30 | if (node.HasNode ("color")) { 31 | var color = node.GetNode ("color"); 32 | var colorMin = color.GetValue ("min"); 33 | var colorMax = color.GetValue ("max"); 34 | gradient = new ParticleSystem.MinMaxGradient (ParseColor (colorMin), ParseColor (colorMax)); 35 | } else if (node.HasValue ("color")) { 36 | var color = ParseColor (node.GetValue ("color")); 37 | gradient = new ParticleSystem.MinMaxGradient (color); 38 | } else if (node.HasNode ("gradientMin") && node.HasNode ("gradientMax")) { 39 | var gradientMin = new GradientCfg (); 40 | var gradientMax = new GradientCfg (); 41 | gradientMin.Load (node.GetNode ("gradientMin")); 42 | gradientMax.Load (node.GetNode ("gradientMax")); 43 | gradient = new ParticleSystem.MinMaxGradient (gradientMin.gradient, gradientMax.gradient); 44 | } else if (node.HasNode ("gradient")) { 45 | var gradient = new GradientCfg (); 46 | gradient.Load (node.GetNode ("gradient")); 47 | this.gradient = new ParticleSystem.MinMaxGradient (gradient.gradient); 48 | } else { 49 | Debug.Log ($"[MinMaxGradientCfg] need one of color (value or node), gradient (node), or gradientMin and gradientMax (node pair)"); 50 | } 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Parts/kethane_1m_converter/part.cfg: -------------------------------------------------------------------------------- 1 | // Kerbal Space Program - Part Config 2 | // MMI.K Converter Medium 3 | 4 | PART 5 | { 6 | // --- general parameters --- 7 | name = kethane_1m_converter 8 | module = Part 9 | author = Dani-Sang 10 | 11 | // --- asset parameters --- 12 | mesh = model.mu 13 | scale = 0.01 14 | 15 | 16 | // --- node definitions --- 17 | node_stack_top = 0.0, 25.0, 0.0, 0.0, 1.0, 0.0 18 | node_stack_bottom = 0.0, -25.0, 0.0, 0.0, -1.0, 0.0 19 | node_attach = 0.0, 0.0, -5.0, 0.0, 0.0, 1.0, 1 20 | 21 | 22 | 23 | // --- editor parameters --- 24 | cost = 2150 25 | category = Utility 26 | subcategory = 0 27 | title = KE-C090 Medium Converter Unit 28 | manufacturer = Mechanical Mouse Industries 29 | description = Designed for maintenance-free operation, the KE-C090 requires no additional cooling, but it lacks the efficiency and convenience of its heavier counterpart. 30 | 31 | TechRequired = fuelSystems 32 | entryCost = 50 33 | 34 | // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision 35 | attachRules = 1,0,1,1,1 36 | bulkheadProfiles = size1 37 | 38 | // --- standard part parameters --- 39 | mass = 0.5 40 | dragModelType = default 41 | maximum_drag = 0.001 42 | minimum_drag = 0.001 43 | angularDrag = 2 44 | crashTolerance = 6 45 | breakingForce = 50 46 | breakingTorque = 50 47 | maxTemp = 2000 48 | fuelCrossFeed = True 49 | 50 | // --- Converter parameters --- 51 | MODULE 52 | { 53 | name = KethaneConverter 54 | Label = Jet Fuel 55 | InputRates 56 | { 57 | Kethane = 1.75 58 | ElectricCharge = 6 59 | } 60 | OutputRatios 61 | { 62 | LiquidFuel = 0.875 63 | MonoPropellant* = 0.075 64 | } 65 | } 66 | 67 | MODULE 68 | { 69 | name = KethaneConverter 70 | Label = Oxidizer 71 | InputRates 72 | { 73 | Kethane = 1.25 74 | ElectricCharge = 14 75 | } 76 | OutputRatios 77 | { 78 | Oxidizer = 0.775 79 | MonoPropellant* = 0.075 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /Plugin/Kethane/BodyResourceData.cs: -------------------------------------------------------------------------------- 1 | using GeodesicGrid; 2 | using System; 3 | using UnityEngine; 4 | 5 | namespace Kethane 6 | { 7 | public class BodyResourceData 8 | { 9 | private readonly CellSet scans; 10 | 11 | public IBodyResources Resources { get; private set; } 12 | 13 | protected BodyResourceData(IBodyResources resources, CellSet scans) 14 | { 15 | Resources = resources; 16 | this.scans = scans; 17 | } 18 | 19 | public bool IsCellScanned(Cell cell) 20 | { 21 | return scans[cell]; 22 | } 23 | 24 | public void ScanCell(Cell cell) 25 | { 26 | scans[cell] = true; 27 | } 28 | 29 | public static BodyResourceData Load(IResourceGenerator generator, CelestialBody body, ConfigNode bodyNode) 30 | { 31 | if (bodyNode == null) { bodyNode = new ConfigNode(); } 32 | var resources = generator.Load(body, bodyNode.GetNode("GeneratorData")); 33 | var scans = new CellSet(KethaneData.GridLevel); 34 | 35 | var scanMask = bodyNode.GetValue("ScanMask"); 36 | if (scanMask != null) 37 | { 38 | try 39 | { 40 | scans = new CellSet(KethaneData.GridLevel, Misc.FromBase64String(scanMask)); 41 | } 42 | catch (FormatException e) 43 | { 44 | Debug.LogError(String.Format("[Kethane] Failed to parse {0} scan string, resetting ({1})", body.name, e.Message)); 45 | } 46 | } 47 | 48 | return new BodyResourceData(resources, scans); 49 | } 50 | 51 | public void Save(ConfigNode bodyNode) 52 | { 53 | bodyNode.AddValue("ScanMask", Misc.ToBase64String(scans.ToByteArray())); 54 | 55 | var node = Resources.Save() ?? new ConfigNode(); 56 | node.name = "GeneratorData"; 57 | bodyNode.AddNode(node); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/TimedMovingAverage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Kethane.PartModules 5 | { 6 | internal class TimedMovingAverage 7 | { 8 | private struct TimedValue 9 | { 10 | public readonly double Time; 11 | public readonly double Value; 12 | public TimedValue(double time, double value) 13 | { 14 | Time = time; 15 | Value = value; 16 | } 17 | } 18 | 19 | private readonly Queue values = new Queue(); 20 | private readonly double interval; 21 | 22 | public TimedMovingAverage(double interval, double initialValue = 0) 23 | { 24 | this.interval = interval; 25 | values.Enqueue(new TimedValue(interval, initialValue)); 26 | } 27 | 28 | public void Update(double time, double value) 29 | { 30 | values.Enqueue(new TimedValue(time, value)); 31 | } 32 | 33 | public double Average 34 | { 35 | get 36 | { 37 | double time = 0; 38 | double value = 0; 39 | int removing = values.Count; 40 | 41 | foreach (var entry in values) 42 | { 43 | removing--; 44 | if (time + entry.Time > interval) 45 | { 46 | value += entry.Value * (interval - time); 47 | break; 48 | } 49 | else 50 | { 51 | time += entry.Time; 52 | value += entry.Value * entry.Time; 53 | } 54 | } 55 | 56 | while (removing > 0) 57 | { 58 | removing--; 59 | values.Dequeue(); 60 | } 61 | 62 | return value / interval; 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Plugin/Kethane/UserInterface/KerbNetModeKethane.cs: -------------------------------------------------------------------------------- 1 | using GeodesicGrid; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Text; 7 | using UnityEngine; 8 | 9 | using KSP.UI.Screens; 10 | using KSP.UI.Dialogs; 11 | using KerbNet; 12 | 13 | namespace Kethane.UserInterface 14 | { 15 | public class KerbNetModeKethane : KerbNetMode 16 | { 17 | CelestialBody body; 18 | BodyResourceData bodyResources; 19 | ResourceDefinition resource; 20 | List resourceDefinitions; 21 | int resourceIndex; 22 | 23 | public KerbNetModeKethane () 24 | { 25 | } 26 | 27 | public override void OnInit () 28 | { 29 | name = "Kethane"; 30 | buttonSprite = Resources.Load("Scanners/resource"); 31 | localCoordinateInfoLabel = "Quantity"; 32 | doTerrainContourPass = true; 33 | doAnomaliesPass = true; 34 | doCoordinatePass = true; 35 | 36 | resourceDefinitions = KethaneController.ResourceDefinitions.ToList(); 37 | resourceIndex = 0; 38 | resource = resourceDefinitions[resourceIndex]; 39 | 40 | customButtonCaption = "Resource: " + resource.Resource; 41 | customButtonCallback = OnResourceClick; 42 | customButtonTooltip = "Cycle Displayed Resource"; 43 | } 44 | 45 | void OnResourceClick () 46 | { 47 | if (++resourceIndex >= resourceDefinitions.Count) { 48 | resourceIndex = 0; 49 | } 50 | resource = resourceDefinitions[resourceIndex]; 51 | customButtonCaption = "Resource: " + resource.Resource; 52 | KerbNetDialog.Instance.ActivateDisplayMode (this); 53 | } 54 | 55 | public override void OnPrecache (Vessel vessel) 56 | { 57 | body = vessel.mainBody; 58 | bodyResources = KethaneData.Current[resource.Resource][body]; 59 | } 60 | 61 | public override Color GetCoordinateColor(Vessel vessel, double currentLatitude, double currentLongitude) 62 | { 63 | Cell cell = MapOverlay.GetCellUnder (body, currentLatitude, currentLongitude); 64 | return MapOverlay.GetCellColor (cell, bodyResources, resource); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The download package (Kethane.zip, Kethane-*a*.*b*.*c*.zip or other variations) is subject to the following copyright notice: 2 | 3 | Copyright (c) Majiir 2012-2014 4 | All rights reserved. 5 | 6 | The Kethane mod (also termed the "Kethane Pack" or simply "Kethane") is comprised of several components with different licensing terms. 7 | 8 | - The Kethane plugin (Kethane.dll) has a license file located at **Plugins/Kethane-LICENSE.txt** (relative to this file). 9 | 10 | - The GeodesicGrid library (GeodesicGrid.dll) has a license file located at **Plugins/GeodesicGrid-LICENSE.txt**. 11 | 12 | - All .cfg and .sfs files included with Kethane are released under the terms of the BSD 2-Clause license with the following copyright notice: 13 | 14 | Copyright (c) Majiir 2012-2014 15 | All rights reserved. 16 | 17 | - Art assets (models, textures and sound files) in the following directories 18 | 19 | - Flags 20 | - Parts/kethane_1m_converter 21 | - Parts/kethane_kerbalBlender 22 | - Parts/kethane_radialDrill 23 | - Parts/kethane_sensor_1m 24 | - Parts/kethane_tank1mLarge 25 | - Parts/kethane_tankExternal 26 | - Sounds 27 | 28 | are subject to the following copyright notice: 29 | 30 | Copyright (c) Dani-Sang 2012-2013 31 | All rights reserved. 32 | 33 | - Art assets (models, textures and sound files) in the following directories 34 | 35 | - Parts/kethane_2m_converter 36 | - Parts/kethane_generator 37 | - Parts/kethane_heavyDrill 38 | - Parts/kethane_highGain 39 | - Parts/kethane_smallDrill 40 | - Parts/kethane_tank1mStandard 41 | - Parts/kethane_tank2mExtralarge 42 | - Parts/kethane_tank2mLarge 43 | - Parts/kethane_tank2mMedium 44 | - Parts/kethane_tank2mSmall 45 | - Parts/kethane_turbine 46 | 47 | are subject to the following copyright notice: 48 | 49 | Copyright (c) Keptin 2012-2014 50 | All rights reserved. 51 | 52 | - All other files included with the download package shall be considered to be subject to the following copyright notice: 53 | 54 | Copyright (c) Majiir 2012-2014 55 | All rights reserved. 56 | 57 | -------------------------------------------------------------------------------- /Parts/kethane_2m_converter/part.cfg: -------------------------------------------------------------------------------- 1 | //Kethane Pack Asset 2 | 3 | PART 4 | { 5 | // --- general parameters --- 6 | name = kethane_2m_converter 7 | module = Part 8 | author = Keptin 9 | 10 | // --- asset parameters --- 11 | mesh = model.mu 12 | rescaleFactor = 1.15 13 | 14 | node_stack_top = 0.0, .308, 0.0, 0.0, 1.0, 0.0, 2 15 | node_stack_bottom = 0.0, -.310, 0.0, 0.0, -1.0, 0.0, 2 16 | 17 | cost = 11350 18 | category = Utility 19 | subcategory = 0 20 | title = KE-C190 Heavy Converter Unit 21 | manufacturer = Organization of Kethane Equipment Producers 22 | description = The KE-C190 features deployable heat sinks to aid in keeping the unit cool during operation. When it's not overheating, this heavy unit can rapidly and efficiently process Kethane into other fuels. 23 | 24 | TechRequired = veryHeavyRocketry 25 | entryCost = 50 26 | 27 | attachRules = 1,0,1,1,0 28 | bulkheadProfiles = size2 29 | 30 | mass = 2 31 | dragModelType = default 32 | maximum_drag = 0.2 33 | minimum_drag = 0.3 34 | angularDrag = 2 35 | crashTolerance = 6 36 | breakingForce = 200 37 | breakingTorque = 200 38 | maxTemp = 2000 39 | fuelCrossFeed = True 40 | 41 | MODULE 42 | { 43 | name = HeatSinkAnimator 44 | HeatAnimation = M2_converter_EmissiveAnimation 45 | OpenAnimation = M2_converter_converting 46 | OpenTemperature = 300 47 | MaxTemperature = 3600 48 | InternalDissipation = 0.04 49 | HeatSinkDissipation = 0.20 50 | PressureDissipation = 0.24 51 | AirSpeedDissipation = 0.009 52 | RadiatorNormal = 0, 0, 1 53 | } 54 | 55 | MODULE 56 | { 57 | name = KethaneConverter 58 | Label = Rocket Fuel 59 | HeatProduction = 1600 60 | InputRates 61 | { 62 | Kethane = 215 63 | ElectricCharge = 165 64 | } 65 | OutputRatios 66 | { 67 | LiquidFuel = 0.4365 68 | Oxidizer = 0.5335 69 | XenonGas* = 0.005 70 | } 71 | } 72 | 73 | MODULE 74 | { 75 | name = KethaneConverter 76 | Label = Monopropellant 77 | HeatProduction = 600 78 | InputRates 79 | { 80 | Kethane = 3 81 | ElectricCharge = 7 82 | } 83 | OutputRatios 84 | { 85 | MonoPropellant = 0.6 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/KethaneParticleDynamics.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using UnityEngine; 4 | 5 | namespace Kethane.PartModules 6 | { 7 | public class KethaneParticleDynamics : PartModule 8 | { 9 | [KSPField(isPersistant = false)] 10 | public string Emitter; 11 | 12 | [KSPField(isPersistant = false)] 13 | public float DampingPressureExponent; 14 | 15 | [KSPField(isPersistant = false)] 16 | public float GravityConstant; 17 | 18 | [KSPField(isPersistant = false)] 19 | public float GravityPressure; 20 | 21 | [KSPField(isPersistant = false)] 22 | public float MaxEnergyConstant; 23 | 24 | [KSPField(isPersistant = false)] 25 | public float MaxEnergyPressure; 26 | 27 | [KSPField(isPersistant = false)] 28 | public Vector3 RandomForcePressure; 29 | 30 | [KSPField(isPersistant = false)] 31 | public float SizeGrowConstant; 32 | 33 | [KSPField(isPersistant = false)] 34 | public float SizeGrowPressureExponent; 35 | 36 | const float AtmkPa = 101.325f; 37 | 38 | private KethaneParticleEmitter emitter; 39 | 40 | public override void OnStart(StartState state) 41 | { 42 | if (state == StartState.Editor) { return; } 43 | emitter = part.Modules.OfType().First(e => e.Label == Emitter); 44 | } 45 | 46 | public override void OnUpdate() 47 | { 48 | if (emitter == null) { return; } 49 | var pressure = (float)FlightGlobals.getStaticPressure(emitter.EmitterTransform.position) / AtmkPa; 50 | emitter.Damping = (float)Math.Exp(DampingPressureExponent * pressure); 51 | emitter.Force = FlightGlobals.getGeeForceAtPosition(emitter.EmitterTransform.position) * (GravityConstant + GravityPressure * pressure); 52 | emitter.MaxEnergy = MaxEnergyConstant + MaxEnergyPressure * pressure; 53 | emitter.RandomForce = RandomForcePressure * pressure; 54 | emitter.SizeGrow = SizeGrowConstant + (float)Math.Exp(SizeGrowPressureExponent * pressure); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Plugin/Kethane/Utilities/ShaderLoader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using UnityEngine; 9 | using UnityEngine.Networking; 10 | using KSPAssets.Loaders; 11 | 12 | using KethaneParticles; 13 | 14 | namespace Kethane.ShaderLoader 15 | { 16 | [KSPAddon(KSPAddon.Startup.Instantly, true)] 17 | public class KethaneShaderLoader: MonoBehaviour 18 | { 19 | const string gamedata = "GameData/Kethane/"; 20 | static Dictionary shaderDictionary = null; 21 | public static bool loaded = false; 22 | 23 | private void Start() 24 | { 25 | ParticleSystemCfg.FindShader = FindShader; 26 | LoadShaders(); 27 | } 28 | 29 | private void LoadShaders() 30 | { 31 | string kethane_bundle = "kethane.ksp"; 32 | 33 | if (shaderDictionary == null) { 34 | shaderDictionary = new Dictionary(); 35 | string root = KSPUtil.ApplicationRootPath; 36 | string path = root + gamedata + kethane_bundle; 37 | 38 | var bundle = AssetBundle.LoadFromFile(path); 39 | if (!bundle) { 40 | Debug.LogFormat("[Kethane] Could not load {0}", 41 | kethane_bundle); 42 | return; 43 | } 44 | 45 | Shader[] shaders = bundle.LoadAllAssets(); 46 | 47 | foreach (Shader shader in shaders) { 48 | Debug.LogFormat ("[Kethane] Shader {0} loaded", 49 | shader.name); 50 | shaderDictionary[shader.name] = shader; 51 | } 52 | bundle.Unload(false); 53 | 54 | loaded = true; 55 | } 56 | } 57 | 58 | public static Shader FindShader(string name) 59 | { 60 | if (shaderDictionary == null) { 61 | Debug.Log("[Kethane] Trying to find shader before assets loaded"); 62 | return null; 63 | } 64 | if (shaderDictionary.ContainsKey(name)) 65 | { 66 | return shaderDictionary[name]; 67 | } 68 | KSPLog.print("[Kethane] Could not find shader " + name); 69 | return null; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | [Tt]emp/ 5 | 6 | # mstest test results 7 | TestResults 8 | 9 | ## Ignore Visual Studio temporary files, build results, and 10 | ## files generated by popular Visual Studio add-ons. 11 | 12 | # User-specific files 13 | *.suo 14 | *.user 15 | *.sln.docstates 16 | *.swp 17 | 18 | # Build results 19 | [Dd]ebug/ 20 | [Rr]elease/ 21 | x64/ 22 | *_i.c 23 | *_p.c 24 | *.ilk 25 | *.obj 26 | *.pch 27 | *.pdb 28 | *.pgc 29 | *.pgd 30 | *.rsp 31 | *.sbr 32 | *.tlb 33 | *.tli 34 | *.tlh 35 | *.tmp 36 | *.log 37 | *.dll 38 | *.mdb 39 | *.vspscc 40 | *.vssscc 41 | .builds 42 | 43 | # Visual C++ cache files 44 | ipch/ 45 | *.aps 46 | *.ncb 47 | *.opensdf 48 | *.sdf 49 | 50 | # Visual Studio profiler 51 | *.psess 52 | *.vsp 53 | *.vspx 54 | 55 | # Guidance Automation Toolkit 56 | *.gpState 57 | 58 | # ReSharper is a .NET coding add-in 59 | _ReSharper* 60 | 61 | # NCrunch 62 | *.ncrunch* 63 | .*crunch*.local.xml 64 | 65 | # Installshield output folder 66 | [Ee]xpress 67 | 68 | # DocProject is a documentation generator add-in 69 | DocProject/buildhelp/ 70 | DocProject/Help/*.HxT 71 | DocProject/Help/*.HxC 72 | DocProject/Help/*.hhc 73 | DocProject/Help/*.hhk 74 | DocProject/Help/*.hhp 75 | DocProject/Help/Html2 76 | DocProject/Help/html 77 | 78 | # Click-Once directory 79 | publish 80 | 81 | # Publish Web Output 82 | *.Publish.xml 83 | 84 | # NuGet Packages Directory 85 | packages 86 | 87 | # Windows Azure Build Output 88 | csx 89 | *.build.csdef 90 | 91 | # Windows Store app package directory 92 | AppPackages/ 93 | 94 | # Others 95 | [Bb]in 96 | [Oo]bj 97 | sql 98 | TestResults 99 | [Tt]est[Rr]esult* 100 | *.Cache 101 | ClientBin 102 | [Ss]tyle[Cc]op.* 103 | ~$* 104 | *.dbmdl 105 | Generated_Code #added for RIA/Silverlight projects 106 | 107 | # Backup & report files from converting an old project file to a newer 108 | # Visual Studio version. Backup files are not needed, because we have git ;-) 109 | _UpgradeReport_Files/ 110 | Backup*/ 111 | UpgradeLog*.XML 112 | 113 | # Windows 114 | Thumbs.db 115 | 116 | # Kethane Utilities Ignores 117 | /ModFolder/ 118 | /KethaneRelease/ 119 | build_plugin.bat 120 | combine_files.bat 121 | -------------------------------------------------------------------------------- /Plugin/Kethane/KethaneController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using UnityEngine; 6 | 7 | namespace Kethane 8 | { 9 | public class KethaneController 10 | { 11 | private static IEnumerable resourceDefinitions = null; 12 | 13 | public static IEnumerable ResourceDefinitions 14 | { 15 | get 16 | { 17 | if (resourceDefinitions == null) 18 | { 19 | resourceDefinitions = loadResourceDefinitions(); 20 | } 21 | return resourceDefinitions; 22 | } 23 | } 24 | 25 | private static IEnumerable loadResourceDefinitions() 26 | { 27 | var defs = new SortedDictionary(); 28 | 29 | foreach (var definition in GameDatabase.Instance.GetConfigNodes("KethaneResource").Select(TryLoadResourceDefinition).Where(d => d != null)) 30 | { 31 | if (!PartResourceLibrary.Instance.resourceDefinitions.Contains(definition.Resource)) 32 | { 33 | Debug.LogWarning(String.Format("[Kethane] {0} is an unknown resource, ignoring", definition.Resource)); 34 | } 35 | else if (defs.ContainsKey(definition.Resource)) 36 | { 37 | Debug.LogWarning(String.Format("[Kethane] Duplicate definition for {0}, ignoring", definition.Resource)); 38 | } 39 | else 40 | { 41 | defs[definition.Resource] = definition; 42 | } 43 | } 44 | 45 | Debug.Log(String.Format("[Kethane] Loaded {0} resource definitions", defs.Count)); 46 | return new ReadOnlyCollection(defs.Values.ToArray()); 47 | } 48 | 49 | private static ResourceDefinition TryLoadResourceDefinition(ConfigNode node) 50 | { 51 | try 52 | { 53 | return new ResourceDefinition(node); 54 | } 55 | catch (Exception e) 56 | { 57 | Debug.LogError(String.Format("[Kethane] Error loading resource definition:\n\n{0}", e)); 58 | return null; 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Plugin/Kethane/Utilities/KopernicusWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using UnityEngine; 7 | 8 | namespace Kethane.Utilities.Kopernicus 9 | { 10 | public class Templates { 11 | static Type KopernicusTemplates_class; 12 | static FieldInfo kop_menuBody; 13 | 14 | public static String MenuBody 15 | { 16 | get { return (String)kop_menuBody.GetValue(null); } 17 | } 18 | 19 | internal static bool Initialize (Assembly kopAsm) 20 | { 21 | var types = kopAsm.GetTypes (); 22 | 23 | foreach (var t in types) { 24 | if (t.Name == "Templates") { 25 | KopernicusTemplates_class = t; 26 | kop_menuBody = KopernicusTemplates_class.GetField ("MenuBody", BindingFlags.Public | BindingFlags.Static); 27 | Debug.Log ($"[Kethane] Kopernicus.Templates `{kop_menuBody}'"); 28 | return true; 29 | } 30 | } 31 | return false; 32 | } 33 | } 34 | 35 | public class Events { 36 | static Type KopernicusEvents_class; 37 | static PropertyInfo kop_oruum; 38 | 39 | public static EventVoid OnRuntimeUtilityUpdateMenu 40 | { 41 | get { return (EventVoid)kop_oruum.GetValue(null, null); } 42 | } 43 | 44 | internal static bool Initialize (Assembly kopAsm) 45 | { 46 | var types = kopAsm.GetTypes (); 47 | 48 | foreach (var t in types) { 49 | if (t.Name == "Events") { 50 | KopernicusEvents_class = t; 51 | kop_oruum = KopernicusEvents_class.GetProperty ("OnRuntimeUtilityUpdateMenu", BindingFlags.Public | BindingFlags.Static); 52 | Debug.Log ($"[Kethane] Kopernicus.Events `{kop_oruum}'"); 53 | return true; 54 | } 55 | } 56 | return false; 57 | } 58 | } 59 | 60 | public class KopernicusWrapper { 61 | static bool inited = false; 62 | static bool haveKopernicus = false; 63 | 64 | public static bool Initialize () 65 | { 66 | if (!inited) { 67 | inited = true; 68 | AssemblyLoader.LoadedAssembly kopAsm = null; 69 | 70 | foreach (var la in AssemblyLoader.loadedAssemblies) { 71 | if (la.assembly.GetName ().Name.Equals ("Kopernicus", StringComparison.InvariantCultureIgnoreCase)) { 72 | kopAsm = la; 73 | } 74 | } 75 | if (kopAsm != null) { 76 | Debug.Log ($"[Kethane] found Kopernicus {kopAsm}"); 77 | Events.Initialize (kopAsm.assembly); 78 | Templates.Initialize (kopAsm.assembly); 79 | haveKopernicus = true; 80 | } 81 | } 82 | return haveKopernicus; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Parts/kethane_smallDrill/part.cfg: -------------------------------------------------------------------------------- 1 | PART 2 | { 3 | // --- general parameters --- 4 | name = kethane_smallDrill 5 | module = Part 6 | author = keptin 7 | 8 | // --- asset parameters --- 9 | mesh = model.mu 10 | rescaleFactor = 0.014 11 | 12 | 13 | // --- node definitions --- 14 | node_attach = 3.0, 0.0, 0.0, 1.0, 0.0, 0.0 15 | 16 | 17 | // --- editor parameters --- 18 | cost = 2350 19 | category = Utility 20 | subcategory = 0 21 | title = KE-X130 External Drilling Unit 22 | manufacturer = Organization of Kethane Equipment Producers 23 | description = This externally mounted housing contains a drill for extracting Kethane from the surface of a planet or moon. 24 | 25 | TechRequired = advConstruction 26 | entryCost = 50 27 | 28 | // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision 29 | attachRules = 0,1,0,0,0 30 | bulkheadProfiles = srf 31 | 32 | // --- standard part parameters --- 33 | mass = 1.0 34 | dragModelType = default 35 | maximum_drag = 0.001 36 | minimum_drag = 0.001 37 | angularDrag = 2 38 | crashTolerance = 6 39 | breakingForce = 50 40 | breakingTorque = 50 41 | maxTemp = 2000 42 | fuelCrossFeed = False 43 | 44 | MODULE 45 | { 46 | name = KethaneExtractor 47 | PowerConsumption = 8 48 | Resource 49 | { 50 | Name = Kethane 51 | Rate = 1.25 52 | } 53 | HeadTransform = shaftSmall_geo 54 | TailTransform = drillMotor_geo 55 | } 56 | 57 | MODULE 58 | { 59 | name = KethaneDrillAnimator 60 | DeployAnimation = deployRetract 61 | DrillAnimation = drilling 62 | } 63 | 64 | MODULE 65 | { 66 | name = KethaneParticleEmitter 67 | Label = gas 68 | System = cloud 69 | 70 | Spread = 0.1, 0.1 71 | } 72 | 73 | MODULE 74 | { 75 | name = KethaneParticleEmitter 76 | Label = sparks 77 | System = sparks 78 | 79 | Spread = 0.1, 0.1 80 | ColorAnimation1 = 0.8, 0.8, 0.8 81 | ColorAnimation2 = 0.8, 0.8, 0.8 82 | ColorAnimation3 = 0.8, 0.8, 0.8 83 | ColorAnimation4 = 0.6517648, 0.3835294, 0.2023529 84 | ColorAnimation5 = 0, 0, 0 85 | 86 | ShaderName = Particles/Additive 87 | TextureName = Kethane/mote 88 | 89 | Emit = False 90 | MaxParticleSize = 1 91 | UseWorldSpace = True 92 | 93 | EmitterScale = 0.2, 0, 0.2 94 | LocalVelocity = 0, 5, 0 95 | RandomVelocity = 1.5, 3.5, 1.5 96 | 97 | MaxEmission = 1000 98 | MinEmission = 500 99 | MinEnergy = 0.1 100 | MaxSize = 0.02 101 | MinSize = 0.01 102 | 103 | RenderMode = Stretch 104 | VelocityScale = 0.08 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /Plugin/Kethane/VesselModules/KethaneProtoDetector.cs: -------------------------------------------------------------------------------- 1 | using Kethane.UserInterface; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | 7 | using Kethane.PartModules; 8 | 9 | namespace Kethane.VesselModules 10 | { 11 | public class KethaneProtoDetector 12 | { 13 | public float DetectingPeriod; 14 | public float DetectingHeight; 15 | public float PowerConsumption; 16 | public List resources; 17 | KethaneVesselScanner vesselScanner; 18 | bool isDetecting; 19 | public bool IsDetecting 20 | { 21 | get { 22 | return isDetecting; 23 | } 24 | set { 25 | isDetecting = value; 26 | vesselScanner.UpdateDetecting(); 27 | } 28 | } 29 | public double TimerEcho; 30 | public double powerRatio; 31 | 32 | public KethaneProtoDetector (KethaneDetector det, 33 | KethaneVesselScanner scanner) 34 | { 35 | vesselScanner = scanner; 36 | DetectingPeriod = det.DetectingPeriod; 37 | DetectingHeight = det.DetectingHeight; 38 | PowerConsumption = det.PowerConsumption; 39 | resources = det.resources; 40 | IsDetecting = det.IsDetecting; 41 | TimerEcho = 0; 42 | } 43 | 44 | public KethaneProtoDetector (ConfigNode node, 45 | KethaneVesselScanner scanner) 46 | { 47 | vesselScanner = scanner; 48 | string s; 49 | if (node.HasValue ("DetectingPeriod")) { 50 | s = node.GetValue ("DetectingPeriod"); 51 | float.TryParse (s, out DetectingPeriod); 52 | } 53 | if (node.HasValue ("DetectingHeight")) { 54 | s = node.GetValue ("DetectingHeight"); 55 | float.TryParse (s, out DetectingHeight); 56 | } 57 | if (node.HasValue ("PowerConsumption")) { 58 | s = node.GetValue ("PowerConsumption"); 59 | float.TryParse (s, out PowerConsumption); 60 | } 61 | if (node.HasValue ("IsDetecting")) { 62 | s = node.GetValue ("IsDetecting"); 63 | bool val; 64 | bool.TryParse (s, out val); 65 | IsDetecting = val; 66 | } 67 | if (node.HasValue ("TimerEcho")) { 68 | s = node.GetValue ("TimerEcho"); 69 | double.TryParse (s, out TimerEcho); 70 | } 71 | resources = node.GetNodes("Resource").Select(n => n.GetValue("Name")).ToList(); 72 | } 73 | 74 | public void Save (ConfigNode node) 75 | { 76 | var n = node.AddNode ("Detector"); 77 | n.AddValue ("DetectingPeriod", DetectingPeriod); 78 | n.AddValue ("DetectingHeight", DetectingHeight); 79 | n.AddValue ("PowerConsumption", PowerConsumption); 80 | n.AddValue ("IsDetecting", IsDetecting); 81 | n.AddValue ("TimerEcho", TimerEcho); 82 | foreach (var res in resources) { 83 | var resnode = n.AddNode ("Resource"); 84 | resnode.AddValue("Name", res); 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Parts/kethane_generator/part.cfg: -------------------------------------------------------------------------------- 1 | PART 2 | { 3 | 4 | name = kethane_generator 5 | module = Part 6 | author = Keptin 7 | 8 | rescaleFactor = 1 9 | 10 | node_stack_top = 0.0, 0.40, 0.0, 0.0, 1.0, 0.0, 1 11 | node_stack_bottom = 0.0, -0.40, 0.0, 0.0, -1.0, 0.0, 1 12 | 13 | cost = 1900 14 | category = Utility 15 | subcategory = 0 16 | title = KE-G35 Kethoelectric Reactor Unit 17 | manufacturer = Organization of Kethane Equipment Producers 18 | description = Constructed from fragments of destroyed solar panels, the KE-G35 is a remarkably efficient Kethane-powered electrical generator. The exhaust manifold captures and stores some of the Xenon gas dissolved in the fuel, making this a versatile unit for exploration and drilling. The generator automatically regulates its output in order to save fuel. 19 | 20 | TechRequired = experimentalElectrics 21 | entryCost = 50 22 | 23 | attachRules = 1,0,1,0,0 24 | bulkheadProfiles = size1 25 | 26 | mass = 0.3 27 | dragModelType = default 28 | maximum_drag = 0.2 29 | minimum_drag = 0.3 30 | angularDrag = 2 31 | crashTolerance = 6 32 | breakingForce = 50 33 | breakingTorque = 50 34 | maxTemp = 2000 35 | fuelCrossFeed = True 36 | 37 | MODEL 38 | { 39 | model = Kethane/Parts/kethane_generator/model 40 | position = 0, -0.40, 0 41 | scale = 0.61, 0.61, 0.61 42 | rotation = 0, 0, 0 43 | } 44 | 45 | MODULE 46 | { 47 | name = KethaneGenerator 48 | KethaneRate = 0.5 49 | PowerRate = 75 50 | XenonMassRatio = 0.04 51 | MaxEmission = 300 52 | MinEmission = 100 53 | } 54 | 55 | MODULE 56 | { 57 | name = KethaneParticleEmitter 58 | Label = exhaust 59 | 60 | ColorAnimation1 = 0.02, 0.021, 0.024 61 | ColorAnimation2 = 0.015, 0.016, 0.017 62 | ColorAnimation3 = 0.004, 0.004, 0.005 63 | ColorAnimation4 = 0.001, 0.001, 0.0014 64 | ColorAnimation5 = 0, 0, 0 65 | 66 | ShaderName = Particles/Additive 67 | TextureName = Kethane/smoke 68 | 69 | MaxParticleSize = 10 70 | UseWorldSpace = True 71 | 72 | EmitterPosition = 0, 0.25, 0.5 73 | EmitterScale = 0.1, 0.1, 0.1 74 | LocalVelocity = 0, -0.5, 3 75 | RandomVelocity = 2.5, 2.5, 2.5 76 | 77 | MinEnergy = 0.25 78 | MaxSize = 0.75 79 | MinSize = 0.25 80 | 81 | AngularVelocity = 0 82 | RandomAngularVelocity = 25 83 | RandomRotation = True 84 | } 85 | 86 | MODULE 87 | { 88 | name = KethaneParticleDynamics 89 | Emitter = exhaust 90 | DampingPressureExponent = -2.3 91 | GravityConstant = 1 92 | GravityPressure = -1.5 93 | MaxEnergyConstant = 2 94 | MaxEnergyPressure = 1.5 95 | RandomForcePressure = 16, 16, 16 96 | SizeGrowConstant = 0.25 97 | SizeGrowPressureExponent = -2.7 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/KethaneDetectorAnimator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace Kethane.PartModules 5 | { 6 | public class KethaneDetectorAnimator : PartModule, IDetectorAnimator 7 | { 8 | [KSPField(isPersistant = false)] 9 | public string BaseTransform; 10 | 11 | [KSPField(isPersistant = false)] 12 | public string PartTransform; 13 | 14 | [KSPField(isPersistant = false)] 15 | public string HeadingTransform; 16 | 17 | [KSPField(isPersistant = false)] 18 | public string ElevationTransform; 19 | 20 | public bool IsDetecting { get; set; } 21 | 22 | public float PowerRatio { get; set; } 23 | 24 | public override void OnUpdate() 25 | { 26 | CelestialBody body = this.vessel.mainBody; 27 | if (body == null) 28 | return; 29 | 30 | var BaseT = this.part.transform.Find("model"); 31 | 32 | if (!String.IsNullOrEmpty(PartTransform)) 33 | { 34 | BaseT = BaseT.Find(PartTransform); 35 | } 36 | 37 | BaseT = BaseT.Find(BaseTransform); 38 | 39 | Vector3 bodyCoords = BaseT.InverseTransformPoint(body.transform.position); 40 | Vector2 pos = cartesianToPolar(bodyCoords); 41 | 42 | var alpha = (float)normalizeAngle(pos.x + 90); 43 | var beta = (float)normalizeAngle(pos.y); 44 | 45 | Transform RotH = BaseT.Find(HeadingTransform); 46 | Transform RotV = RotH.Find(ElevationTransform); 47 | 48 | if (Math.Abs(RotH.localEulerAngles.y - beta) > 90) 49 | { 50 | beta += 180; 51 | alpha = 360 - alpha; 52 | } 53 | 54 | var speed = Time.deltaTime * PowerRatio * 60; 55 | 56 | RotH.localRotation = Quaternion.RotateTowards(RotH.localRotation, Quaternion.AngleAxis(beta, new Vector3(0, 1, 0)), speed); 57 | RotV.localRotation = Quaternion.RotateTowards(RotV.localRotation, Quaternion.AngleAxis(alpha, new Vector3(1, 0, 0)), speed); 58 | 59 | if (float.IsNaN(RotH.localRotation.w)) { RotH.localRotation = Quaternion.identity; } 60 | if (float.IsNaN(RotV.localRotation.w)) { RotV.localRotation = Quaternion.identity; } 61 | } 62 | 63 | private static double normalizeAngle(double a) 64 | { 65 | a = a % 360; 66 | if (a < 0) 67 | a += 360; 68 | return a; 69 | } 70 | 71 | private static Vector2 cartesianToPolar(Vector3 point) 72 | { 73 | Vector2 polar = new Vector2(); 74 | polar.y = Mathf.Atan2(point.x, point.z); 75 | float xzLen = new Vector2(point.x, point.z).magnitude; 76 | polar.x = Mathf.Atan2(-point.y, xzLen); 77 | polar *= Mathf.Rad2Deg; 78 | return polar; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Plugin/Kethane/KethaneData.cs: -------------------------------------------------------------------------------- 1 | using GeodesicGrid; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | 7 | namespace Kethane 8 | { 9 | [KSPScenario(ScenarioCreationOptions.AddToAllGames, 10 | GameScenes.SPACECENTER, GameScenes.FLIGHT, 11 | GameScenes.TRACKSTATION)] 12 | public class KethaneData : ScenarioModule 13 | { 14 | public const int GridLevel = 5; 15 | 16 | public static KethaneData Current { get; private set; } 17 | 18 | private Dictionary resources = new Dictionary(); 19 | 20 | public ResourceData this[string resourceName] 21 | { 22 | get { return resources[resourceName]; } 23 | } 24 | 25 | public void ResetGeneratorConfig(ResourceDefinition resource) 26 | { 27 | resources[resource.Resource] = Kethane.ResourceData.Load(resource, new ConfigNode()); 28 | } 29 | 30 | public override void OnLoad(ConfigNode config) 31 | { 32 | var timer = System.Diagnostics.Stopwatch.StartNew(); 33 | 34 | resources.Clear(); 35 | 36 | var resourceNodes = config.GetNodes("Resource"); 37 | 38 | foreach (var resource in KethaneController.ResourceDefinitions) 39 | { 40 | var resourceName = resource.Resource; 41 | var resourceNode = resourceNodes.SingleOrDefault(n => n.GetValue("Resource") == resourceName) ?? new ConfigNode(); 42 | resources[resourceName] = Kethane.ResourceData.Load(resource, resourceNode); 43 | } 44 | 45 | timer.Stop(); 46 | Debug.LogWarning(String.Format("Kethane deposits loaded ({0}ms)", timer.ElapsedMilliseconds)); 47 | if (UserInterface.MapOverlay.Instance != null) { 48 | UserInterface.MapOverlay.Instance.ClearBody (); 49 | } 50 | } 51 | 52 | public override void OnSave(ConfigNode configNode) 53 | { 54 | var timer = System.Diagnostics.Stopwatch.StartNew(); 55 | 56 | configNode.AddValue("Version", System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).ProductVersion); 57 | 58 | foreach (var resource in resources) 59 | { 60 | var resourceNode = new ConfigNode("Resource"); 61 | resourceNode.AddValue("Resource", resource.Key); 62 | resource.Value.Save(resourceNode); 63 | configNode.AddNode(resourceNode); 64 | } 65 | 66 | timer.Stop(); 67 | Debug.LogWarning(String.Format("Kethane deposits saved ({0}ms)", timer.ElapsedMilliseconds)); 68 | } 69 | 70 | public override void OnAwake () 71 | { 72 | Current = this; 73 | Debug.LogFormat("[KethaneData] OnAwake"); 74 | } 75 | 76 | void OnDestroy () 77 | { 78 | Current = null; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Plugin/Particles/MinMaxCurveCfg.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace KethaneParticles 5 | { 6 | 7 | public class MinMaxCurveCfg 8 | { 9 | public ParticleSystem.MinMaxCurve curve { get; private set; } 10 | 11 | public MinMaxCurveCfg () 12 | { 13 | curve = new ParticleSystem.MinMaxCurve (0); 14 | } 15 | 16 | AnimationCurve ParseCurve (ConfigNode node) 17 | { 18 | var keys = node.GetValues("key"); 19 | var frames = new Keyframe[keys.Length]; 20 | for (int i = 0; i < keys.Length; i++) { 21 | var values = Utils.ParseFloatArray (keys[i]); 22 | if (values.Length >= 2) { 23 | frames[i] = new Keyframe (values[0], values[1]); 24 | } else { 25 | Debug.Log ($"[MinMaxCurveCfg] key {i}: need 2 or 4 values: {values}"); 26 | frames[i] = new Keyframe (0, 0); 27 | } 28 | if (values.Length >= 4) { 29 | frames[i].inTangent = values[2]; 30 | frames[i].outTangent = values[3]; 31 | if (values.Length > 4) { 32 | Debug.Log ($"[MinMaxCurveCfg] key {i}: ignorning extra values: {values}"); 33 | } 34 | } else if (values.Length == 3) { 35 | Debug.Log ($"[MinMaxCurveCfg] key {i}: ignorning extra value (missing out-tangent value?): {values}"); 36 | } 37 | } 38 | return new AnimationCurve (frames); 39 | } 40 | 41 | public void Load (ConfigNode node) 42 | { 43 | float scalar = 1; 44 | if (node.HasValue ("scalar")) { 45 | if (!float.TryParse (node.GetValue ("scalar"), out scalar)) { 46 | scalar = 1; 47 | } 48 | } 49 | if (node.HasNode ("curve")) { 50 | var curve = ParseCurve (node.GetNode ("curve")); 51 | if (curve != null) { 52 | this.curve = new ParticleSystem.MinMaxCurve (scalar, curve); 53 | } else { 54 | Debug.Log ("[MinMaxCurveCfg] bogus curve"); 55 | } 56 | } else if (node.HasNode ("minCurve") && node.HasNode ("maxCurve")) { 57 | var minCurve = ParseCurve (node.GetNode ("minCurve")); 58 | var maxCurve = ParseCurve (node.GetNode ("maxCurve")); 59 | if (minCurve != null && maxCurve != null) { 60 | curve = new ParticleSystem.MinMaxCurve (scalar, minCurve, maxCurve); 61 | } else { 62 | Debug.Log ("[MinMaxCurveCfg] bogus minCurve or maxCurve"); 63 | } 64 | } else if (node.HasValue ("constant")) { 65 | var constant = node.GetValue ("constant"); 66 | var values = Utils.ParseFloatArray (constant); 67 | if (values.Length == 1) { 68 | curve = new ParticleSystem.MinMaxCurve (values[0]); 69 | } else if (values.Length >= 2) { 70 | curve = new ParticleSystem.MinMaxCurve (values[0], values[1]); 71 | if (values.Length > 2) { 72 | Debug.Log ($"[MinMaxCurveCfg] ignoring excess values: {constant}"); 73 | } 74 | } else { 75 | Debug.Log ($"[MinMaxCurveCfg] need 1 or two values: {constant}"); 76 | } 77 | } else { 78 | Debug.Log ($"[MinMaxCurveCfg] need one of constant (value), curve (node), or minCurve and maxCurve (node pair)"); 79 | } 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/KethaneParticleEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using UnityEngine; 4 | 5 | using KethaneParticles; 6 | 7 | namespace Kethane.PartModules 8 | { 9 | public class KethaneParticleEmitter : PartModule 10 | { 11 | ParticleManager.Emitter emitter; 12 | 13 | bool bad; 14 | public bool Emit 15 | { 16 | get { 17 | if (emitter != null) { 18 | return emitter.enabled; 19 | } 20 | return false; 21 | } 22 | set { 23 | if (!bad && value && emitter == null) { 24 | emitter = ParticleManager.CreateEmitter (System); 25 | if (emitter == null) { 26 | bad = true; 27 | return; 28 | } 29 | emitter.position = position; 30 | emitter.direction = direction; 31 | Utils.CalcAxes (emitter.direction, out emitter.spreadX, out emitter.spreadY); 32 | emitter.spreadX *= spread.x; 33 | emitter.spreadY *= spread.y; 34 | } 35 | if (emitter != null) { 36 | emitter.enabled = value; 37 | } 38 | } 39 | } 40 | 41 | public float Rate 42 | { 43 | get { 44 | if (emitter != null) { 45 | return emitter.rate; 46 | } 47 | return 0; 48 | } 49 | set { 50 | if (emitter == null) { 51 | Emit = false; 52 | } 53 | if (emitter != null) { 54 | emitter.rate = value; 55 | } 56 | } 57 | } 58 | 59 | public Vector3 Position 60 | { 61 | get { 62 | if (emitter != null) { 63 | return emitter.position; 64 | } 65 | return position; 66 | } 67 | set { 68 | if (emitter == null) { 69 | Emit = false; 70 | } 71 | if (emitter != null) { 72 | emitter.position = value; 73 | } 74 | position = value; 75 | } 76 | } 77 | 78 | public Vector3 Direction 79 | { 80 | get { 81 | if (emitter != null) { 82 | return emitter.direction; 83 | } 84 | return Vector3.up; 85 | } 86 | set { 87 | if (emitter == null) { 88 | Emit = false; 89 | } 90 | if (emitter != null) { 91 | emitter.direction = value; 92 | } 93 | } 94 | } 95 | 96 | [KSPField(isPersistant = false)] 97 | public string Label; 98 | 99 | [KSPField(isPersistant = false)] 100 | public string System; 101 | 102 | [KSPField(isPersistant = false)] 103 | public Vector3 position; 104 | 105 | [KSPField(isPersistant = false)] 106 | public Vector3 direction = Vector3.up; 107 | 108 | [KSPField(isPersistant = false)] 109 | public Vector2 spread; 110 | 111 | public override void OnLoad(ConfigNode config) 112 | { 113 | } 114 | 115 | public override void OnSave(ConfigNode config) 116 | { 117 | } 118 | 119 | public override void OnStart(StartState state) 120 | { 121 | if (!HighLogic.LoadedSceneIsFlight) { return; } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Parts/kethane_turbine/part.cfg: -------------------------------------------------------------------------------- 1 | // Kerbal Space Program - Part Config 2 | 3 | PART 4 | { 5 | // --- general parameters --- 6 | name = kethane_turbine 7 | module = Part 8 | author = Keptin 9 | 10 | // --- asset parameters --- 11 | mesh = model.mu 12 | rescaleFactor = 0.65 13 | 14 | // --- node definitions --- 15 | node_stack_top = 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 16 | 17 | // --- FX definitions --- 18 | fx_exhaustLight_blue = 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, power 19 | fx_smokeTrail_light = 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, power 20 | fx_exhaustSparks_flameout = 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, flameout 21 | 22 | // --- Sound FX definition --- 23 | sound_vent_medium = engage 24 | sound_jet_low = running 25 | sound_jet_deep = power 26 | sound_vent_soft = disengage 27 | sound_explosion_low = flameout 28 | 29 | // --- editor parameters --- 30 | cost = 4890 31 | category = Propulsion 32 | subcategory = 0 33 | title = KE-J65 Zero-Bypass Turbine 34 | manufacturer = Organization of Kethane Equipment Producers 35 | description = The KE-J65's ability to operate in non-oxygenated atmospheres gives it tremendous utility for exploring the solar system. It's a bulky unit and the integrated air intake that gets irritable at high speeds, but while cruising it's an efficient engine with a fantastic paint job. 36 | 37 | TechRequired = aerospaceTech 38 | entryCost = 50 39 | 40 | // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision 41 | attachRules = 1,0,1,0,0 42 | bulkheadProfiles = size1 43 | 44 | // --- standard part parameters --- 45 | mass = 1.75 46 | dragModelType = default 47 | maximum_drag = 0.2 48 | minimum_drag = 0.2 49 | angularDrag = 2 50 | crashTolerance = 7 51 | maxTemp = 2200 52 | 53 | MODULE 54 | { 55 | name = ModuleEngines 56 | thrustVectorTransformName = thrustTransform 57 | exhaustDamage = True 58 | ignitionThreshold = 0.04 59 | minThrust = 0 60 | maxThrust = 200 61 | heatProduction = 700 62 | useEngineResponseTime = True 63 | engineAccelerationSpeed = 0.08 64 | engineDecelerationSpeed = 0.52 65 | useVelocityCurve = True 66 | fxOffset = 0, 0, 0.7 67 | PROPELLANT 68 | { 69 | name = Kethane 70 | ratio = 1 71 | DrawGauge = True 72 | } 73 | PROPELLANT 74 | { 75 | name = KIntakeAir 76 | ratio = 15 77 | } 78 | atmosphereCurve 79 | { 80 | key = 0 1000 81 | key = 0.45 1900 82 | key = 1 2300 83 | } 84 | velocityCurve 85 | { 86 | key = 1000 0 0 0 87 | key = 850 0.2 0 0 88 | key = 0 1 0 0 89 | } 90 | } 91 | 92 | MODULE 93 | { 94 | name = OrthogonalIntake 95 | Resource = KIntakeAir 96 | BaseFlowRate = 0.8 97 | PowerFlowRate = 7 98 | SpeedFlowRate = -0.01 99 | } 100 | 101 | RESOURCE 102 | { 103 | name = KIntakeAir 104 | amount = 0 105 | maxAmount = 1.0 106 | } 107 | 108 | MODULE 109 | { 110 | name = ModuleAnimateHeat 111 | ThermalAnim = centrifugalTurbine_EmissiveAnimation 112 | } 113 | 114 | MODULE 115 | { 116 | name = ModuleAlternator 117 | RESOURCE 118 | { 119 | name = ElectricCharge 120 | rate = 1.8 121 | } 122 | } 123 | RESOURCE 124 | { 125 | name = ElectricCharge 126 | amount = 0 127 | maxAmount = 0 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /Plugin/Kethane/Generators/CellularResourceGenerator.cs: -------------------------------------------------------------------------------- 1 | using GeodesicGrid; 2 | using System; 3 | using System.Linq; 4 | 5 | namespace Kethane.Generators 6 | { 7 | public abstract class CellularResourceGenerator : IResourceGenerator 8 | { 9 | public IBodyResources Load(CelestialBody body, ConfigNode node) 10 | { 11 | if (node == null) 12 | { 13 | var amounts = new CellMap(KethaneData.GridLevel); 14 | Initialize(body, amounts); 15 | return new BodyResources(new CellMap(amounts)); 16 | } 17 | else 18 | { 19 | var bytes = Misc.FromBase64String(node.GetValue("amounts")); 20 | ensureBigEndian(bytes); 21 | 22 | var amounts = new CellMap(KethaneData.GridLevel); 23 | var count = Cell.CountAtLevel(KethaneData.GridLevel); 24 | for (uint i = 0; i < count; i++) { 25 | amounts[new Cell(i)] = BitConverter.ToDouble(bytes, (int)i * 8); 26 | } 27 | 28 | return new BodyResources(amounts); 29 | } 30 | } 31 | 32 | public abstract void Initialize(CelestialBody body, CellMap amounts); 33 | 34 | private static void ensureBigEndian(byte[] bytes) 35 | { 36 | if (BitConverter.IsLittleEndian) 37 | { 38 | for (var i = 0; i < bytes.Length; i += 8) 39 | { 40 | for (var j = 0; j < 4; j++) 41 | { 42 | var temp = bytes[i + j]; 43 | bytes[i + j] = bytes[i + 7 - j]; 44 | bytes[i + 7 - j] = temp; 45 | } 46 | } 47 | } 48 | } 49 | 50 | private class BodyResources : IBodyResources 51 | { 52 | private readonly CellMap amounts; 53 | 54 | public BodyResources(CellMap amounts) 55 | { 56 | this.amounts = amounts; 57 | MaxQuantity = amounts.Max(p => p.Value); 58 | } 59 | 60 | public ConfigNode Save() 61 | { 62 | var count = Cell.CountAtLevel(KethaneData.GridLevel); 63 | 64 | var bytes = new byte[count * 8]; 65 | for (uint i = 0; i < count; i++) { 66 | var b = BitConverter.GetBytes(amounts[new Cell(i)]); 67 | for (int j = 0; j < 8; j++) 68 | { 69 | bytes[i * 8 + j] = b[j]; 70 | } 71 | } 72 | 73 | ensureBigEndian(bytes); 74 | 75 | var node = new ConfigNode(); 76 | node.AddValue("amounts", Misc.ToBase64String(bytes)); 77 | return node; 78 | } 79 | 80 | public double MaxQuantity { get; private set; } 81 | 82 | public double? GetQuantity(Cell cell) 83 | { 84 | double? amount = amounts[cell]; 85 | return amount > 0 ? amount : null; 86 | } 87 | 88 | public double Extract(Cell cell, double amount) 89 | { 90 | var current = amounts[cell]; 91 | var delta = Math.Min(current, Math.Max(0, amount)); 92 | amounts[cell] = current - delta; 93 | return delta; 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Plugin/Kethane/Particles/ParticleManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | using KethaneParticles; 7 | 8 | namespace Kethane 9 | { 10 | [KSPAddon (KSPAddon.Startup.Flight, false)] 11 | public class ParticleManager : MonoBehaviour 12 | { 13 | public class System { 14 | public ParticleSystem psystem; 15 | public ParticlePhysics physics; 16 | } 17 | 18 | public class Emitter 19 | { 20 | public bool enabled; 21 | public float rate; 22 | public Vector3 position; 23 | public Vector3 direction; 24 | public Vector3 spreadX; 25 | public Vector3 spreadY; 26 | public System system; 27 | 28 | public float emission; 29 | } 30 | 31 | static Dictionary systemCfg; 32 | static ParticleManager instance; 33 | 34 | Dictionary systems; 35 | 36 | Emitter createEmitter (string system) 37 | { 38 | System sys; 39 | if (!systems.TryGetValue (system, out sys)) { 40 | sys = createSystem (system); 41 | if (sys == null) { 42 | return null; 43 | } 44 | systems[system] = sys; 45 | } 46 | var emitter = new Emitter (); 47 | emitter.system = sys; 48 | sys.physics.AddEmitter (emitter); 49 | return emitter; 50 | } 51 | 52 | public static Emitter CreateEmitter (string system) 53 | { 54 | return instance.createEmitter (system); 55 | } 56 | 57 | static System createSystem (string name) 58 | { 59 | ConfigNode node; 60 | System sys; 61 | 62 | if (!systemCfg.TryGetValue (name, out node)) { 63 | Debug.Log ($"[Kethane.ParticleManager] unknown system: {name}"); 64 | return null; 65 | } 66 | 67 | var go = new GameObject ($"KethaneParticleSystem:{name}"); 68 | Debug.Log ($"[Kethane.ParticleManager] creating {go.name}"); 69 | var psys = new ParticleSystemCfg (go); 70 | psys.Load (node.GetNode ("Particles")); 71 | sys = new System(); 72 | sys.psystem = psys.psystem; 73 | sys.physics = go.AddComponent (); 74 | sys.physics.psystem = psys.psystem; 75 | sys.physics.Load (node.GetNode ("Physics")); 76 | sys.psystem.Play (); 77 | return sys; 78 | } 79 | 80 | static void LoadSystemConfigs () 81 | { 82 | var dbase = GameDatabase.Instance; 83 | var node_list = dbase.GetConfigNodes ("KethaneParticleSystem"); 84 | 85 | systemCfg = new Dictionary (); 86 | for (int i = 0; i < node_list.Length; i++) { 87 | var node = node_list[i]; 88 | string name = node.GetValue ("name"); 89 | if (String.IsNullOrEmpty (name)) { 90 | Debug.Log ($"[Kethane.ParticleManager] skipping unnamed system"); 91 | continue; 92 | } 93 | if (systemCfg.ContainsKey (name)) { 94 | Debug.Log ($"[Kethane.ParticleManager] duplicate system name: {name}"); 95 | continue; 96 | } 97 | systemCfg[name] = node; 98 | } 99 | } 100 | 101 | void onFloatingOriginShift (Vector3d refPos, Vector3d nonFrame) 102 | { 103 | } 104 | 105 | void Awake () 106 | { 107 | instance = this; 108 | systems = new Dictionary (); 109 | if (systemCfg == null) { 110 | LoadSystemConfigs (); 111 | } 112 | GameEvents.onFloatingOriginShift.Add (onFloatingOriginShift); 113 | } 114 | 115 | void OnDestroy () 116 | { 117 | Debug.Log ($"[ParticleManager] OnDestroy"); 118 | GameEvents.onFloatingOriginShift.Remove (onFloatingOriginShift); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Plugin/Particles/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace KethaneParticles 5 | { 6 | 7 | public static class Utils 8 | { 9 | public static TEnum ToEnum (this string strEnumValue, TEnum defaultValue) 10 | { 11 | if (!Enum.IsDefined (typeof (TEnum), strEnumValue)) { 12 | return defaultValue; 13 | } 14 | 15 | return (TEnum) Enum.Parse (typeof (TEnum), strEnumValue); 16 | } 17 | 18 | static readonly char []comma = {','}; 19 | 20 | public static string []ParseArray (string text) 21 | { 22 | string []array = text.Split(comma, StringSplitOptions.RemoveEmptyEntries); 23 | for (int i = array.Length; i-- > 0; ) { 24 | array[i] = array[i].Trim(); 25 | } 26 | return array; 27 | } 28 | 29 | public static float []ParseFloatArray (string text) 30 | { 31 | string []elements = ParseArray(text); 32 | float []values = new float[elements.Length]; 33 | for (int i = elements.Length; i-- > 0; ) { 34 | if (!float.TryParse (elements[i], out values[i])) { 35 | return null; 36 | } 37 | } 38 | return values; 39 | } 40 | 41 | public static bool ParseBool (ConfigNode node, string name, bool defaultValue) 42 | { 43 | if (node.HasValue (name)) { 44 | bool val; 45 | bool.TryParse (node.GetValue (name), out val); 46 | return val; 47 | } 48 | return defaultValue; 49 | } 50 | 51 | public static float ParseFloat (ConfigNode node, string name, float defaultValue) 52 | { 53 | if (node.HasValue (name)) { 54 | float val; 55 | float.TryParse (node.GetValue (name), out val); 56 | return val; 57 | } 58 | return defaultValue; 59 | } 60 | 61 | public static int ParseInt (ConfigNode node, string name, int defaultValue) 62 | { 63 | if (node.HasValue (name)) { 64 | int val; 65 | int.TryParse (node.GetValue (name), out val); 66 | return val; 67 | } 68 | return defaultValue; 69 | } 70 | 71 | public static LayerMask ParseLayerMask (ConfigNode node, string name, LayerMask defaultValue) 72 | { 73 | if (node.HasValue (name)) { 74 | int val; 75 | if (!int.TryParse (node.GetValue (name), out val)) { 76 | return LayerMask.NameToLayer (node.GetValue (name)); 77 | } 78 | return val; 79 | } 80 | return defaultValue; 81 | } 82 | 83 | public static Vector2 ParseVector2 (ConfigNode node, string name, Vector2 defaultValue) 84 | { 85 | if (node.HasValue (name)) { 86 | var values = ParseFloatArray (node.GetValue (name)); 87 | return new Vector2 (values[0], values[1]); 88 | } 89 | return defaultValue; 90 | } 91 | 92 | public static Vector3 ParseVector3 (ConfigNode node, string name, Vector2 defaultValue) 93 | { 94 | if (node.HasValue (name)) { 95 | var values = ParseFloatArray (node.GetValue (name)); 96 | return new Vector3 (values[0], values[1], values[2]); 97 | } 98 | return defaultValue; 99 | } 100 | 101 | public static void CalcAxes (Vector3 norm, out Vector3 X, out Vector3 Y) 102 | { 103 | Vector3 axis = Vector3.right; 104 | float n = Mathf.Abs (Vector3.Dot (norm, axis)); 105 | if (Mathf.Abs (Vector3.Dot (norm, Vector3.forward)) < n) { 106 | axis = Vector3.forward; 107 | n = Mathf.Abs (Vector3.Dot (norm, axis)); 108 | } 109 | if (Mathf.Abs (Vector3.Dot (norm, Vector3.up)) < n) { 110 | axis = Vector3.up; 111 | n = Mathf.Abs (Vector3.Dot (norm, axis)); 112 | } 113 | Y = Vector3.Cross (norm, axis); 114 | X = Vector3.Cross (Y, norm); 115 | } 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /Parts/kethane_radialDrill/part.cfg: -------------------------------------------------------------------------------- 1 | // Kerbal Space Program - Part Config 2 | // MMI.K Miner 01 3 | 4 | PART 5 | { 6 | // --- general parameters --- 7 | name = kethane_radialDrill 8 | module = Part 9 | author = Dani-Sang | Kulesz 10 | 11 | // --- asset parameters --- 12 | mesh = model.mu 13 | scale = 0.1 14 | 15 | 16 | // --- node definitions --- 17 | node_attach = -1.0, 0.0, 0.0, 1.0, 0.0, 0.0 18 | 19 | 20 | // --- editor parameters --- 21 | cost = 2350 22 | category = -1 23 | TechHidden = True 24 | subcategory = 0 25 | title = KE-X130 External Drilling Unit 26 | manufacturer = Mechanical Mouse Industries 27 | description = This externally mounted housing contains a drill for extracting Kethane from the surface of a planet or moon. 28 | 29 | // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision 30 | attachRules = 0,1,0,1,0 31 | bulkheadProfiles = srf 32 | 33 | // --- standard part parameters --- 34 | mass = 1.0 35 | dragModelType = default 36 | maximum_drag = 0.001 37 | minimum_drag = 0.001 38 | angularDrag = 2 39 | crashTolerance = 6 40 | breakingForce = 50 41 | breakingTorque = 50 42 | maxTemp = 2000 43 | fuelCrossFeed = False 44 | 45 | MODULE 46 | { 47 | name = KethaneExtractor 48 | PowerConsumption = 8 49 | Resource 50 | { 51 | Name = Kethane 52 | Rate = 1.25 53 | } 54 | HeadTransform = 3 Cyl 55 | TailTransform = 1 Cyl 56 | HeadOffset = 0.5 57 | TailOffset = 0.5 58 | } 59 | 60 | MODULE 61 | { 62 | name = KethaneDrillAnimatorLegacy 63 | } 64 | 65 | MODULE 66 | { 67 | name = KethaneParticleEmitter 68 | Label = gas 69 | 70 | ColorAnimation1 = 0.02, 0.025, 0.02 71 | ColorAnimation2 = 0.02, 0.025, 0.02 72 | ColorAnimation3 = 0.015, 0.018, 0.015 73 | ColorAnimation4 = 0.015, 0.018, 0.015 74 | ColorAnimation5 = 0, 0, 0 75 | 76 | ShaderName = Particles/Additive 77 | TextureName = Kethane/smoke 78 | 79 | Emit = False 80 | MaxParticleSize = 10 81 | UseWorldSpace = True 82 | 83 | EmitterScale = 0.5, 0.5, 0.5 84 | LocalVelocity = 0, 4, 0 85 | RandomVelocity = 3, 3, 3 86 | 87 | MaxEmission = 20 88 | MinEmission = 0 89 | MinEnergy = 1 90 | MaxSize = 2 91 | MinSize = 1 92 | 93 | AngularVelocity = 5 94 | RandomAngularVelocity = 15 95 | RandomRotation = True 96 | } 97 | 98 | MODULE 99 | { 100 | name = KethaneParticleDynamics 101 | Emitter = gas 102 | DampingPressureExponent = -2.3 103 | GravityConstant = 1 104 | GravityPressure = -1.4 105 | MaxEnergyConstant = 5 106 | MaxEnergyPressure = 5 107 | RandomForcePressure = 8, 8, 8 108 | SizeGrowConstant = 0.25 109 | SizeGrowPressureExponent = -2.7 110 | } 111 | 112 | MODULE 113 | { 114 | name = KethaneParticleEmitter 115 | Label = sparks 116 | 117 | ColorAnimation1 = 0.8, 0.8, 0.8 118 | ColorAnimation2 = 0.8, 0.8, 0.8 119 | ColorAnimation3 = 0.8, 0.8, 0.8 120 | ColorAnimation4 = 0.6517648, 0.3835294, 0.2023529 121 | ColorAnimation5 = 0, 0, 0 122 | 123 | ShaderName = Particles/Additive 124 | TextureName = Kethane/mote 125 | 126 | Emit = False 127 | MaxParticleSize = 1 128 | UseWorldSpace = True 129 | 130 | EmitterScale = 0.325, 0, 0.325 131 | LocalVelocity = 0, 5, 0 132 | RandomVelocity = 1.5, 3.5, 1.5 133 | 134 | MaxEmission = 1000 135 | MinEmission = 500 136 | MinEnergy = 0.1 137 | MaxSize = 0.02 138 | MinSize = 0.01 139 | 140 | RenderMode = Stretch 141 | VelocityScale = 0.08 142 | } 143 | 144 | MODULE 145 | { 146 | name = KethaneParticleDynamics 147 | Emitter = sparks 148 | GravityConstant = 1 149 | MaxEnergyConstant = 0.2 150 | SizeGrowConstant = -1 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/KethaneDrillAnimator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using UnityEngine; 4 | 5 | namespace Kethane.PartModules 6 | { 7 | public class KethaneDrillAnimator : PartModule, IExtractorAnimator 8 | { 9 | [KSPField(isPersistant = false)] 10 | public string DeployAnimation; 11 | 12 | [KSPField(isPersistant = false)] 13 | public string DrillAnimation; 14 | 15 | [KSPField(isPersistant = true)] 16 | public string State; 17 | 18 | private AnimationState[] deployStates; 19 | private AnimationState[] drillStates; 20 | 21 | public override void OnStart(PartModule.StartState state) 22 | { 23 | deployStates = this.part.SetUpAnimation(DeployAnimation); 24 | drillStates = this.part.SetUpAnimation(DrillAnimation); 25 | 26 | if (CurrentState == ExtractorState.Deploying) { CurrentState = ExtractorState.Retracted; } 27 | else if (CurrentState == ExtractorState.Retracting) { CurrentState = ExtractorState.Deployed; } 28 | 29 | if (CurrentState == ExtractorState.Deployed) 30 | { 31 | foreach (var deployState in deployStates) 32 | { 33 | deployState.normalizedTime = 1; 34 | } 35 | } 36 | 37 | foreach (var drillState in drillStates) 38 | { 39 | drillState.enabled = false; 40 | drillState.wrapMode = WrapMode.Loop; 41 | } 42 | } 43 | 44 | public ExtractorState CurrentState 45 | { 46 | get 47 | { 48 | try 49 | { 50 | return (ExtractorState)Enum.Parse(typeof(ExtractorState), State); 51 | } 52 | catch 53 | { 54 | CurrentState = ExtractorState.Retracted; 55 | return CurrentState; 56 | } 57 | } 58 | private set 59 | { 60 | State = Enum.GetName(typeof(ExtractorState), value); 61 | } 62 | } 63 | 64 | public void Deploy() 65 | { 66 | if (CurrentState != ExtractorState.Retracted) { return; } 67 | CurrentState = ExtractorState.Deploying; 68 | } 69 | 70 | public void Retract() 71 | { 72 | if (CurrentState != ExtractorState.Deployed) { return; } 73 | CurrentState = ExtractorState.Retracting; 74 | foreach (var state in drillStates) 75 | { 76 | state.enabled = false; 77 | state.normalizedTime = 0; 78 | state.speed = 0; 79 | } 80 | } 81 | 82 | public void Update() 83 | { 84 | if (CurrentState == ExtractorState.Deploying && deployStates.All(s => s.normalizedTime >= 1)) 85 | { 86 | CurrentState = ExtractorState.Deployed; 87 | foreach (var state in drillStates) 88 | { 89 | state.enabled = true; 90 | state.normalizedTime = 0; 91 | state.speed = 1; 92 | } 93 | } 94 | else if (CurrentState == ExtractorState.Retracting && deployStates.All(s => s.normalizedTime <= 0)) 95 | { 96 | CurrentState = ExtractorState.Retracted; 97 | } 98 | 99 | foreach (var deployState in deployStates) 100 | { 101 | var time = Mathf.Clamp01(deployState.normalizedTime); 102 | deployState.normalizedTime = time; 103 | var speed = HighLogic.LoadedSceneIsEditor ? 1 - 10 * (time - 1) * time : 1; 104 | deployState.speed = (CurrentState == ExtractorState.Deploying || CurrentState == ExtractorState.Deployed) ? speed : -speed; 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Plugin/Kethane/Makefile: -------------------------------------------------------------------------------- 1 | KSPDIR := ${HOME}/ksp/KSP_linux 2 | MANAGED := ${KSPDIR}/KSP_Data/Managed 3 | GAMEDATA := ${KSPDIR}/GameData 4 | KGAMEDATA := ${GAMEDATA}/Kethane 5 | PLUGINDIR := ${KGAMEDATA}/Plugins 6 | 7 | TARGETS := Kethane.dll 8 | 9 | K_FILES := \ 10 | BodyResourceData.cs \ 11 | Generators/CellularResourceGenerator.cs \ 12 | Generators/LegacyResourceGenerator.cs \ 13 | Generators/RandomExtensions.cs \ 14 | IResourceGenerator.cs \ 15 | KethaneController.cs \ 16 | KethaneData.cs \ 17 | Misc.cs \ 18 | Particles/ParticleManager.cs \ 19 | Particles/ParticlePhysics.cs \ 20 | PartModules/HeatSinkAnimator.cs \ 21 | PartModules/IDetectorAnimator.cs \ 22 | PartModules/IExtractorAnimator.cs \ 23 | PartModules/KethaneConverter.cs \ 24 | PartModules/KethaneDetector.cs \ 25 | PartModules/KethaneDetectorAnimator.cs \ 26 | PartModules/KethaneDetectorAnimatorUnity.cs \ 27 | PartModules/KethaneDrillAnimator.cs \ 28 | PartModules/KethaneDrillAnimatorLegacy.cs \ 29 | PartModules/KethaneExtractor.cs \ 30 | PartModules/KethaneGenerator.cs \ 31 | PartModules/KethaneKerbalBlender.cs \ 32 | PartModules/KethaneParticleEmitter.cs \ 33 | PartModules/KethaneWetMassIndicator.cs \ 34 | PartModules/OrthogonalIntake.cs \ 35 | PartModules/PartExtensions.cs \ 36 | PartModules/TimedMovingAverage.cs \ 37 | Properties/AssemblyInfo.cs \ 38 | ResourceData.cs \ 39 | ResourceDefinition.cs \ 40 | Scenarios/KethaneScanningTutorial.cs \ 41 | SettingsManager.cs \ 42 | UserInterface/KerbNetModeKethane.cs \ 43 | UserInterface/MainMenuOverlay.cs \ 44 | UserInterface/MapOverlay.cs \ 45 | UserInterface/OverlayRenderer.cs \ 46 | UserInterface/TerrainData.cs \ 47 | Utilities/CompatibilityChecker.cs \ 48 | Utilities/InstallChecker.cs \ 49 | Utilities/InstallCleanup.cs \ 50 | Utilities/KopernicusWrapper.cs \ 51 | Utilities/LicenseSentinel.cs \ 52 | Utilities/ShaderLoader.cs \ 53 | Utilities/TutorialInstaller.cs \ 54 | VesselModules/IKethaneBattery.cs \ 55 | VesselModules/KethaneBattery.cs \ 56 | VesselModules/KethaneProtoBattery.cs \ 57 | VesselModules/KethaneProtoDetector.cs \ 58 | VesselModules/KethaneScanner.cs \ 59 | $e 60 | 61 | RESGEN2 := resgen2 62 | CSC := csc 63 | CSCFLAGS := -highentropyva- -noconfig -nostdlib+ -t:library -optimize -debug -warnaserror+ 64 | GIT := git 65 | TAR := tar 66 | ZIP := zip 67 | 68 | #all: version ${TARGETS} 69 | all: ${TARGETS} 70 | 71 | .PHONY: version 72 | version: 73 | @./tools/git-version.sh 74 | 75 | info: 76 | @echo "Kethane Build Information" 77 | @echo " resgen2: ${RESGEN2}" 78 | @echo " gmcs: ${CSC}" 79 | @echo " gmcs flags: ${CSCFLAGS}" 80 | @echo " git: ${GIT}" 81 | @echo " tar: ${TAR}" 82 | @echo " zip: ${ZIP}" 83 | @echo " KSP Data: ${KSPDIR}" 84 | 85 | SYSTEM := \ 86 | -lib:${MANAGED} \ 87 | -r:${MANAGED}/mscorlib.dll \ 88 | -r:${MANAGED}/System.dll \ 89 | -r:${MANAGED}/System.Core.dll 90 | 91 | KSP := \ 92 | -r:Assembly-CSharp.dll \ 93 | -r:Assembly-CSharp-firstpass.dll \ 94 | -r:KSPAssets.dll 95 | 96 | UNITY := \ 97 | -r:UnityEngine.dll \ 98 | -r:UnityEngine.UI.dll \ 99 | -r:UnityEngine.CoreModule.dll \ 100 | -r:UnityEngine.AnimationModule.dll \ 101 | -r:UnityEngine.PhysicsModule.dll \ 102 | -r:UnityEngine.IMGUIModule.dll \ 103 | -r:UnityEngine.AudioModule.dll \ 104 | -r:UnityEngine.ParticleSystemModule.dll \ 105 | -r:UnityEngine.TextRenderingModule.dll \ 106 | -r:UnityEngine.InputLegacyModule.dll \ 107 | -r:UnityEngine.UnityWebRequestWWWModule.dll \ 108 | -r:UnityEngine.UnityWebRequestModule.dll \ 109 | -r:UnityEngine.AssetBundleModule.dll \ 110 | $e 111 | 112 | Kethane.dll: ${K_FILES} Kethane-LICENSE.txt 113 | ${CSC} ${CSCFLAGS} ${SYSTEM} ${KSP} ${UNITY} \ 114 | -lib:../../../GeodesicGrid/bin -r:GeodesicGrid.dll \ 115 | -lib:../Particles -r:KethaneParticles.dll \ 116 | -resource:Kethane-LICENSE.txt,Kethane.Kethane-LICENSE.txt \ 117 | -out:$@ ${K_FILES} 118 | 119 | clean: 120 | rm -f ${TARGETS} 121 | 122 | install: all 123 | mkdir -p "${PLUGINDIR}" 124 | cp ${TARGETS} "${PLUGINDIR}" 125 | #cp ${TARGETS} bin/Kethane.version ${PLUGINDIR} 126 | 127 | .PHONY: all clean install 128 | -------------------------------------------------------------------------------- /Parts/kethane_heavyDrill/part.cfg: -------------------------------------------------------------------------------- 1 | //Kethane Pack Asset 2 | 3 | PART 4 | { 5 | 6 | // --- general parameters --- 7 | name = kethane_heavyDrill 8 | module = Part 9 | author = Keptin 10 | 11 | 12 | // --- asset parameters --- 13 | rescaleFactor = 0.01 14 | 15 | // --- node definitions --- 16 | node_stack_top = 0.0, 126, 0.0, 0.0, 1.0, 0.0, 1 17 | node_stack_bottom = 0.0, -155, 0.0, 0.0, -1.0, 0.0, 1 18 | node_attach = 60, 0.0, 0.0, 1.0, 0.0, 0.0 19 | 20 | 21 | // --- editor parameters --- 22 | cost = 6450 23 | category = Utility 24 | subcategory = 0 25 | title = KE-X270 Heavy Drilling Unit 26 | manufacturer = Organization of Kethane Equipment Producers 27 | description = This hydraulic drilling pod solves the long-standing problem of having too much electrical power available on a spacecraft. While it does extract more Kethane per unit of energy expended, "energy efficient" isn't what this behemoth drill brings to mind. 28 | 29 | TechRequired = advMetalworks 30 | entryCost = 50 31 | 32 | // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision 33 | attachRules = 1,1,1,1,0 34 | bulkheadProfiles = size1, srf 35 | 36 | mass = 4 37 | dragModelType = default 38 | maximum_drag = 0.2 39 | minimum_drag = 0.3 40 | angularDrag = 2 41 | crashTolerance = 6 42 | breakingForce = 50 43 | breakingTorque = 50 44 | maxTemp = 2000 45 | fuelCrossFeed = True 46 | 47 | MODEL 48 | { 49 | model = Kethane/Parts/kethane_heavyDrill/model 50 | position = 0, -14.15, 0 51 | scale = 0.625, 0.625, 0.625 52 | rotation = 0, 0, 0 53 | } 54 | 55 | MODULE 56 | { 57 | name = KethaneExtractor 58 | PowerConsumption = 24 59 | Resource 60 | { 61 | Name = Kethane 62 | Rate = 5 63 | } 64 | HeadTransform = heavyDrillHead 65 | TailTransform = heavyDrillShaft4 66 | } 67 | 68 | MODULE 69 | { 70 | name = KethaneDrillAnimator 71 | DeployAnimation = idle 72 | DrillAnimation = idle0 73 | } 74 | 75 | MODULE 76 | { 77 | name = KethaneParticleEmitter 78 | Label = gas 79 | System = cloud 80 | 81 | Spread = 0.2, 0.2 82 | 83 | ColorAnimation1 = 0.02, 0.025, 0.02 84 | ColorAnimation2 = 0.02, 0.025, 0.02 85 | ColorAnimation3 = 0.015, 0.018, 0.015 86 | ColorAnimation4 = 0.015, 0.018, 0.015 87 | ColorAnimation5 = 0, 0, 0 88 | 89 | ShaderName = Particles/Additive 90 | TextureName = Kethane/smoke 91 | 92 | Emit = False 93 | MaxParticleSize = 10 94 | UseWorldSpace = True 95 | 96 | EmitterScale = 0.5, 0.5, 0.5 97 | LocalVelocity = 0, 6, 0 98 | RandomVelocity = 5, 5, 5 99 | 100 | MaxEmission = 90 101 | MinEmission = 0 102 | MinEnergy = 1 103 | MaxSize = 2.5 104 | MinSize = 1.5 105 | 106 | AngularVelocity = 5 107 | RandomAngularVelocity = 15 108 | RandomRotation = True 109 | } 110 | 111 | MODULE 112 | { 113 | name = KethaneParticleDynamics 114 | Emitter = gas 115 | DampingPressureExponent = -2.3 116 | GravityConstant = 1 117 | GravityPressure = -1.4 118 | MaxEnergyConstant = 5 119 | MaxEnergyPressure = 5 120 | RandomForcePressure = 8, 8, 8 121 | SizeGrowConstant = 0.25 122 | SizeGrowPressureExponent = -2.7 123 | } 124 | 125 | MODULE 126 | { 127 | name = KethaneParticleEmitter 128 | Label = sparks 129 | System = sparks 130 | 131 | Spread = 0.2, 0.2 132 | 133 | ColorAnimation1 = 0.8, 0.8, 0.8 134 | ColorAnimation2 = 0.8, 0.8, 0.8 135 | ColorAnimation3 = 0.8, 0.8, 0.8 136 | ColorAnimation4 = 0.6517648, 0.3835294, 0.2023529 137 | ColorAnimation5 = 0, 0, 0 138 | 139 | ShaderName = Particles/Additive 140 | TextureName = Kethane/mote 141 | 142 | Emit = False 143 | MaxParticleSize = 1 144 | UseWorldSpace = True 145 | 146 | EmitterScale = 0.325, 0, 0.325 147 | LocalVelocity = 0, 7, 0 148 | RandomVelocity = 2, 3.5, 2 149 | 150 | MaxEmission = 10000 151 | MinEmission = 5000 152 | MinEnergy = 0.1 153 | MaxSize = 0.02 154 | MinSize = 0.01 155 | 156 | RenderMode = Stretch 157 | VelocityScale = 0.08 158 | } 159 | 160 | MODULE 161 | { 162 | name = KethaneParticleDynamics 163 | Emitter = sparks 164 | GravityConstant = 1 165 | MaxEnergyConstant = 0.2 166 | SizeGrowConstant = -1 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /Plugin/Kethane/ResourceData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEngine; 5 | 6 | namespace Kethane 7 | { 8 | public class ResourceData 9 | { 10 | private IResourceGenerator generator; 11 | private ConfigNode generatorNode; 12 | 13 | private Dictionary bodies = new Dictionary(); 14 | 15 | protected ResourceData(IResourceGenerator generator, ConfigNode generatorNode, IDictionary bodies) 16 | { 17 | this.generator = generator; 18 | this.generatorNode = generatorNode; 19 | this.bodies = new Dictionary(bodies); 20 | } 21 | 22 | public BodyResourceData this[CelestialBody body] 23 | { 24 | get { return bodies[body]; } 25 | } 26 | 27 | public void ResetBodyData(CelestialBody body) 28 | { 29 | bodies[body] = BodyResourceData.Load(generator, body, null); 30 | } 31 | 32 | public static ResourceData Load(ResourceDefinition resource, ConfigNode resourceNode) 33 | { 34 | var bodyResources = new Dictionary(); 35 | 36 | var generatorNode = resourceNode.GetNode("Generator") ?? resource.Generator; 37 | var generator = createGenerator(generatorNode.CreateCopy()); 38 | if (generator == null) 39 | { 40 | Debug.LogWarning("[Kethane] Defaulting to empty generator for " + resource.Resource); 41 | generator = new EmptyResourceGenerator(); 42 | } 43 | 44 | var bodyNodes = resourceNode.GetNodes("Body"); 45 | 46 | foreach (var body in FlightGlobals.Bodies) 47 | { 48 | var bodyNode = bodyNodes.SingleOrDefault(n => n.GetValue("Name") == body.name); 49 | bodyResources[body] = BodyResourceData.Load(generator, body, bodyNode); 50 | } 51 | 52 | return new ResourceData(generator, generatorNode, bodyResources); 53 | } 54 | 55 | public void Save(ConfigNode resourceNode) 56 | { 57 | resourceNode.AddNode(generatorNode); 58 | 59 | foreach (var body in bodies) 60 | { 61 | var bodyNode = new ConfigNode("Body"); 62 | bodyNode.AddValue("Name", body.Key.name); 63 | body.Value.Save(bodyNode); 64 | resourceNode.AddNode(bodyNode); 65 | } 66 | } 67 | 68 | private static IResourceGenerator createGenerator(ConfigNode generatorNode) 69 | { 70 | var name = generatorNode.GetValue("name"); 71 | if (name == null) { Debug.LogError("[Kethane] Could not find generator name"); return null; } 72 | 73 | System.Reflection.ConstructorInfo constructor = null; 74 | foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) 75 | { 76 | try 77 | { 78 | constructor = assembly.GetTypes() 79 | .Where(t => t.Name == name) 80 | .Where(t => t.GetInterfaces().Contains(typeof(IResourceGenerator))) 81 | .Select(t => t.GetConstructor(new Type[] { typeof(ConfigNode) })) 82 | .FirstOrDefault(c => c != null); 83 | 84 | if (constructor != null) { break; } 85 | } 86 | catch (Exception e) 87 | { 88 | Debug.LogWarning("[Kethane] Error inspecting assembly '" + assembly.GetName().Name + "': \n" + e); 89 | } 90 | } 91 | 92 | if (constructor == null) { Debug.LogError("[Kethane] Could not find appropriate constructor for " + name); return null; } 93 | 94 | try 95 | { 96 | return (IResourceGenerator)constructor.Invoke(new object[] { generatorNode }); 97 | } 98 | catch (Exception e) 99 | { 100 | Debug.LogError("[Kethane] Could not instantiate " + name + ":\n" + e); 101 | return null; 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Kethane 2 | ======= 3 | 4 | Resource mining and processing plugin for [Kerbal Space Program](http://www.kerbalspaceprogram.com/). 5 | 6 | Forum thread: [Kethane Pack](http://forum.kerbalspaceprogram.com/showthread.php/23979-Kethane-Pack) 7 | Maintainer: [Majiir](http://forum.kerbalspaceprogram.com/member.php/7556-Majiir) 8 | 9 | Building 10 | -------- 11 | 12 | To build Kethane you can use the [Kethane Utilities](https://github.com/LaylConway/KethaneUtilities) scripts to automatically build the plugin and assemble a mod folder. 13 | 14 | If you don't want to use the automatic build scripts, you can build manually using the following steps: 15 | 16 | 1. Build the plugin DLL. Make sure to reference the Assembly-CSharp and UnityEngine assemblies from the version of KSP you wish to target. (A plugin build targeted to one version may not work on another, even if no code changes are necessary for compatibility.) 17 | 2. Copy the part .cfg files from the repository Parts/ directory. 18 | 3. Copy any other assets from the latest public release. In particular, .wav, .mu and .mbm files are currently excluded from the repository. 19 | 20 | Reporting Issues 21 | ---------------- 22 | 23 | Please provide as much detail as possible when reporting an issue. That said, if you encounter an issue and aren't able to pin down the cause, post it and explain what you've tried so far. Some bugs are difficult to reproduce, and we need to know about them anyway. 24 | 25 | Feature suggestions are also welcome. However, don't be surprised if these issues are closed. The project will be expanding, but some features are out of the intended scope and won't be included. 26 | 27 | Coding and Pull Request Guidelines 28 | ---------------------------------- 29 | 30 | - The project follows ["A successful Git branching model"](http://nvie.com/posts/a-successful-git-branching-model/) with some minor modifications. Namely, before a release branch may be merged into `master`, any changes on `master` must have been merged into the release branch (or other branches upstream). 31 | - Pull requests should be tested before submission. 32 | - Keep your commits clean. Before committing, check your changes and make sure you're only committing the absolute minimum changes necessary. In particular, avoid committing changes to the .csproj file unless you're absolutely sure that those changes should be included in the repository. 33 | - Don't commit binary files, including DLLs and models. 34 | - No merges should be included in pull requests unless the pull request's purpose is a merge. 35 | - No tabs, please. Use four spaces instead. 36 | - No trailing whitespaces. 37 | - No CRLF line endings, LF only, put your gits 'core.autocrlf' on 'true'. 38 | - No 80 column limit or 'weird' midstatement newlines. 39 | 40 | Keep in mind that some pull requests will be rejected at first, but with changes, may be accepted again. Don't be offended if your pull is rejected; it's just an effort to maintain a consistent code base and feature growth. 41 | 42 | Model and Part Asset Submission Guidelines 43 | ------------------------------------------ 44 | 45 | Part assets should be submitted by private message to [Majiir](http://forum.kerbalspaceprogram.com/member.php/7556-Majiir) for review. 46 | 47 | The following contents should be packaged in a .zip or .rar archive: 48 | 49 | - Model to be submitted as a .obj or .FBX, must include low-poly convex node_collider mesh 50 | - Meshes of appropriate resolution/polycount (<1000 tris small part, <2000 tris large part recommended) 51 | - Objects named properly (there's no strict convention, but make sure they're clearly named; e.g. fuelTank_geo, geo_engine, enginePoly, etc.) 52 | - All transformations must be cleared/frozen 53 | - No n-gons (five or greater sided polygons) 54 | - UVs mapped to 0-1 space, not overlapping (you can overlap UV shells, just not UVs of the same shell) 55 | - Texture map and Normal map (optional) to be submitted as a .png or .jpeg 56 | - Appropriate resolution for part size, not to exceed 1024 57 | - AO baked textures recommended 58 | - Notes in .txt file (optional) 59 | 60 | Part submissions may be returned for any of the following reasons, in approximately this order: 61 | 62 | - Part doesn't fall within the scope of the plugin 63 | - Assets submission doesn't meet above guidelines 64 | - Part concept doesn't fit the art style of the plugin 65 | - Assets need further work to improve quality 66 | 67 | Our goal is to help you submit your parts, but at the same time, contributions can't add significantly to our workload. These guidelines are here to ensure a high standard of quality and to make the process as painless as possible for everyone involved. -------------------------------------------------------------------------------- /Plugin/Kethane/UserInterface/MainMenuOverlay.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using GeodesicGrid; 3 | using System.Linq; 4 | using UnityEngine; 5 | 6 | namespace Kethane.UserInterface 7 | { 8 | [KSPAddon(KSPAddon.Startup.Instantly, true)] 9 | internal class MainMenuUpdateCatcher : MonoBehaviour 10 | { 11 | public static GameObject MenuBody = null; 12 | public static bool updated = false; 13 | 14 | protected void Start() 15 | { 16 | if (Utilities.Kopernicus.KopernicusWrapper.Initialize()) { 17 | Utilities.Kopernicus.Events.OnRuntimeUtilityUpdateMenu.Add (UpdateMenu); 18 | } 19 | GameObject.DontDestroyOnLoad(this); 20 | } 21 | 22 | void OnDestroy () 23 | { 24 | if (Utilities.Kopernicus.KopernicusWrapper.Initialize()) { 25 | Utilities.Kopernicus.Events.OnRuntimeUtilityUpdateMenu.Remove (UpdateMenu); 26 | } 27 | } 28 | 29 | void UpdateMenu () 30 | { 31 | // if we're in here, Kopernicus is known to have set up a main menu body and thus various safety checks have already been done 32 | MainMenu main = FindObjectOfType(); 33 | MainMenuEnvLogic logic = main.envLogic; 34 | GameObject space = logic.areas[1]; 35 | string menuBody = Utilities.Kopernicus.Templates.MenuBody; 36 | foreach (Transform t in space.transform) { 37 | if (t.gameObject.activeInHierarchy && t.name == menuBody) { 38 | Debug.Log ($"[Kethane] UpdateMenu: {t.name}"); 39 | MenuBody = t.gameObject; 40 | } 41 | } 42 | updated = true; 43 | } 44 | } 45 | 46 | [KSPAddon(KSPAddon.Startup.MainMenu, false)] 47 | internal class MainMenuOverlay : MonoBehaviour 48 | { 49 | private GameObject FindKerbin () 50 | { 51 | var objects = GameObject.FindObjectsOfType(typeof(GameObject)); 52 | if (objects.Any(o => o.name == "LoadingBuffer")) { return null; } 53 | var kerbin = objects.OfType().Where(b => b.name == "Kerbin").LastOrDefault(); 54 | 55 | if (kerbin == null) 56 | { 57 | Debug.LogWarning("[Kethane] Couldn't find Kerbin!"); 58 | } 59 | return kerbin; 60 | } 61 | 62 | protected IEnumerator Start() 63 | { 64 | Debug.Log ($"[Kethane] MainMenuOverlay Start {MainMenuUpdateCatcher.MenuBody}"); 65 | if (Utilities.Kopernicus.KopernicusWrapper.Initialize()) { 66 | while (!MainMenuUpdateCatcher.updated) { 67 | yield return null; 68 | } 69 | if (MainMenuUpdateCatcher.MenuBody != null) { 70 | // Kopernicus already fixed things up 71 | Debug.Log ($"[Kethane] adding overlay to: {MainMenuUpdateCatcher.MenuBody.transform.name}"); 72 | var overlayRenderer = gameObject.AddComponent(); 73 | overlayRenderer.SetGridLevel(KethaneData.GridLevel); 74 | overlayRenderer.IsVisible = startMenuOverlay(overlayRenderer, MainMenuUpdateCatcher.MenuBody); 75 | } 76 | } else { 77 | var overlayRenderer = gameObject.AddComponent(); 78 | overlayRenderer.SetGridLevel(KethaneData.GridLevel); 79 | overlayRenderer.IsVisible = startMenuOverlay(overlayRenderer, FindKerbin ()); 80 | } 81 | } 82 | 83 | private bool startMenuOverlay(OverlayRenderer overlayRenderer, GameObject menuBody) 84 | { 85 | Debug.Log ($"[Kethane] startMenuOverlay {menuBody}"); 86 | if (!Misc.Parse(SettingsManager.GetValue("ShowInMenu"), true)) { return false; } 87 | if (menuBody == null) { 88 | return false; 89 | } 90 | 91 | 92 | overlayRenderer.SetTarget(menuBody.transform); 93 | overlayRenderer.SetRadiusMultiplier(1.02f); 94 | 95 | var random = new System.Random(); 96 | var colors = new CellMap(KethaneData.GridLevel); 97 | 98 | foreach (var cell in Cell.AtLevel(KethaneData.GridLevel)) 99 | { 100 | var rand = random.Next(100); 101 | Color32 color; 102 | if (rand < 16) 103 | { 104 | color = rand < 4 ? new Color32(21, 176, 26, 255) : new Color32(128, 128, 128, 192); 105 | foreach (var neighbor in cell.GetNeighbors(KethaneData.GridLevel)) 106 | { 107 | if (random.Next(2) < 1) 108 | { 109 | colors[neighbor] = color; 110 | } 111 | } 112 | } 113 | else 114 | { 115 | color = new Color32(0, 0, 0, 128); 116 | } 117 | 118 | colors[cell] = color; 119 | } 120 | 121 | overlayRenderer.SetCellColors(colors); 122 | 123 | return true; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Plugin/Kethane/Misc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | 7 | namespace Kethane 8 | { 9 | internal static class Misc 10 | { 11 | #region Parsing utility methods 12 | 13 | public static float Parse(string s, float defaultValue) 14 | { 15 | float value; 16 | if (!float.TryParse(s, out value)) 17 | { 18 | value = defaultValue; 19 | } 20 | return value; 21 | } 22 | 23 | public static double Parse(string s, double defaultValue) 24 | { 25 | double value; 26 | if (!double.TryParse(s, out value)) 27 | { 28 | value = defaultValue; 29 | } 30 | return value; 31 | } 32 | 33 | public static int Parse(string s, int defaultValue) 34 | { 35 | int value; 36 | if (!int.TryParse(s, out value)) 37 | { 38 | value = defaultValue; 39 | } 40 | return value; 41 | } 42 | 43 | public static bool Parse(string s, bool defaultValue) 44 | { 45 | bool value; 46 | if (!bool.TryParse(s, out value)) 47 | { 48 | value = defaultValue; 49 | } 50 | return value; 51 | } 52 | 53 | public static Vector3 Parse(string s, Vector3 defaultValue) 54 | { 55 | try 56 | { 57 | return ConfigNode.ParseVector3(s); 58 | } 59 | catch 60 | { 61 | return defaultValue; 62 | } 63 | } 64 | 65 | public static Color32 Parse(string s, Color32 defaultValue) 66 | { 67 | if (s == null) { return defaultValue; } 68 | return ConfigNode.ParseColor32(s); 69 | } 70 | 71 | static MethodInfo PreFormatConfig = typeof(ConfigNode).GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(m => m.Name == "PreFormatConfig" && m.GetParameters().Length == 1).FirstOrDefault(); 72 | static MethodInfo RecurseFormat = typeof(ConfigNode).GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(m => m.Name == "RecurseFormat" && m.GetParameters().Length == 1).FirstOrDefault(); 73 | public static ConfigNode Parse(string s) 74 | { 75 | var lines = s.Split(new char[]{'\n', '\r'}); 76 | object obj = PreFormatConfig.Invoke(null, new object[] {lines}); 77 | return (ConfigNode) RecurseFormat.Invoke(null, new object[] {obj}); 78 | } 79 | 80 | public static ParticleSystemRenderMode Parse(string s, ParticleSystemRenderMode defaultValue) 81 | { 82 | try 83 | { 84 | return (ParticleSystemRenderMode)Enum.Parse(typeof(ParticleSystemRenderMode), s); 85 | } 86 | catch 87 | { 88 | return defaultValue; 89 | } 90 | } 91 | 92 | #endregion 93 | 94 | #region Encoding 95 | 96 | public static byte[] FromBase64String(string encoded) 97 | { 98 | return Convert.FromBase64String(encoded.Replace('.', '/').Replace('%', '=')); 99 | } 100 | 101 | public static string ToBase64String(byte[] data) 102 | { 103 | return Convert.ToBase64String(data).Replace('/', '.').Replace('=', '%'); 104 | } 105 | 106 | #endregion 107 | 108 | // Get true altitude above terrain (from MuMech lib) 109 | // Also from: http://kerbalspaceprogram.com/forum/index.php?topic=10324.msg161923#msg161923 110 | public static double getTrueAltitude(this Vessel vessel) 111 | { 112 | Vector3 CoM = vessel.CoM; 113 | Vector3 up = (CoM - vessel.mainBody.position).normalized; 114 | double altitudeASL = vessel.mainBody.GetAltitude(CoM); 115 | double altitudeTrue = 0.0; 116 | RaycastHit sfc; 117 | if (Physics.Raycast(CoM, -up, out sfc, (float)altitudeASL + 10000.0F, 1 << 15)) 118 | altitudeTrue = sfc.distance; 119 | else if (vessel.mainBody.pqsController != null) 120 | altitudeTrue = vessel.mainBody.GetAltitude(CoM) - (vessel.mainBody.pqsController.GetSurfaceHeight(QuaternionD.AngleAxis(vessel.mainBody.GetLongitude(CoM), Vector3d.down) * QuaternionD.AngleAxis(vessel.mainBody.GetLatitude(CoM), Vector3d.forward) * Vector3d.right) - vessel.mainBody.pqsController.radius); 121 | else 122 | altitudeTrue = vessel.mainBody.GetAltitude(CoM); 123 | return altitudeTrue; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Particles.cfg: -------------------------------------------------------------------------------- 1 | KethaneParticleSystem { 2 | name = cloud 3 | Particles { 4 | useAutoRandomSeed = true 5 | 6 | main { 7 | cullingMode = AlwaysSimulate 8 | duration = 10000 9 | emitterVelocityMode = Transform 10 | flipRotation = 0.5 11 | gravityModifier = 0 12 | gravityModifierMultiplier = 0 13 | loop = true 14 | maxParticles = 5000 15 | playOnAwake = false 16 | prewarm = false 17 | ringBufferLoopRange = 0, 1 18 | ringBufferMode = Disabled 19 | scalingMode = Local 20 | simulationSpace = World 21 | simulationSpeed = 1 22 | startColor { 23 | color = 1, 1, 1, 0.1372549 24 | } 25 | startLifetime { 26 | constant = 50 27 | } 28 | startRotation { 29 | constant = -3.1415926, 3.1415926 30 | } 31 | startRotation3D = false 32 | startSize { 33 | constant = 0.3, 3 34 | } 35 | startSize3D = false 36 | startSizeMultipler = 1 37 | startSpeed { 38 | constant = 4, 7 39 | } 40 | startSpeedMultipler = 1 41 | stopAction = None 42 | useUnscaledTime = false 43 | } 44 | emission { 45 | rateOverDistance { 46 | constant = 0 47 | } 48 | rateOverTime { 49 | constant = 0 50 | } 51 | } 52 | Xshape { 53 | shapeType = Mesh 54 | mesh = emitter 55 | meshShapeType = Triangle 56 | angle = 15 57 | arc = 360 58 | arcMode = Random 59 | arcSpread = 0 60 | radius = 0.1 61 | radiusMode = Random 62 | radiusThickness = 1 63 | position = 0, 0, 0 64 | rotation = 0, 0, 0 65 | scale = 1, 1, 1 66 | randomDirectionAmount = 0.1 67 | randomPositionAmount = 1 68 | } 69 | colorOverLifetime { 70 | color { 71 | gradient { 72 | key = 0, 0, 0.9811321, 0.003455702, 0.4196078431372549 73 | key = 1, 0, 0.9811321, 0.003455702, 0.0 74 | } 75 | } 76 | } 77 | rotationOverLifetime { 78 | separateAxes = false 79 | z { 80 | constant = -2.356194490192345, 2.356194490192345 81 | } 82 | } 83 | sizeOverLifetime { 84 | separateAxes = false 85 | sizeMultiplier = 5 86 | size { 87 | curve { 88 | key = 0, 0.2, 0, 0 89 | key = 1, 1, 2, 2 90 | } 91 | } 92 | } 93 | renderer { 94 | material { 95 | shader = Kethane/Unlit/AdditiveFade 96 | texture = Kethane/smoke 97 | } 98 | vertexStreams = Position, Normal, Color, UV2, AgePercent 99 | } 100 | } 101 | Physics { 102 | gravity = 0, -9.8, 0 103 | dispersion = 500 104 | drag = 0.1 105 | density = 1.2 106 | densityExp = -0.0001 107 | particleDensity = 1.213 108 | } 109 | } 110 | 111 | KethaneParticleSystem { 112 | name = sparks 113 | Particles { 114 | useAutoRandomSeed = true 115 | 116 | main { 117 | cullingMode = AlwaysSimulate 118 | duration = 10000 119 | emitterVelocityMode = Transform 120 | flipRotation = 0.5 121 | gravityModifier = 0 122 | gravityModifierMultiplier = 0 123 | loop = true 124 | maxParticles = 500 125 | playOnAwake = false 126 | prewarm = false 127 | ringBufferLoopRange = 0, 1 128 | ringBufferMode = Disabled 129 | scalingMode = Local 130 | simulationSpace = World 131 | simulationSpeed = 1 132 | startColor { 133 | color = 1, 1, 1, 1 134 | } 135 | startLifetime { 136 | constant = 0.5, 2 137 | } 138 | startRotation { 139 | constant = -3.1415926, 3.1415926 140 | } 141 | startRotation3D = false 142 | startSize { 143 | constant = 0.075 144 | } 145 | startSize3D = false 146 | startSizeMultipler = 1 147 | startSpeed { 148 | constant = 1, 4 149 | } 150 | startSpeedMultipler = 1 151 | stopAction = None 152 | useUnscaledTime = false 153 | } 154 | emission { 155 | rateOverDistance { 156 | constant = 0 157 | } 158 | rateOverTime { 159 | constant = 0 160 | } 161 | } 162 | Xshape { 163 | shapeType = Mesh 164 | mesh = emitter 165 | meshShapeType = Triangle 166 | angle = 15 167 | arc = 360 168 | arcMode = Random 169 | arcSpread = 0 170 | radius = 0.1 171 | radiusMode = Random 172 | radiusThickness = 1 173 | position = 0, 0, 0 174 | rotation = 0, 0, 0 175 | scale = 1, 1, 1 176 | randomDirectionAmount = 0.1 177 | randomPositionAmount = 1 178 | } 179 | colorOverLifetime { 180 | color { 181 | gradient { 182 | key = 0.0, 0.8, 0.8, 0.8 183 | key = 0.25, 0.8, 0.8, 0.8 184 | key = 0.6, 0.8, 0.8, 0.8 185 | key = 0.75, 0.6517648, 0.3835294, 0.2023529 186 | key = 1.0, 0, 0, 0 187 | } 188 | } 189 | } 190 | rotationOverLifetime { 191 | separateAxes = false 192 | z { 193 | constant = -2.356194490192345, 2.356194490192345 194 | } 195 | } 196 | renderer { 197 | material { 198 | shader = Kethane/Unlit/AdditiveFade 199 | texture = Kethane/mote 200 | } 201 | vertexStreams = Position, Normal, Color, UV2, AgePercent 202 | velocityScale = 0.08 203 | } 204 | } 205 | Physics { 206 | gravity = 0, -9.8, 0 207 | dispersion = 0 208 | drag = 0.02 209 | density = 1 210 | densityExp = -0.0001 211 | particleDensity = 8 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/KethaneDetector.cs: -------------------------------------------------------------------------------- 1 | using Kethane.UserInterface; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | 7 | using Kethane.VesselModules; 8 | 9 | namespace Kethane.PartModules 10 | { 11 | public class KethaneDetector : PartModule 12 | { 13 | public static bool ScanningSound 14 | { 15 | get { return Misc.Parse(SettingsManager.GetValue("ScanningSound"), true); } 16 | set { SettingsManager.SetValue("ScanningSound", value); } 17 | } 18 | 19 | [KSPField(isPersistant = false)] 20 | public float DetectingPeriod; 21 | 22 | [KSPField(isPersistant = false)] 23 | public float DetectingHeight; 24 | 25 | [KSPField(isPersistant = false)] 26 | public float PowerConsumption; 27 | 28 | [KSPField(isPersistant = true)] 29 | public bool IsDetecting; 30 | 31 | public string configString; 32 | 33 | internal List resources; 34 | internal KethaneProtoDetector scanner; 35 | 36 | [KSPEvent(guiActive = true, guiName = "Activate Detector", active = true, externalToEVAOnly = true, guiActiveUnfocused = true, unfocusedRange = 1.5f)] 37 | public void EnableDetection() 38 | { 39 | IsDetecting = true; 40 | if (scanner != null) { 41 | scanner.IsDetecting = IsDetecting; 42 | } 43 | } 44 | 45 | [KSPEvent(guiActive = true, guiName = "Deactivate Detector", active = false, externalToEVAOnly = true, guiActiveUnfocused = true, unfocusedRange = 1.5f)] 46 | public void DisableDetection() 47 | { 48 | IsDetecting = false; 49 | if (scanner != null) { 50 | scanner.IsDetecting = IsDetecting; 51 | } 52 | } 53 | 54 | [KSPAction("Activate Detector")] 55 | public void EnableDetectionAction(KSPActionParam param) 56 | { 57 | EnableDetection(); 58 | } 59 | 60 | [KSPAction("Deactivate Detector")] 61 | public void DisableDetectionAction(KSPActionParam param) 62 | { 63 | DisableDetection(); 64 | } 65 | 66 | [KSPAction("Toggle Detector")] 67 | public void ToggleDetectionAction(KSPActionParam param) 68 | { 69 | IsDetecting = !IsDetecting; 70 | if (scanner != null) { 71 | scanner.IsDetecting = IsDetecting; 72 | } 73 | } 74 | 75 | [KSPEvent(guiActive = true, guiName = "Enable Scan Tone", active = true, externalToEVAOnly = true, guiActiveUnfocused = true, unfocusedRange = 1.5f)] 76 | public void EnableSounds() 77 | { 78 | ScanningSound = true; 79 | } 80 | 81 | [KSPEvent(guiActive = true, guiName = "Disable Scan Tone", active = false, externalToEVAOnly = true, guiActiveUnfocused = true, unfocusedRange = 1.5f)] 82 | public void DisableSounds() 83 | { 84 | ScanningSound = false; 85 | } 86 | 87 | [KSPField(isPersistant = false, guiActive = true, guiName = "Status")] 88 | public string Status; 89 | 90 | public override string GetInfo() 91 | { 92 | return String.Format("Maximum Altitude: {0:N0}m\nPower Consumption: {1:F2}/s\nScanning Period: {2:F2}s\nDetects: {3}", DetectingHeight, PowerConsumption, DetectingPeriod, String.Join(", ", resources.ToArray())); 93 | } 94 | 95 | public override void OnStart(PartModule.StartState state) 96 | { 97 | if (state == StartState.Editor) { return; } 98 | this.part.force_activate(); 99 | } 100 | 101 | public override void OnLoad(ConfigNode config) 102 | { 103 | if (this.configString == null) 104 | { 105 | this.configString = config.ToString(); 106 | } 107 | 108 | config = Misc.Parse(configString).GetNode("MODULE"); 109 | 110 | resources = config.GetNodes("Resource").Select(n => n.GetValue("Name")).ToList(); 111 | if (resources.Count == 0) 112 | { 113 | resources = KethaneController.ResourceDefinitions.Select(r => r.Resource).ToList(); 114 | } 115 | } 116 | 117 | public override void OnUpdate() 118 | { 119 | Events["EnableDetection"].active = !IsDetecting; 120 | Events["DisableDetection"].active = IsDetecting; 121 | Events["EnableSounds"].active = !ScanningSound; 122 | Events["DisableSounds"].active = ScanningSound; 123 | 124 | if (vessel.getTrueAltitude() <= this.DetectingHeight) 125 | { 126 | if (IsDetecting && scanner != null) 127 | { 128 | Status = scanner.powerRatio > 0 ? "Active" : "Insufficient Power"; 129 | } 130 | else 131 | { 132 | Status = "Idle"; 133 | } 134 | } 135 | else 136 | { 137 | Status = "Out Of Range"; 138 | } 139 | 140 | foreach (var animator in part.Modules.OfType()) 141 | { 142 | animator.IsDetecting = IsDetecting; 143 | if (scanner != null) { 144 | animator.PowerRatio = (float) scanner.powerRatio; 145 | } else { 146 | animator.PowerRatio = 0; 147 | } 148 | } 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/KethaneGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | 7 | namespace Kethane.PartModules 8 | { 9 | public class KethaneGenerator : PartModule 10 | { 11 | [KSPField(isPersistant = false)] 12 | public float KethaneRate; 13 | 14 | [KSPField(isPersistant = false)] 15 | public float PowerRate; 16 | 17 | [KSPField(isPersistant = false)] 18 | public float XenonMassRatio; 19 | 20 | [KSPField(isPersistant = false)] 21 | public float MaxEmission; 22 | 23 | [KSPField(isPersistant = false)] 24 | public float MinEmission; 25 | 26 | [KSPField(isPersistant = false, guiActive = true, guiName = "Output", guiFormat = "P1")] 27 | public double Output; 28 | 29 | [KSPField(isPersistant = true)] 30 | public bool Enabled; 31 | 32 | private AnimationState[] fanStates; 33 | private AnimationState[] slatStates; 34 | 35 | private KethaneParticleEmitter exhaustEmitter; 36 | 37 | private TimedMovingAverage output = new TimedMovingAverage(3); 38 | private TimedMovingAverage fanSpeed = new TimedMovingAverage(1); 39 | private Func logistic = x => (1 / (Math.Exp(15 * x - 10.5) + 1)); 40 | 41 | [KSPEvent(guiActive = true, guiName = "Enable Generator", active = true, externalToEVAOnly = true, guiActiveUnfocused = true, unfocusedRange = 1.5f)] 42 | public void Enable() 43 | { 44 | Enabled = true; 45 | } 46 | 47 | [KSPEvent(guiActive = true, guiName = "Disable Generator", active = false, externalToEVAOnly = true, guiActiveUnfocused = true, unfocusedRange = 1.5f)] 48 | public void Disable() 49 | { 50 | Enabled = false; 51 | } 52 | 53 | [KSPAction("Enable Generator")] 54 | public void EnableAction(KSPActionParam param) { Enable(); } 55 | 56 | [KSPAction("Disable Generator")] 57 | public void DisableAction(KSPActionParam param) { Disable(); } 58 | 59 | [KSPAction("Toggle Generator")] 60 | public void ToggleAction(KSPActionParam param) 61 | { 62 | Enabled = !Enabled; 63 | } 64 | 65 | public override void OnStart(PartModule.StartState state) 66 | { 67 | if (state == StartState.Editor) { return; } 68 | this.part.force_activate(); 69 | 70 | fanStates = this.part.SetUpAnimation("generatorFan_anim"); 71 | slatStates = this.part.SetUpAnimation("generatorSlats_anim"); 72 | 73 | foreach (var fanState in fanStates) 74 | { 75 | fanState.wrapMode = WrapMode.Loop; 76 | } 77 | 78 | exhaustEmitter = part.Modules.OfType().First(e => e.Label == "exhaust"); 79 | } 80 | 81 | public override void OnUpdate() 82 | { 83 | Events["Enable"].active = !Enabled; 84 | Events["Disable"].active = Enabled; 85 | 86 | exhaustEmitter.Emit = Output > 0; 87 | //XXX exhaustEmitter.MaxEmission = (float)(MaxEmission * Output); 88 | //XXX exhaustEmitter.MinEmission = (float)(MinEmission * Output); 89 | 90 | foreach (var state in fanStates) 91 | { 92 | state.speed = (float) fanSpeed.Average * 2f; 93 | } 94 | 95 | foreach (var state in slatStates) 96 | { 97 | state.normalizedTime = Mathf.Clamp01(state.normalizedTime); 98 | state.speed = (Output > 0 ? -1 : 1); 99 | } 100 | } 101 | 102 | public override void OnFixedUpdate() 103 | { 104 | double amount, maxAmount; 105 | part.GetConnectedResourceTotals("ElectricCharge", out amount, out maxAmount); 106 | var demand = (Enabled ? logistic(amount / maxAmount) : 0); 107 | 108 | if (demand < 0.1f) { demand = 0; } 109 | 110 | var pressure = FlightGlobals.getStaticPressure(part.transform.position); 111 | fanSpeed.Update(TimeWarp.fixedDeltaTime, demand * (2 * pressure) / (pressure * pressure + 1)); 112 | 113 | var pressureEfficiencyFactor = 0.5f; 114 | double kethaneDemand = demand * KethaneRate * TimeWarp.fixedDeltaTime / (1 + fanSpeed.Average * pressure * pressureEfficiencyFactor); 115 | 116 | double kethaneDrawn = part.RequestResource("Kethane", kethaneDemand); 117 | output.Update(TimeWarp.fixedDeltaTime, kethaneDemand > 0 ? demand * kethaneDrawn / kethaneDemand : 0); 118 | 119 | part.RequestResource("XenonGas", -kethaneDrawn * XenonMassRatio * PartResourceLibrary.Instance.GetDefinition("Kethane").density / PartResourceLibrary.Instance.GetDefinition("XenonGas").density); 120 | 121 | Output = output.Average; 122 | part.RequestResource("ElectricCharge", -Output * PowerRate * TimeWarp.fixedDeltaTime); 123 | } 124 | 125 | public override string GetInfo() 126 | { 127 | return String.Format("Kethane Consumption: {0:F1}L/s\nPower Generation: {1:F1}/s\nXenonGas Byproduct: {2:F2}L/s", KethaneRate, PowerRate, KethaneRate * XenonMassRatio * PartResourceLibrary.Instance.GetDefinition("Kethane").density / PartResourceLibrary.Instance.GetDefinition("XenonGas").density); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /Plugin/Kethane/Kethane.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {8D1501AC-6C11-459B-9561-6CC5E3EC15FE} 9 | Library 10 | Properties 11 | Kethane 12 | Kethane 13 | v3.5 14 | 512 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | False 37 | 38 | 39 | False 40 | 41 | 42 | 43 | 44 | 45 | False 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | Always 96 | 97 | 98 | 99 | 106 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/HeatSinkAnimator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEngine; 5 | 6 | namespace Kethane.PartModules 7 | { 8 | public class HeatSinkAnimator : PartModule, IMultipleDragCube 9 | { 10 | [KSPField(isPersistant = false)] 11 | public string HeatAnimation; 12 | 13 | [KSPField(isPersistant = false)] 14 | public string OpenAnimation; 15 | public string deployAnimationName; // for FAR 16 | 17 | [KSPField(isPersistant = false)] 18 | public float OpenTemperature; 19 | 20 | [KSPField(isPersistant = false)] 21 | public float MaxTemperature; 22 | 23 | [KSPField(isPersistant = false)] 24 | public float InternalDissipation; 25 | 26 | [KSPField(isPersistant = false)] 27 | public float HeatSinkDissipation; 28 | 29 | [KSPField(isPersistant = false)] 30 | public float PressureDissipation; 31 | 32 | [KSPField(isPersistant = false)] 33 | public float AirSpeedDissipation; 34 | 35 | [KSPField(isPersistant = false)] 36 | public Vector3 RadiatorNormal; 37 | 38 | private AnimationState[] heatAnimationStates; 39 | private AnimationState[] openAnimationStates; 40 | 41 | private float temperature; 42 | 43 | private float requested; 44 | private float lastRequested; 45 | private float dissipated; 46 | 47 | void FindAnimations() 48 | { 49 | deployAnimationName = OpenAnimation; 50 | openAnimationStates = this.part.SetUpAnimation(OpenAnimation); 51 | heatAnimationStates = this.part.SetUpAnimation(HeatAnimation); 52 | } 53 | 54 | public override void OnStart(PartModule.StartState state) 55 | { 56 | FindAnimations(); 57 | } 58 | 59 | public override void OnUpdate() 60 | { 61 | var draperPoint = 525; 62 | var heatFraction = (temperature - draperPoint) / (MaxTemperature - draperPoint); 63 | 64 | foreach (var state in heatAnimationStates) 65 | { 66 | state.normalizedTime = heatFraction; 67 | } 68 | 69 | var shouldOpen = temperature >= OpenTemperature; 70 | 71 | foreach (var state in openAnimationStates) 72 | { 73 | state.normalizedTime = Mathf.Clamp01(state.normalizedTime); 74 | } 75 | 76 | var openState = openAnimationStates.First(); 77 | var isMoving = (openState.normalizedTime > 0) && (openState.normalizedTime < 1); 78 | if (!isMoving) 79 | { 80 | var isOpen = openState.normalizedTime == 1; 81 | if (isOpen != shouldOpen) 82 | { 83 | foreach (var state in openAnimationStates) 84 | { 85 | state.speed = shouldOpen ? 1 : -1; 86 | } 87 | } 88 | } 89 | } 90 | 91 | [KSPField(isPersistant = false, guiActive = true, guiName = "Cooling Efficiency", guiFormat = "P1")] 92 | public float CoolingEfficiency; 93 | 94 | public override void OnFixedUpdate() 95 | { 96 | var position = this.part.transform.position; 97 | var outsideTemp = FlightGlobals.getExternalTemperature(FlightGlobals.getAltitudeAtPos(position), FlightGlobals.getMainBody()); 98 | var pressure = FlightGlobals.getStaticPressure(position); 99 | 100 | var surfaceVelocity = this.vessel.GetSrfVelocity(); 101 | var radiatorNormal = this.part.transform.InverseTransformDirection(RadiatorNormal); 102 | var airSpeed = (surfaceVelocity - Vector3.Dot(surfaceVelocity, radiatorNormal) * radiatorNormal).magnitude; 103 | 104 | var deployAmount = Mathf.Clamp01(openAnimationStates.First().normalizedTime); 105 | var rate = InternalDissipation + deployAmount * (HeatSinkDissipation + pressure * (PressureDissipation + AirSpeedDissipation * airSpeed)); 106 | 107 | SetDragState (deployAmount); 108 | 109 | temperature = (float) (outsideTemp + (temperature - outsideTemp) * Math.Exp(-rate * TimeWarp.fixedDeltaTime)); 110 | 111 | CoolingEfficiency = requested == 0 ? 1 : dissipated / requested; 112 | 113 | lastRequested = requested; 114 | requested = dissipated = 0; 115 | } 116 | 117 | public float AddHeat(float heat) 118 | { 119 | requested += heat; 120 | var remaining = MaxTemperature - (temperature - dissipated); 121 | var requestRatio = lastRequested == 0 ? 1 : Math.Min(heat / lastRequested, 1); 122 | heat = Math.Min(heat, remaining * requestRatio); 123 | temperature += heat; 124 | dissipated += heat; 125 | return heat; 126 | } 127 | 128 | void SetDragState (float t) 129 | { 130 | part.DragCubes.SetCubeWeight ("A", t); 131 | part.DragCubes.SetCubeWeight ("B", 1 - t); 132 | } 133 | 134 | public string[] GetDragCubeNames() 135 | { 136 | return new string[] {"A", "B"}; 137 | } 138 | 139 | public void AssumeDragCubePosition(string name) 140 | { 141 | FindAnimations(); 142 | 143 | float time = 0f; 144 | 145 | switch (name) { 146 | case "A": 147 | time = 1f; 148 | break; 149 | case "B": 150 | time = 0f; 151 | break; 152 | } 153 | foreach (var state in openAnimationStates) { 154 | state.normalizedTime = time; 155 | } 156 | } 157 | 158 | public bool IsMultipleCubesActive { get { return false; } } 159 | 160 | public bool UsesProceduralDragCubes() 161 | { 162 | return false; 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/KethaneDrillAnimatorLegacy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace Kethane.PartModules 5 | { 6 | public class KethaneDrillAnimatorLegacy : PartModule, IExtractorAnimator 7 | { 8 | private Transform BaseTransform, Cyl1Transform, Cyl2Transform, Cyl3Transform; 9 | 10 | [KSPField(isPersistant = true)] 11 | private bool ArmWantToGoDown = false; 12 | 13 | public enum DeployState 14 | { 15 | Idle, 16 | DeployBase, 17 | DeployArm1, 18 | DeployArm2, 19 | DeployArm3, 20 | Deployed, 21 | }; 22 | 23 | public DeployState DrillDeploymentState = DeployState.Idle; 24 | 25 | public void Update() 26 | { 27 | var dt = Time.deltaTime; 28 | var down = ArmWantToGoDown; 29 | 30 | if (DrillDeploymentState != DeployState.Idle) 31 | { 32 | float Rotation = dt * 3.75f; 33 | Cyl1Transform.Rotate(new Vector3(0, 1, 0), Rotation); 34 | Cyl2Transform.Rotate(new Vector3(0, 1, 0), Rotation); 35 | Cyl3Transform.Rotate(new Vector3(0, 1, 0), Rotation); 36 | } 37 | 38 | switch (DrillDeploymentState) 39 | { 40 | case DeployState.Idle: 41 | { 42 | 43 | if (down) 44 | DrillDeploymentState = DeployState.DeployBase; 45 | } break; 46 | 47 | case DeployState.DeployBase: 48 | { 49 | Vector3 Translation = new Vector3(-dt * 0.35f, 0, 0); 50 | BaseTransform.localPosition += (down ? Translation : -Translation); 51 | if (down) 52 | { 53 | if (BaseTransform.localPosition.x <= -0.35) 54 | { 55 | BaseTransform.localPosition = new Vector3(-0.35f, BaseTransform.localPosition.y, BaseTransform.localPosition.z); 56 | DrillDeploymentState = DeployState.DeployArm1; 57 | } 58 | } 59 | else 60 | { 61 | if (BaseTransform.localPosition.x >= -0.0521) 62 | { 63 | BaseTransform.localPosition = new Vector3(-0.0521f, BaseTransform.localPosition.y, BaseTransform.localPosition.z); 64 | DrillDeploymentState = DeployState.Idle; 65 | } 66 | } 67 | } break; 68 | 69 | case DeployState.DeployArm1: 70 | { 71 | float Speed = 0.5f; 72 | Vector3 Translation = new Vector3(0, -dt * Speed, 0); 73 | Cyl1Transform.localPosition += (down ? Translation : -Translation); 74 | if (down) 75 | { 76 | if (Cyl1Transform.localPosition.y <= -0.399f) 77 | { 78 | Cyl1Transform.localPosition = new Vector3(Cyl1Transform.localPosition.x, -0.399f, Cyl1Transform.localPosition.z); 79 | DrillDeploymentState = DeployState.DeployArm2; 80 | } 81 | } 82 | else 83 | { 84 | if (Cyl1Transform.localPosition.y >= 0.417346f) 85 | { 86 | Cyl1Transform.localPosition = new Vector3(Cyl1Transform.localPosition.x, 0.417346f, Cyl1Transform.localPosition.z); 87 | DrillDeploymentState = DeployState.DeployBase; 88 | } 89 | } 90 | } break; 91 | 92 | case DeployState.DeployArm2: 93 | { 94 | float Speed = 0.5f; 95 | Vector3 Translation = new Vector3(0, -dt * Speed, 0); 96 | Cyl2Transform.localPosition += (down ? Translation : -Translation); 97 | if (down) 98 | { 99 | if (Cyl2Transform.localPosition.y <= -0.899f) 100 | { 101 | Cyl2Transform.localPosition = new Vector3(Cyl2Transform.localPosition.x, -0.899f, Cyl2Transform.localPosition.z); 102 | DrillDeploymentState = DeployState.DeployArm3; 103 | } 104 | } 105 | else 106 | { 107 | if (Cyl2Transform.localPosition.y >= -0.01016799f) 108 | { 109 | Cyl2Transform.localPosition = new Vector3(Cyl2Transform.localPosition.x, -0.01016799f, Cyl2Transform.localPosition.z); 110 | DrillDeploymentState = DeployState.DeployArm1; 111 | } 112 | } 113 | } break; 114 | 115 | case DeployState.DeployArm3: 116 | { 117 | float Speed = 0.5f; 118 | Vector3 Translation = new Vector3(0, -dt * Speed, 0); 119 | Cyl3Transform.localPosition += (down ? Translation : -Translation); 120 | if (down) 121 | { 122 | if (Cyl3Transform.localPosition.y <= -0.899f) 123 | { 124 | Cyl3Transform.localPosition = new Vector3(Cyl3Transform.localPosition.x, -0.899f, Cyl3Transform.localPosition.z); 125 | DrillDeploymentState = DeployState.Deployed; 126 | } 127 | } 128 | else 129 | { 130 | if (Cyl3Transform.localPosition.y >= 0.037) 131 | { 132 | Cyl3Transform.localPosition = new Vector3(Cyl3Transform.localPosition.x, 0.037f, Cyl3Transform.localPosition.z); 133 | DrillDeploymentState = DeployState.DeployArm2; 134 | } 135 | } 136 | } break; 137 | 138 | case DeployState.Deployed: 139 | { 140 | if (down == false) 141 | DrillDeploymentState = DeployState.DeployArm3; 142 | } break; 143 | } 144 | } 145 | 146 | public override void OnStart(PartModule.StartState state) 147 | { 148 | BaseTransform = this.part.transform.Find("model").Find("Kethane Small Miner").Find("Main Box"); 149 | Cyl3Transform = BaseTransform.Find("1 Cyl"); 150 | Cyl2Transform = Cyl3Transform.Find("2 Cyl"); 151 | Cyl1Transform = Cyl2Transform.Find("3 Cyl"); 152 | } 153 | 154 | public void Deploy() 155 | { 156 | ArmWantToGoDown = true; 157 | } 158 | 159 | public void Retract() 160 | { 161 | ArmWantToGoDown = false; 162 | } 163 | 164 | public ExtractorState CurrentState 165 | { 166 | get 167 | { 168 | if (DrillDeploymentState == DeployState.Deployed) { return ExtractorState.Deployed; } 169 | if (DrillDeploymentState == DeployState.Idle) { return ExtractorState.Retracted; } 170 | return ArmWantToGoDown ? ExtractorState.Deploying : ExtractorState.Retracting; 171 | } 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /Plugin/Kethane/UserInterface/OverlayRenderer.cs: -------------------------------------------------------------------------------- 1 | using GeodesicGrid; 2 | using System; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using UnityEngine.Rendering; 6 | 7 | using Kethane.ShaderLoader; 8 | 9 | namespace Kethane.UserInterface 10 | { 11 | internal class OverlayRenderer : MonoBehaviour 12 | { 13 | private Mesh mesh; 14 | 15 | private int gridLevel = 0; 16 | private Func heightMap = c => 1; 17 | private float radiusMultiplier = 1; 18 | private Transform target = null; 19 | 20 | private Vector3 []vertices; 21 | private Color32 []colors32; 22 | private int []triangles; 23 | private bool colorsDirty; 24 | 25 | public bool IsVisible 26 | { 27 | get 28 | { 29 | var renderer = gameObject.GetComponent(); 30 | if (renderer == null) { return false; } 31 | return renderer.enabled; 32 | } 33 | set 34 | { 35 | var renderer = gameObject.GetComponent(); 36 | if (renderer == null) { throw new InvalidOperationException("OverlayRenderer has not started"); } 37 | renderer.enabled = value; 38 | } 39 | } 40 | 41 | protected void Awake() 42 | { 43 | setUpComponents(); 44 | updateArrays(); 45 | updateTriangles(); 46 | updateVertices(); 47 | updateTarget(); 48 | } 49 | 50 | protected void Update () 51 | { 52 | if (colorsDirty) { 53 | colorsDirty = false; 54 | mesh.colors32 = colors32; 55 | } 56 | } 57 | 58 | #region Configuration setters 59 | 60 | public void SetGridLevel(int gridLevel) 61 | { 62 | SetGridLevelAndHeightMap(gridLevel, heightMap); 63 | } 64 | 65 | public void SetHeightMap(Func heightMap) 66 | { 67 | SetGridLevelAndHeightMap(gridLevel, heightMap); 68 | } 69 | 70 | public void SetGridLevelAndHeightMap(int gridLevel, Func heightMap) 71 | { 72 | if (gridLevel < 0) { throw new ArgumentOutOfRangeException("gridLevel"); } 73 | if (heightMap == null) { throw new ArgumentNullException("heightMap"); } 74 | 75 | if (gridLevel != this.gridLevel) 76 | { 77 | this.gridLevel = gridLevel; 78 | updateArrays(); 79 | updateTriangles(); 80 | } 81 | else 82 | { 83 | if (heightMap == this.heightMap) { return; } 84 | } 85 | 86 | this.heightMap = heightMap; 87 | updateVertices(); 88 | } 89 | 90 | public void SetRadiusMultiplier(float radiusMultiplier) 91 | { 92 | if (radiusMultiplier < 0) { throw new ArgumentOutOfRangeException("radiusMultiplier"); } 93 | if (radiusMultiplier != this.radiusMultiplier) 94 | { 95 | this.radiusMultiplier = radiusMultiplier; 96 | updateScale(); 97 | } 98 | } 99 | 100 | public void SetTarget(Transform target) 101 | { 102 | if (target != this.target) 103 | { 104 | this.target = target; 105 | updateTarget(); 106 | } 107 | } 108 | 109 | #endregion 110 | 111 | #region Cell color setters 112 | 113 | public void SetCellColor(Cell cell, Color32 color) 114 | { 115 | setCellColor(cell, color); 116 | } 117 | 118 | public void SetCellColors(IDictionary assignments) 119 | { 120 | setCellColors(assignments); 121 | } 122 | 123 | public void SetCellColors(CellMap assignments) 124 | { 125 | setCellColors(assignments); 126 | } 127 | 128 | private void setCellColors(IEnumerable> assignments) 129 | { 130 | foreach (var assignment in assignments) 131 | { 132 | setCellColor(assignment.Key, assignment.Value); 133 | } 134 | } 135 | 136 | private void setCellColor(Cell cell, Color32 color) 137 | { 138 | var idx = cell.Index * 6; 139 | for (var i = idx; i < idx + 6; i++) 140 | { 141 | colors32[i] = color; 142 | } 143 | colorsDirty = true; 144 | } 145 | 146 | #endregion 147 | 148 | private void setUpComponents() 149 | { 150 | mesh = gameObject.AddComponent().mesh; 151 | var renderer = gameObject.AddComponent(); 152 | 153 | renderer.enabled = false; 154 | renderer.shadowCastingMode = ShadowCastingMode.Off; 155 | renderer.receiveShadows = false; 156 | 157 | var shader = KethaneShaderLoader.FindShader("Kethane/AlphaUnlitVertexColored"); 158 | if (shader != null) { 159 | var material = new Material(shader); 160 | 161 | var color = Color.white; 162 | color.a = 0.4f; 163 | material.color = color; 164 | 165 | renderer.material = material; 166 | } 167 | } 168 | 169 | private void updateArrays() 170 | { 171 | mesh.Clear(); 172 | uint faces = Cell.CountAtLevel(gridLevel); 173 | vertices = new Vector3[faces * 6]; 174 | colors32 = new Color32[vertices.Length]; 175 | triangles = new int[3 * (4 * (faces - 12) + 5 * 12)]; 176 | mesh.vertices = vertices; 177 | mesh.colors32 = colors32; 178 | } 179 | 180 | private void updateTriangles() 181 | { 182 | int tri = 0; 183 | foreach (var cell in Cell.AtLevel(gridLevel)) 184 | { 185 | var t = (int)cell.Index * 6; 186 | if (cell.IsPentagon) 187 | { 188 | for (var i = 0; i < 5; i++, tri += 3) 189 | { 190 | triangles[tri + 0] = t + 1 + i; 191 | triangles[tri + 1] = t + 1 + (i + 1) % 5; 192 | triangles[tri + 2] = t; 193 | } 194 | } 195 | else 196 | { 197 | for (var i = 0; i < 3; i++, tri += 3) 198 | { 199 | triangles[tri + 0] = t + 0 + i * 2; 200 | triangles[tri + 1] = t + 1 + i * 2; 201 | triangles[tri + 2] = t + (2 + i * 2) % 6; 202 | } 203 | triangles[tri + 0] = t + 0; 204 | triangles[tri + 1] = t + 2; 205 | triangles[tri + 2] = t + 4; 206 | tri += 3; 207 | } 208 | } 209 | 210 | mesh.triangles = triangles; 211 | } 212 | 213 | private void updateVertices() 214 | { 215 | int vert = 0; 216 | 217 | foreach (var cell in Cell.AtLevel(gridLevel)) 218 | { 219 | var center = cell.Position * heightMap(cell); 220 | 221 | if (cell.IsPentagon) 222 | { 223 | vertices[vert++] = center; 224 | } 225 | 226 | var blend = 0.08f; 227 | center *= blend; 228 | 229 | foreach (var vertex in cell.GetVertices(gridLevel, heightMap)) 230 | { 231 | vertices[vert++] = center + vertex * (1 - blend); 232 | } 233 | } 234 | 235 | mesh.vertices = vertices; 236 | mesh.RecalculateBounds(); 237 | } 238 | 239 | private void updateTarget() 240 | { 241 | if (target != null) 242 | { 243 | gameObject.layer = target.gameObject.layer; 244 | } 245 | gameObject.transform.parent = target; 246 | gameObject.transform.localPosition = Vector3.zero; 247 | gameObject.transform.localRotation = Quaternion.identity; 248 | updateScale(); 249 | } 250 | 251 | private void updateScale() 252 | { 253 | gameObject.transform.localScale = Vector3.one * 1000 * radiusMultiplier; 254 | } 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /Plugin/Kethane/Particles/ParticlePhysics.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | using KethaneParticles; 7 | 8 | namespace Kethane 9 | { 10 | 11 | public class ParticlePhysics : MonoBehaviour 12 | { 13 | 14 | public class FloatVolume 15 | { 16 | float []data; 17 | public Vector3 metric; 18 | public Vector3 scale; 19 | public Vector3 center; 20 | public Vector3Int size; 21 | 22 | Vector3Int mapPos (Vector3 position) 23 | { 24 | position = Vector3.Scale (position, scale) + center; 25 | return new Vector3Int ((int) (position.x * (size.x - 1) + 0.5), 26 | (int) (position.y * (size.y - 1) + 0.5), 27 | (int) (position.z * (size.z - 1) + 0.5)); 28 | } 29 | 30 | int mapPos (Vector3Int pos) 31 | { 32 | if (pos.x < 0 || pos.y < 0 || pos.z < 0 33 | || pos.x >= size.x || pos.y >= size.y || pos.z >= size.z) { 34 | return -1; 35 | } 36 | int ind = (pos.y * size.z + pos.z) * size.x + pos.x; 37 | return ind; 38 | } 39 | 40 | float GetData (Vector3Int vind) 41 | { 42 | int ind = mapPos (vind); 43 | if (ind < 0) { 44 | return 0; 45 | } 46 | return data[ind]; 47 | } 48 | 49 | public float this[Vector3 position] 50 | { 51 | get { 52 | return GetData (mapPos (position)); 53 | } 54 | set { 55 | int ind = mapPos (mapPos(position)); 56 | if (ind >= 0) { 57 | data[ind] = value; 58 | } 59 | } 60 | } 61 | 62 | Vector3Int X = new Vector3Int(1, 0, 0); 63 | Vector3Int Y = new Vector3Int(0, 1, 0); 64 | Vector3Int Z = new Vector3Int(0, 0, 1); 65 | 66 | float delta (Vector3Int ind, Vector3Int dir, float b, float x) 67 | { 68 | float a = GetData (ind - dir); 69 | float c = GetData (ind + dir); 70 | return (c - a) / 2 + x * (c + a - 2 * b); 71 | } 72 | 73 | public Vector3 Gradient (Vector3 position) 74 | { 75 | Vector3Int ind = mapPos (position); 76 | position = Vector3.Scale(position, scale) + center; 77 | position.x *= size.x - 1; 78 | position.y *= size.y - 1; 79 | position.z *= size.z - 1; 80 | position -= ind; 81 | Vector3 grad = Vector3.zero; 82 | float b = GetData (ind); 83 | grad.x = delta (ind, X, b, position.x); 84 | grad.y = delta (ind, Y, b, position.y); 85 | grad.z = delta (ind, Z, b, position.z); 86 | return Vector3.Scale (metric, grad); 87 | } 88 | 89 | public FloatVolume (Vector3 size, int sizeX, int sizeY, int sizeZ) 90 | { 91 | this.size.x = sizeX; 92 | this.size.y = sizeY; 93 | this.size.z = sizeZ; 94 | center = new Vector3 (0.5f, 0.5f, 0.5f); 95 | scale.x = 1f / size.x; 96 | scale.y = 1f / size.y; 97 | scale.z = 1f / size.z; 98 | metric = Vector3.Scale (scale, scale); 99 | data = new float[sizeX * sizeY * sizeZ]; 100 | } 101 | 102 | public void Clear () 103 | { 104 | int ind = 0; 105 | for (int i = data.Length; i-- > 0; ) { 106 | data[ind++] = 0; 107 | } 108 | } 109 | } 110 | 111 | public ParticleSystem psystem; 112 | public Vector3 gravity = new Vector3 (0, -9.8f, 0); 113 | public float dispersion = 500; 114 | public float drag = 0.05f; 115 | public float density = 1.2f; 116 | public float densityExp = -0.0001f; 117 | public float particleDensity = 1.1965f; 118 | public FloatVolume pressure; 119 | ParticleSystem.Particle []particles; 120 | float []part_sizes; 121 | List emitters; 122 | int lastEmitter; 123 | 124 | public void Load (ConfigNode node) 125 | { 126 | gravity = Utils.ParseVector3 (node, "gravity", gravity); 127 | dispersion = Utils.ParseFloat (node, "dispersion", dispersion); 128 | drag = Utils.ParseFloat (node, "drag", drag); 129 | density = Utils.ParseFloat (node, "density", density); 130 | densityExp = Utils.ParseFloat (node, "densityExp", densityExp); 131 | particleDensity = Utils.ParseFloat (node, "particleDensity", particleDensity); 132 | } 133 | 134 | public void AddEmitter (ParticleManager.Emitter emitter) 135 | { 136 | emitters.Add (emitter); 137 | } 138 | 139 | void Awake () 140 | { 141 | emitters = new List (); 142 | } 143 | 144 | void Start () 145 | { 146 | pressure = new FloatVolume (new Vector3 (400, 400, 400), 200, 200, 200); 147 | } 148 | 149 | void FixedUpdate () 150 | { 151 | if (psystem == null) { 152 | return; 153 | } 154 | float deltaTime = Time.fixedDeltaTime; 155 | InitializeIfNeeded (); 156 | Vector3 up = Vector3.up; 157 | if (FlightGlobals.ActiveVessel != null) { 158 | var body = FlightGlobals.ActiveVessel.mainBody; 159 | gravity = FlightGlobals.ActiveVessel.gravityForPos; 160 | double altitude = FlightGlobals.getAltitudeAtPos(Vector3d.zero, body); 161 | double pressure = FlightGlobals.getStaticPressure (altitude, body); 162 | double temperature = body.GetTemperature (altitude); 163 | density = (float) FlightGlobals.getAtmDensity (pressure, temperature, body); 164 | //Debug.Log ($"[ParticlePhysics] density: {density}"); 165 | up = -gravity.normalized; 166 | } 167 | 168 | int numParticles = psystem.GetParticles (particles); 169 | //Debug.Log($"[ParticleSystem] {gameObject.name} {numParticles}"); 170 | 171 | for (int i = numParticles; i-- > 0; ) { 172 | float p = particles[i].GetCurrentSize(psystem); 173 | part_sizes[i] = p; 174 | pressure[particles[i].position] += p * Mathf.Exp(-p); 175 | } 176 | for (int i = numParticles; i-- > 0; ) { 177 | Vector3 pos = particles[i].position; 178 | Vector3 vel = particles[i].velocity; 179 | Vector3 grad = pressure.Gradient (pos); 180 | float dens = density * Mathf.Exp(densityExp * Vector3.Dot(pos, up)); 181 | float buoy = (particleDensity - dens) / particleDensity; 182 | //Debug.Log ($"[ParticlePhysics] {pos} {pressure[pos]} {grad * 1000}"); 183 | Vector3 force = gravity * buoy; 184 | force += -grad * part_sizes[i] * dispersion; 185 | force -= vel * drag * dens; 186 | if (pos.y < 0) { 187 | force.y = -pos.y; 188 | } 189 | Vector3 ov = vel; 190 | particles[i].velocity = ov + force * deltaTime; 191 | //if (i == 0) { 192 | // Debug.Log ($"[ParticlePhysics] {pos} {grad} {force} {buoy} {gravity} {buoy * gravity} {particles[i].velocity} {dens} {drag}"); 193 | //} 194 | } 195 | int maxParticles = psystem.main.maxParticles; 196 | int count = emitters.Count; 197 | int eind = lastEmitter; 198 | for (int i = 0; i < count && numParticles < maxParticles; i++) { 199 | eind = (lastEmitter + i) % count; 200 | var emitter = emitters[eind]; 201 | if (!emitter.enabled) { 202 | continue; 203 | } 204 | emitter.emission += emitter.rate * deltaTime; 205 | int num = (int) emitter.emission; 206 | emitter.emission -= num; 207 | //Debug.Log ($"[ParticlePhysics] emit {num} {emitter.emission}"); 208 | while (num-- > 0 && numParticles < maxParticles) { 209 | SetParticle (emitter, ref particles[numParticles++]); 210 | } 211 | } 212 | lastEmitter = (eind + 1) % count; 213 | psystem.SetParticles (particles, numParticles); 214 | } 215 | 216 | void SetParticle (ParticleManager.Emitter emitter, ref ParticleSystem.Particle particle) 217 | { 218 | float x = UnityEngine.Random.value; 219 | float y = UnityEngine.Random.value; 220 | var main = psystem.main; 221 | particle.angularVelocity = (x * 2) - 1; 222 | particle.startColor = main.startColor.Evaluate (x, y);; 223 | particle.position = emitter.position; 224 | particle.rotation = main.startRotation.Evaluate (x, y); 225 | particle.startSize = main.startSize.Evaluate (x, y); 226 | particle.startLifetime = main.startLifetime.Evaluate (x, y); 227 | particle.remainingLifetime = particle.startLifetime; 228 | particle.velocity = main.startSpeed.Evaluate (x, y) * (emitter.direction + x * emitter.spreadX + y * emitter.spreadY); 229 | } 230 | 231 | void InitializeIfNeeded () 232 | { 233 | if (particles == null || particles.Length < psystem.main.maxParticles) { 234 | particles = new ParticleSystem.Particle[psystem.main.maxParticles]; 235 | part_sizes = new float[psystem.main.maxParticles]; 236 | } 237 | } 238 | 239 | void OnDestroy () 240 | { 241 | //Debug.Log ($"[ParticlePhysics] OnDestroy"); 242 | } 243 | } 244 | 245 | } 246 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/KethaneConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Kethane.PartModules 7 | { 8 | public class KethaneConverter : PartModule 9 | { 10 | private struct ResourceRate 11 | { 12 | public String Resource { get; private set; } 13 | public double Rate { get; private set; } 14 | public bool Optional { get; private set; } 15 | 16 | public ResourceRate(String resource, double rate) : this(resource, rate, false) { } 17 | 18 | public ResourceRate(String resource, double rate, bool optional) 19 | : this() 20 | { 21 | Resource = resource; 22 | Rate = rate; 23 | Optional = optional; 24 | } 25 | 26 | public static ResourceRate operator *(ResourceRate rate, double multiplier) 27 | { 28 | return new ResourceRate(rate.Resource, rate.Rate * multiplier, rate.Optional); 29 | } 30 | } 31 | 32 | [KSPField(isPersistant = false)] 33 | public bool AlwaysActive; 34 | 35 | [KSPField(isPersistant = false)] 36 | public String Label; 37 | 38 | [KSPField(isPersistant = false)] 39 | public float HeatProduction; 40 | 41 | [KSPField(isPersistant = true)] 42 | public bool IsEnabled; 43 | 44 | public string configString; 45 | 46 | private ResourceRate[] inputRates; 47 | private ResourceRate[] outputRates; 48 | 49 | private Dictionary resourceActivity = new Dictionary(); 50 | 51 | public Dictionary ResourceActivity 52 | { 53 | get { return new Dictionary(resourceActivity); } 54 | } 55 | 56 | [KSPEvent(guiActive = true, guiName = "Activate Converter", active = true, externalToEVAOnly = true, guiActiveUnfocused = true, unfocusedRange = 1.5f)] 57 | public void ActivateConverter() 58 | { 59 | IsEnabled = true; 60 | } 61 | 62 | [KSPEvent(guiActive = true, guiName = "Deactivate Converter", active = false, externalToEVAOnly = true, guiActiveUnfocused = true, unfocusedRange = 1.5f)] 63 | public void DeactivateConverter() 64 | { 65 | IsEnabled = false; 66 | } 67 | 68 | [KSPAction("Activate Converter")] 69 | public void ActivateConverterAction(KSPActionParam param) 70 | { 71 | ActivateConverter(); 72 | } 73 | 74 | [KSPAction("Deactivate Converter")] 75 | public void DeactivateConverterAction(KSPActionParam param) 76 | { 77 | DeactivateConverter(); 78 | } 79 | 80 | [KSPAction("Toggle Converter")] 81 | public void ToggleConverterAction(KSPActionParam param) 82 | { 83 | IsEnabled = !IsEnabled; 84 | } 85 | 86 | public override string GetInfo() 87 | { 88 | var sb = new StringBuilder(); 89 | if (HeatProduction > 0) 90 | { 91 | sb.AppendFormat("Waste heat production: {0:F1}", HeatProduction); 92 | sb.AppendLine(); 93 | } 94 | getRateGroupInfo(sb, "Inputs", inputRates); 95 | getRateGroupInfo(sb, "Outputs", outputRates); 96 | return sb.ToString(); 97 | } 98 | 99 | private static void getRateGroupInfo(StringBuilder sb, String heading, IEnumerable rates) 100 | { 101 | sb.Append(""); 102 | sb.Append(heading); 103 | sb.AppendLine(":"); 104 | foreach (var rate in rates) 105 | { 106 | sb.AppendFormat("- {0}: {1:N2}/s", rate.Resource, rate.Rate); 107 | if (rate.Optional) { 108 | sb.Append(" (optional)"); 109 | } 110 | sb.AppendLine(); 111 | } 112 | } 113 | 114 | public override void OnLoad(ConfigNode config) 115 | { 116 | if (this.configString == null) 117 | { 118 | this.configString = config.ToString(); 119 | } 120 | 121 | loadConfig(); 122 | } 123 | 124 | private void loadConfig() 125 | { 126 | ConfigNode config = Misc.Parse(configString).GetNode("MODULE"); 127 | var definitions = PartResourceLibrary.Instance.resourceDefinitions; 128 | 129 | inputRates = loadRates(config.GetNode("InputRates")).ToArray(); 130 | var inputMassRate = inputRates.Sum(p => p.Rate * definitions[p.Resource].density); 131 | 132 | var outRates = loadRates(config.GetNode("OutputRatios")).ToList(); 133 | var reqOutRates = outRates.Where(r => !r.Optional).Select(r => r * (inputMassRate / definitions[r.Resource].density)).GroupBy(r => r.Resource).Select(g => new ResourceRate(g.Key, g.Sum(r => r.Rate), false)); 134 | var optOutRates = outRates.Where(r => r.Optional).Select(r => r * (inputMassRate / definitions[r.Resource].density)).GroupBy(r => r.Resource).Select(g => new ResourceRate(g.Key, g.Sum(r => r.Rate), true)); 135 | 136 | outputRates = reqOutRates.Concat(optOutRates).Concat(loadRates(config.GetNode("OutputRates"))).ToArray(); 137 | 138 | if (Label == null) 139 | { 140 | Label = String.Join("/", outputRates.Select(r => r.Resource).ToArray()); 141 | } 142 | } 143 | 144 | private static IEnumerable loadRates(ConfigNode config) 145 | { 146 | if (config == null) { yield break; } 147 | 148 | foreach (var entry in config.values.Cast()) 149 | { 150 | var name = entry.name; 151 | bool optional = name.EndsWith("*"); 152 | if (optional) 153 | { 154 | name = name.Substring(0, name.Length - 1); 155 | } 156 | var rate = Misc.Parse(entry.value, 0.0); 157 | if (PartResourceLibrary.Instance.GetDefinition(name) != null && rate > 0) 158 | { 159 | yield return new ResourceRate(name, rate, optional); 160 | } 161 | } 162 | } 163 | 164 | public override void OnStart(PartModule.StartState state) 165 | { 166 | loadConfig(); 167 | 168 | Actions["ActivateConverterAction"].guiName = Events["ActivateConverter"].guiName = String.Format("Activate {0} Converter", Label); 169 | Actions["DeactivateConverterAction"].guiName = Events["DeactivateConverter"].guiName = String.Format("Deactivate {0} Converter", Label); 170 | Actions["ToggleConverterAction"].guiName = String.Format("Toggle {0} Converter", Label); 171 | 172 | Events["ActivateConverter"].guiActive = Events["DeactivateConverter"].guiActive = !AlwaysActive; 173 | 174 | if (state == StartState.Editor) { return; } 175 | this.part.force_activate(); 176 | } 177 | 178 | public override void OnUpdate() 179 | { 180 | Events["ActivateConverter"].active = !IsEnabled; 181 | Events["DeactivateConverter"].active = IsEnabled; 182 | } 183 | 184 | double ResouceCapacity(ResourceRate r) 185 | { 186 | double amount, maxAmount; 187 | part.GetConnectedResourceTotals(r.Resource, out amount, out maxAmount, r.Rate > 0); 188 | return amount; 189 | } 190 | 191 | public override void OnFixedUpdate() 192 | { 193 | resourceActivity.Clear(); 194 | if (!IsEnabled && !AlwaysActive) { return; } 195 | 196 | var rates = outputRates.Select(r => r * -1).Concat(inputRates).Select(r => r * TimeWarp.fixedDeltaTime).ToArray(); 197 | var ratio = rates.Where(r => !r.Optional).Select(r => ResouceCapacity(r) / Math.Abs(r.Rate)).Where(r => r < 1).DefaultIfEmpty(1).Min(); 198 | 199 | var heatsink = this.part.Modules.OfType().SingleOrDefault(); 200 | if (ratio > 0 && heatsink != null) 201 | { 202 | var heatRequest = (float)ratio * HeatProduction * TimeWarp.fixedDeltaTime; 203 | ratio *= heatsink.AddHeat(heatRequest) / heatRequest; 204 | } 205 | 206 | foreach (var rate in rates) 207 | { 208 | resourceActivity[rate.Resource] = this.part.RequestResource(rate.Resource, rate.Rate * ratio); 209 | } 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /Plugin/Kethane/PartModules/KethaneExtractor.cs: -------------------------------------------------------------------------------- 1 | using GeodesicGrid; 2 | using Kethane.UserInterface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using UnityEngine; 7 | 8 | namespace Kethane.PartModules 9 | { 10 | public class KethaneExtractor : PartModule 11 | { 12 | public class Resource 13 | { 14 | public string Name { get; private set; } 15 | public float Rate { get; private set; } 16 | 17 | public Resource(ConfigNode node) 18 | { 19 | Name = node.GetValue("Name"); 20 | Rate = float.Parse(node.GetValue("Rate")); 21 | } 22 | } 23 | 24 | private class DefaultExtractorAnimator : IExtractorAnimator 25 | { 26 | public ExtractorState CurrentState { get; private set; } 27 | public void Deploy() { CurrentState = ExtractorState.Deployed; } 28 | public void Retract() { CurrentState = ExtractorState.Retracted; } 29 | 30 | public DefaultExtractorAnimator() 31 | { 32 | CurrentState = ExtractorState.Retracted; 33 | } 34 | } 35 | 36 | private IExtractorAnimator animator; 37 | 38 | private List resources; 39 | 40 | [KSPField(isPersistant = false)] 41 | public float PowerConsumption; 42 | 43 | [KSPField(isPersistant = false, guiActive = true, guiName = "Status")] 44 | public string Status; 45 | 46 | [KSPField(isPersistant = false)] 47 | public string HeadTransform; 48 | 49 | [KSPField(isPersistant = false)] 50 | public string TailTransform; 51 | 52 | [KSPField(isPersistant = false)] 53 | public float HeadOffset; 54 | 55 | [KSPField(isPersistant = false)] 56 | public float TailOffset; 57 | 58 | public string configString; 59 | 60 | private Transform headTransform; 61 | private Transform tailTransform; 62 | 63 | private KethaneParticleEmitter[] emitters; 64 | 65 | public override void OnStart(PartModule.StartState state) 66 | { 67 | this.part.force_activate(); 68 | animator = part.Modules.OfType().SingleOrDefault(); 69 | 70 | if (animator == null) 71 | { 72 | animator = new DefaultExtractorAnimator(); 73 | } 74 | else 75 | { 76 | Events["DeployDrill"].guiActiveEditor = true; 77 | Events["RetractDrill"].guiActiveEditor = true; 78 | } 79 | 80 | headTransform = this.part.FindModelTransform(HeadTransform); 81 | tailTransform = this.part.FindModelTransform(TailTransform); 82 | 83 | if (state == StartState.Editor) { return; } 84 | if (FlightGlobals.fetch == null) { return; } 85 | 86 | emitters = part.Modules.OfType().ToArray(); 87 | } 88 | 89 | public override void OnLoad(ConfigNode config) 90 | { 91 | if (this.configString == null) 92 | { 93 | this.configString = config.ToString(); 94 | } 95 | config = Misc.Parse(configString).GetNode("MODULE"); 96 | 97 | resources = config.GetNodes("Resource").Select(n => new Resource(n)).ToList(); 98 | } 99 | 100 | [KSPEvent(guiActive = true, guiName = "Deploy Drill", active = true, externalToEVAOnly = true, guiActiveUnfocused = true, unfocusedRange = 1.5f)] 101 | public void DeployDrill() 102 | { 103 | animator.Deploy(); 104 | } 105 | 106 | [KSPEvent(guiActive = true, guiName = "Retract Drill", active = false, externalToEVAOnly = true, guiActiveUnfocused = true, unfocusedRange = 1.5f)] 107 | public void RetractDrill() 108 | { 109 | animator.Retract(); 110 | } 111 | 112 | [KSPAction("Deploy Drill")] 113 | public void DeployDrillAction(KSPActionParam param) 114 | { 115 | DeployDrill(); 116 | } 117 | 118 | [KSPAction("Retract Drill")] 119 | public void RetractDrillAction(KSPActionParam param) 120 | { 121 | RetractDrill(); 122 | } 123 | 124 | [KSPAction("Toggle Drill")] 125 | public void ToggleDrillAction(KSPActionParam param) 126 | { 127 | if (animator.CurrentState == ExtractorState.Deployed || animator.CurrentState == ExtractorState.Deploying) 128 | { 129 | RetractDrill(); 130 | } 131 | else if (animator.CurrentState == ExtractorState.Retracted || animator.CurrentState == ExtractorState.Retracting) 132 | { 133 | DeployDrill(); 134 | } 135 | } 136 | 137 | public override string GetInfo() 138 | { 139 | return String.Concat(resources.Select(r => String.Format("{0} Rate: {1:F2}L/s\n", r.Name, r.Rate)).ToArray()) + String.Format("Power Consumption: {0:F2}/s", PowerConsumption); 140 | } 141 | 142 | public void Update() 143 | { 144 | var retracted = (animator.CurrentState == ExtractorState.Retracted); 145 | var deployed = (animator.CurrentState == ExtractorState.Deployed); 146 | if (Events["DeployDrill"].active != retracted || Events["RetractDrill"].active != deployed) 147 | { 148 | Events["DeployDrill"].active = retracted; 149 | Events["RetractDrill"].active = deployed; 150 | foreach (var window in GameObject.FindObjectsOfType(typeof(UIPartActionWindow)).OfType().Where(w => w.part == part)) 151 | { 152 | window.displayDirty = true; 153 | } 154 | } 155 | Status = animator.CurrentState.ToString(); 156 | 157 | if (!HighLogic.LoadedSceneIsFlight) { return; } 158 | 159 | if (animator.CurrentState != ExtractorState.Retracted) { 160 | RaycastHit hitInfo; 161 | var hit = raycastGround(out hitInfo); 162 | 163 | foreach (var emitter in emitters) { 164 | if (hit) { 165 | emitter.Position = hitInfo.point; 166 | emitter.Direction = tailTransform.position - headTransform.position; 167 | } 168 | if (emitter.Label != "gas") { 169 | emitter.Emit = hit; 170 | emitter.Rate = 20; 171 | } else { 172 | var bodyResource = getBodyResources("Kethane"); 173 | if (bodyResource != null && animator.CurrentState == ExtractorState.Deployed) { 174 | emitter.Emit = hit && bodyResource.GetQuantity(getCellUnder()) != null; 175 | emitter.Rate = 20; 176 | } else { 177 | emitter.Emit = false; 178 | } 179 | } 180 | } 181 | } else { 182 | foreach (var emitter in emitters) { 183 | emitter.Emit = false; 184 | } 185 | } 186 | } 187 | 188 | public override void OnFixedUpdate() 189 | { 190 | if (animator.CurrentState != ExtractorState.Deployed) { return; } 191 | if (!raycastGround()) { return; } 192 | 193 | double energyRequest = this.PowerConsumption * TimeWarp.fixedDeltaTime; 194 | double energyRatio = this.part.RequestResource("ElectricCharge", energyRequest) / energyRequest; 195 | 196 | foreach (var resource in resources) { 197 | var cell = getCellUnder(); 198 | var bodyResources = getBodyResources(resource.Name); 199 | if (bodyResources != null) { 200 | var deposit = bodyResources.GetQuantity(cell); 201 | if (deposit == null) { continue; } 202 | 203 | double amount = TimeWarp.fixedDeltaTime * resource.Rate * energyRatio; 204 | amount = Math.Min(amount, deposit.Value); 205 | bodyResources.Extract(cell, -this.part.RequestResource(resource.Name, -amount)); 206 | } 207 | } 208 | } 209 | 210 | private Cell getCellUnder() 211 | { 212 | return MapOverlay.GetCellUnder(this.vessel.mainBody, this.vessel.transform.position); 213 | } 214 | 215 | private IBodyResources getBodyResources(string resourceName) 216 | { 217 | if (KethaneData.Current == null) { 218 | return null; 219 | } 220 | return KethaneData.Current[resourceName][this.vessel.mainBody].Resources; 221 | } 222 | 223 | private bool raycastGround() 224 | { 225 | RaycastHit hitInfo; 226 | return raycastGround(out hitInfo); 227 | } 228 | 229 | private bool raycastGround(out RaycastHit hitInfo) 230 | { 231 | var mask = 1 << 15; 232 | var direction = headTransform.position - tailTransform.position; 233 | return Physics.Raycast(tailTransform.position - direction.normalized * TailOffset, direction, out hitInfo, direction.magnitude + HeadOffset + TailOffset, mask); 234 | } 235 | } 236 | } 237 | --------------------------------------------------------------------------------