spiritplumber
Members-
Posts
139 -
Joined
-
Last visited
Content Type
Profiles
Forums
Developer Articles
KSP2 Release Notes
Everything posted by spiritplumber
-
This is a simple solar sail, and a simple quadcopter style electric impeller. No plugins, no new graphics. I'd like to see these go in larger mods. Note that the solar sail will actually have to be "tacked"! http://www.ugcs.caltech.edu/~diedrich/solarsails/intro/tacking.html This is the solar sail; it may be expedient to turn sun tracking off for it. RESOURCE_DEFINITION { name = SolarWindIntensity density = 0.0001 flowMode = NO_FLOW transfer = NONE } PART { // --- general parameters --- name = SolarSail2 module = Part author = NovaSilisko+mkb // --- asset parameters --- MODEL { model = Squad/Parts/Electrical/largeSolarPanel/model position = 0, 0, 0 scale = 1.0, 3.0, 0.5 rotation = 0, 0, 0 // parent = anotherModelTransform <---------Not necessary unless Second or subsequent part. // texture = model000 , Squad/Parts/Command/probeCoreOcto/model000 // texture = model001 , Squad/Parts/FuelTank/fuelTank2-2/model001 } MODEL { model = Squad/Parts/Utility/CircularIntake/model position = 0, 0, 0 scale = 0.5, 2.0, 0.5 rotation = 0, 0, 90 // parent = anotherModelTransform <---------Not necessary unless Second or subsequent part. // texture = model000 , Squad/Parts/Command/probeCoreOcto/model000 // texture = model001 , Squad/Parts/FuelTank/fuelTank2-2/model001 } // --- node definitions --- // definition format is Position X, Position Y, Position Z, Up X, Up Y, Up Z node_attach = -0.05, 0.0, 0.0, 1, 0, 0 // --- editor parameters --- TechRequired = advScienceTech entryCost = 666 Cost =5000 category = Science subcategory = 0 title = LH-CP Orientable solar Sail manufacturer = Robots Everywhere LLC description = A thin, reflective surface that can be used to reflect solar radiation to provide thrust. Very fragile. // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision attachRules = 0,1,0,0,1 // --- standard part parameters --- mass = 0.35 // same as the big solar panel, for ease of probe building. dragModelType = default maximum_drag = 0.2 minimum_drag = 0.2 angularDrag = 1 crashTolerance = 1 // very fragile maxTemp = 3200 fx_exhaustFlame_white_tiny = 0.0, 1.0, 0.163, 0.0, 1.0, 0.0, running RESOURCE { name = SolarWindIntensity amount = 0 maxAmount = 1.0 } MODULE { name = ModuleDeployableSolarPanel animationName = bigsolarpanel sunTracking = false raycastTransformName = suncatcher resourceName = SolarWindIntensity chargeRate = 20.0 powerCurve { key = 206000000000 0 0 0 key = 13599840256 1 0 0 key = 68773560320 0.5 0 0 key = 0 10 0 0 } } MODULE { name = ModuleEngines thrustVectorTransformName = suncatcher exhaustDamage = False allowShutdown = False throttleLocked = True ignitionThreshold = 0.01 minThrust = 0 maxThrust = 1.0 heatProduction = 0.1 fxOffset = 0.0, 0.0, -0.7 PROPELLANT { name = SolarWindIntensity ratio = 1.0 DrawGauge = True } atmosphereCurve { key = 0 100 key = 0.1 100 key = 0.5 100 key = 0 100 } } } This is the electric impeller; it will work in oxygenless atmospheres, but not up to a very great height. RESOURCE_DEFINITION { name = ImpellerAirflow density = 0.004 flowMode = NO_FLOW transfer = NONE } PART { // Kerbal Space Program - Part Config // RT-10 Solid Fuel Booster // MODEL { model = Squad/Parts/Engine/sepMotor1/model position = 0, 0, 0 scale = 3.0, 0.3, 3.0 rotation = 0, 0, 0 // parent = anotherModelTransform <---------Not necessary unless Second or subsequent part. // texture = model000 , Squad/Parts/Command/probeCoreOcto/model000 // texture = model001 , Squad/Parts/FuelTank/fuelTank2-2/model001 } MODEL { model = Squad/Parts/Utility/CircularIntake/model position = 0, 0.07, 0.6 scale = 0.3, 0.3, 0.3 rotation = 0, 0, 0 // parent = anotherModelTransform <---------Not necessary unless Second or subsequent part. // texture = model000 , Squad/Parts/Command/probeCoreOcto/model000 // texture = model001 , Squad/Parts/FuelTank/fuelTank2-2/model001 } // --- general parameters --- name = ElectricImpeller module = Part author = NovaSilisko+mkb // --- asset parameters --- mesh = model.mu scale = 1 node_attach = 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1 // --- FX definitions --- fx_exhaustFlame_white_tiny = 0.0, 1.0, 0.163, 0.0, 1.0, 0.0, running // --- Sound FX definition --- sound_vent_medium = disengage sound_rocket_mini = disengage sound_vent_soft = disengage // --- editor parameters --- TechRequired = electrics entryCost = 666 Cost =100 category = Propulsion subcategory = 0 title = Electric Impeller manufacturer = Robots Everywhere LLC description = A light, powerful radially mounted electric motor that can be used in any atmosphere, if power is available. Useless at high altitudes and in vacuum, but does not require oxygen. // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision attachRules = 0,1,0,0,0 // --- standard part parameters --- mass = 0.01 dragModelType = default maximum_drag = 0.3 minimum_drag = 0.2 angularDrag = 2 crashTolerance = 7 maxTemp = 3600 MODULE { name = ModuleEngines thrustVectorTransformName = thrustTransform throttleLocked = False exhaustDamage = False allowShutdown = True ignitionThreshold = 0.01 minThrust = 0 maxThrust = 1.2 heatProduction = 0.1 fxOffset = 0.0, 0.0, -0.7 PROPELLANT { name = ImpellerAirflow ratio = 5.0 DrawGauge = True } PROPELLANT { name = ElectricCharge ratio = 12.0 DrawGauge = False } atmosphereCurve { key = 0 800 key = 0.1 1200 key = 0.5 1200 key = 1 1000 } } MODULE { name = ModuleResourceIntake resourceName = ImpellerAirflow checkForOxygen = false area = 0.00000666 IntakeSpeed = 1 IntakeTransformName = Intake } RESOURCE { name = ImpellerAirflow amount = 0.0 maxAmount = 0.00125 } }
-
Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship! Spaceship!
-
Spaceship spaceship spaceship spaceship spaceship spaceship spaceship spaceship spaceship spaceship spaceship spaceship spaceship spaceship spaceship spaceship
-
Ooooh This looks interesting!
-
Mini sounding rocket + EVA extender
spiritplumber replied to spiritplumber's topic in KSP1 Mod Releases
I have tried it, and it does work. I agree on the bottom node - it doesn't seem to work anyway, this is intended to be connected radially. -
A self-decoupling small solid rocket, can be used as a missile / sounding rocket. If carried on EVA using KAS, it can be used to extend the EVA tank. Uses existing models and textures to save memory. RESOURCE_DEFINITION { name = EVA Propellant density = 0.004 flowMode = ALL_VESSEL transfer = PUMP isTweakable = true } PART { // --- general parameters --- name = MiniMissile module = Part author = mkb MODEL { model = Squad/Parts/Engine/solidBooster/model position = 0, 0, 0 scale = 0.1, 0.2, 0.1 rotation = 0, 0, 0 } // --- node definitions --- node_stack_bottom = 0.0, -12.5127, 0.0, 0.0, 1.0, 0.0, 1 node_stack_top = 0.0, 0.5, 0.0, 0.0, 1.0, 0.0, 1 node_attach = 0.0, 0.0, -0.075, 0.0, 0.0, 1.0, 1 //de_attach = 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1 // --- FX definitions --- fx_exhaustFlame_yellow_tiny = 0.0, -0.17, 0.163, 0.0, 1.0, 0.0, running // --- Sound FX definition --- sound_vent_medium = engage sound_rocket_mini = running sound_vent_soft = disengage // --- editor parameters --- TechRequired = generalRocketry entryCost = 666 Cost =100 category = Science subcategory = 0 title = Mini Sounding Rocket manufacturer = Robots Everywhere, LLC description = Not intended for use as a weapon, honest. Can, however, be used to refill EVA packs when used with the Kerbal Attachment System. // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision attachRules = 0,1,1,0,1 // --- standard part parameters --- mass = 0.02 dragModelType = default maximum_drag = 0.2 minimum_drag = 0.2 angularDrag = 2 crashTolerance = 12 explosionPotential=10.0 maxTemp = 3600 ActivatesEvenIfDisconnected = true stagingIcon = SOLID_BOOSTER MODULE { name = ModuleEngines thrustVectorTransformName = thrustTransform throttleLocked = True exhaustDamage = True allowShutdown = False ignitionThreshold = 0.1 minThrust = 0 maxThrust = 18 heatProduction = 550 fxOffset = 0, 0, 0.01 PROPELLANT { name = EVA Propellant ratio = 1.0 DrawGauge = True } atmosphereCurve { key = 0 100 key = 1 100 } } RESOURCE { name = EVA Propellant amount = 8 maxAmount = 8 } MODULE { name = ModuleDecouple isOmniDecoupler = true explosiveNodeID = srf ejectionForce = 0.0 } MODULE { name = KASModuleGrab //This module will add the possibility to grab the part from Eva. grabKey = g // Key used for grabbing a part maxDistance = 3 // Max distance for grabbing a part breakForce = 3 // The force that needs to be applied on the grab joint to break. (Not used, as eva don't react correctly to a physic joint) evaPartPos = (0.0, 0.1, -0.1) // Position of the grabbed part on eva evaPartRot = (70.0, -180.0, 90.0) // Rotation of the grabbed part on eva addPartMass = false // Add or not the grabbed part mass to eva grabSndPath = KAS/Sounds/grab // Grab sound } MODULE { name = KASModuleAttach // This module will add the possibility to attach part grabbed from Eva. attachKey = h // Key used to toggle attach pointer rotateLeftKey = b // Key used to rotate left the attach pointer rotateRightKey = n // Key used to rotate right the attach pointer surfaceDist = 0.0 // Surface distance above the target part maxDistance = 3 // Max distance for attaching a part allowPart = True // Disable/enable attaching a part to another part allowEva = False // Disable/enable attaching a part to eva allowStatic = True // Disable/enable attaching a part to a static object like ground or building. sendMsgOnly = False // Delegate the attachment system to another module on attach. (ex : hook module) If set to false, the attachment will be handled by this module. pointerUseModel = True // Use or not the current part model as pointer instead of a sphere. partRot = (-90.0, -15.0, 0.0) // Rotation of the attached part attachSndPath = KAS/Sounds/attach // Attach sound path detachSndPath = KAS/Sounds/detach // Detach sound path } }
-
This uses stock textures/models, so it's very simple. I wanted to have something that would work as an air breathing electric engine, without installing plugins or loading extra textures, because I'm on an older computer. It is possible to circumnavigate Kerbin with this, but it takes a while (I used Mechjeb, attitude hold, and actually managed to nap through the flight). RESOURCE_DEFINITION { name = EIntakeAir density = 0.001 flowMode = NO_FLOW transfer = NONE } PART { MODEL { model = Squad/Parts/Engine/sepMotor1/model position = 0, 0, 0 scale = 3.0, 0.3, 3.0 rotation = 0, 0, 0 } MODEL { model = Squad/Parts/Utility/CircularIntake/model position = 0, 0.07, 0.6 scale = 0.3, 0.3, 0.3 rotation = 0, 0, 0 } // --- general parameters --- name = ElectricImpeller module = Part author = NovaSilisko+mkb // --- asset parameters --- mesh = model.mu scale = 1 node_attach = 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1 // --- FX definitions --- fx_exhaustFlame_white_tiny = 0.0, 1.0, 0.163, 0.0, 1.0, 0.0, running // --- Sound FX definition --- sound_vent_medium = disengage sound_rocket_mini = disengage sound_vent_soft = disengage // --- editor parameters --- TechRequired = electrics entryCost = 666 Cost =100 category = Propulsion subcategory = 0 title = Electric Impeller manufacturer = Robots Everywhere LLC description = A light, powerful radially mounted electric motor that can be used in any atmosphere, if power is available. Useless at high altitudes and in vacuum, but does not require oxygen. // attachment rules: stack, srfAttach, allowStack, allowSrfAttach, allowCollision attachRules = 0,1,0,0,0 // --- standard part parameters --- mass = 0.01 dragModelType = default maximum_drag = 0.3 minimum_drag = 0.2 angularDrag = 2 crashTolerance = 7 maxTemp = 3600 MODULE { name = ModuleEngines thrustVectorTransformName = thrustTransform throttleLocked = False exhaustDamage = False allowShutdown = True ignitionThreshold = 0.01 minThrust = 0 maxThrust = 1.2 heatProduction = 0.1 fxOffset = 0.0, 0.0, -0.7 PROPELLANT { name = EIntakeAir ratio = 5.0 DrawGauge = True } PROPELLANT { name = ElectricCharge ratio = 12.0 DrawGauge = False } atmosphereCurve { key = 0 800 key = 0.1 1200 key = 0.5 1200 key = 1 1000 } } MODULE { name = ModuleResourceIntake resourceName = EIntakeAir checkForOxygen = false area = 0.0000015 IntakeSpeed = 1 IntakeTransformName = Intake } RESOURCE { name = EIntakeAir amount = 0.0 maxAmount = 0.00125 } }
-
If Randall is right, the universe is also probably littered with spent second and third stages.
-
http://emlia.org/pmwiki/pmwiki.php?n=WhatGoesUp.WhatGoesUp I wrote this a while ago, feel free to integrate. It only covers Kerbin/Mun/Minmus for now though.
-
Kethane mining. All in one VS separate task vehicles.
spiritplumber replied to Galane's topic in KSP1 Mods Discussions
I find it more efficient to have a "gas station" on the surface, it cuts down on the number of landings and dockings. On occasion I may use a refuel truck. -
[20.2] EVA Followers 0.12 (Open to a Good Home)
spiritplumber replied to Fel's topic in KSP1 Mod Releases
I have recompiled this. There was no source code change, so all credit goes to original author. It works now in 0.22. http://emlia.org/ksp_shared/eva_followers_022.dll -
+ [Part] [1.6] Davon supply mod v025
spiritplumber replied to PrivateFlip's topic in KSP1 Mod Releases
I edited it so that I only have to fill it once, but love this. Great idea, minimal overhead, worked the first time. -
kOS Scriptable Autopilot System 0.9
spiritplumber replied to KevinLaity's topic in KSP1 Mod Releases
Confirmed here too. Keep up the good work, still! -
I wish to add ammo (as a ship resource) to this. OK to do so?
-
KMP v0.1.5.1 [0.23] [alpha] [inactive]
spiritplumber replied to TehGimp666's topic in KSP1 Mod Releases
This is awesome! Will you be releaseing at least the plugin source? I would like to tweak a few things for use on lower-end machines. EDIT: Derp, it's right in the start post. Sorry. -
[0.22] Extraplanetary Launchpads Legacy Thread
spiritplumber replied to skykooler's topic in KSP1 Mod Releases
Thanks! I put the modified source in the thread.... -
[0.22] Extraplanetary Launchpads Legacy Thread
spiritplumber replied to skykooler's topic in KSP1 Mod Releases
If you do not wish to compile this yourself, I also linked a modified Launchpad.dll with the functionality. http://emlia.org/ksp_shared/Launchpad.dll I hope the creator of the mod is OK with this as a temporary solution. -
Science leads to wonderful things (PIC HEAVY)
spiritplumber replied to Dave Kerbin's topic in KSP1 Mission Reports
This thread is good and you should feel good. -
MechJeb 2 - Patch test bed release (October 10)
spiritplumber replied to sarbian's topic in KSP1 Mod Releases
You know what would be awesome -- being able to make parts that only have sections of mechjeb enable. Like, just the smartASS, or just the orbital info, etc. -
[0.22] Extraplanetary Launchpads Legacy Thread
spiritplumber replied to skykooler's topic in KSP1 Mod Releases
That is the case. I am posting the modified dll. I am hoping skykooler will add this to the mod. It seems to work well. Replace the original dll with mine, and LaunchPadEx parts will function in orbit. However, there is a bug: you will have to "switch to" another spacecraft before having control. http://emlia.org/ksp_shared/Launchpad.dll -
The Rescue Mission That Should Never Have Worked
spiritplumber replied to MauveGnome's topic in KSP1 Mission Reports
This is awesome. You can also use landing legs as claws, to some extent. -
[0.22] Extraplanetary Launchpads Legacy Thread
spiritplumber replied to skykooler's topic in KSP1 Mod Releases
This works more reliably. using System; using System.Collections.Generic; using System.Linq; //using System.IO; // needed for Path manipulation //using Uri; using UnityEngine; using KSP.IO; namespace ExLP { // edited by spiritplumber to add orbital launchpads public class ExLaunchPad : PartModule { [KSPField] public bool debug = false; //public static bool kethane_present = CheckForKethane(); public static bool kethane_present; public enum crafttype { SPH, VAB }; public class Styles { public static GUIStyle normal; public static GUIStyle red; public static GUIStyle yellow; public static GUIStyle green; public static GUIStyle white; public static GUIStyle label; public static GUIStyle slider; public static GUIStyle sliderText; private static bool initialized; public static void Init() { if (initialized) return; initialized = true; normal = new GUIStyle(GUI.skin.button); normal.normal.textColor = normal.focused.textColor = Color.white; normal.hover.textColor = normal.active.textColor = Color.yellow; normal.onNormal.textColor = normal.onFocused.textColor = normal.onHover.textColor = normal.onActive.textColor = Color.green; normal.padding = new RectOffset(8, 8, 8, 8); red = new GUIStyle(GUI.skin.box); red.padding = new RectOffset(8, 8, 8, 8); red.normal.textColor = red.focused.textColor = Color.red; yellow = new GUIStyle(GUI.skin.box); yellow.padding = new RectOffset(8, 8, 8, 8); yellow.normal.textColor = yellow.focused.textColor = Color.yellow; green = new GUIStyle(GUI.skin.box); green.padding = new RectOffset(8, 8, 8, 8); green.normal.textColor = green.focused.textColor = Color.green; white = new GUIStyle(GUI.skin.box); white.padding = new RectOffset(8, 8, 8, 8); white.normal.textColor = white.focused.textColor = Color.white; label = new GUIStyle(GUI.skin.label); label.normal.textColor = label.focused.textColor = Color.white; label.alignment = TextAnchor.MiddleCenter; slider = new GUIStyle(GUI.skin.horizontalSlider); slider.margin = new RectOffset(0, 0, 0, 0); sliderText = new GUIStyle(GUI.skin.label); sliderText.alignment = TextAnchor.MiddleCenter; sliderText.margin = new RectOffset(0, 0, 0, 0); } } public class UIStatus { public Rect windowpos; public bool builduiactive = false; // Whether the build menu is open or closed public bool builduivisible = true; // Whether the build menu is allowed to be shown public bool showbuilduionload = false; public bool init = true; public bool linklfosliders = true; public bool showvab = true; public bool showsph = false; public bool canbuildcraft = false; public crafttype ct = crafttype.VAB; public string craftfile = null; public string flagname = null; public CraftBrowser craftlist = null; public bool showcraftbrowser = false; public ConfigNode craftnode = null; public bool craftselected = false; public Vector2 resscroll; public Dictionary<string, double> requiredresources = null; public double hullRocketParts = 0.0; public Dictionary<string, float> resourcesliders = new Dictionary<string, float>(); public float timer = 0.0f; public Vessel launchee, launcher; public Orbit currentorbit; } int padPartsCount; // the number of parts in the pad vessel (for docking detection) VesselResources padResources; // resources available to the pad [KSPField(isPersistant = false)] public float SpawnHeightOffset = 1.0f; // amount of pad between origin and open space private UIStatus uis = new UIStatus(); //private List<Vessel> bases; private static bool CheckForKethane() { if (AssemblyLoader.loadedAssemblies.Any(a => a.assembly.GetName().Name == "MMI_Kethane")) { Debug.Log("[EL] Kethane found"); return true; } Debug.Log("[EL] Kethane not found"); return false; } // ===================================================================================================================================================== // UI Functions private void UseResources(Vessel craft) { VesselResources craftResources = new VesselResources(craft); // Remove all resources that we might later fill (hull resources will not be touched) HashSet<string> resources_to_remove = new HashSet<string>(uis.requiredresources.Keys); craftResources.RemoveAllResources(resources_to_remove); // remove rocket parts required for the hull and solid fuel padResources.TransferResource("RocketParts", -uis.hullRocketParts); // use resources foreach (KeyValuePair<string, double> pair in uis.requiredresources) { // If resource is "JetFuel", rename to "LiquidFuel" string res = pair.Key; if (pair.Key == "JetFuel") { res = "LiquidFuel"; if (pair.Value == 0) continue; } if (!uis.resourcesliders.ContainsKey(pair.Key)) { Debug.Log(String.Format("[EL] missing slider {0}", pair.Key)); continue; } // Calculate resource cost based on slider position - note use pair.Key NOT res! we need to use the position of the dedicated LF slider not the LF component of LFO slider double tot = pair.Value * uis.resourcesliders[pair.Key]; // Transfer the resource from the vessel doing the building to the vessel being built padResources.TransferResource(res, -tot); craftResources.TransferResource(res, tot); } } private void FixCraftLock() { Double localepoch = uis.currentorbit.epoch; Double localdist = -0.1; //uis.launcher.state = Vessel.State.ACTIVE; FlightGlobals.ForceSetActiveVessel(uis.launcher); if (uis.launcher.situation == Vessel.Situations.ORBITING) { WarpShip(uis.launcher, uis.currentorbit); uis.currentorbit.epoch = localepoch + localdist; WarpShip(uis.launchee, uis.currentorbit); uis.currentorbit.epoch = localepoch; } if (uis.launcher.situation == Vessel.Situations.SUB_ORBITAL) { uis.currentorbit.epoch = localepoch; WarpShip(uis.launcher, uis.currentorbit); } if (uis.launchee.situation == Vessel.Situations.SUB_ORBITAL) { uis.currentorbit.epoch = localepoch + localdist; WarpShip(uis.launchee, uis.currentorbit); uis.currentorbit.epoch = localepoch; } //PopupDialog.SpawnPopupDialog("launcheE", " " + uis.launchee.orbit.inclination + " " + uis.launchee.orbit.altitude + " " + uis.launchee.orbit.argumentOfPeriapsis + " " + uis.launchee.orbit.eccentricity + " ", "OK", false, HighLogic.Skin); //PopupDialog.SpawnPopupDialog("launcheR", " " + uis.launcher.orbit.inclination + " " + uis.launcher.orbit.altitude + " " + uis.launcher.orbit.argumentOfPeriapsis + " " + uis.launcher.orbit.eccentricity + " ", "OK", false, HighLogic.Skin); if ((uis.launchee.situation == Vessel.Situations.SUB_ORBITAL) || (uis.launcher.situation == Vessel.Situations.SUB_ORBITAL)) { uis.timer = 1.0f; // try again } else { uis.currentorbit.epoch = localepoch; // Many thanks to Snjo (firespitter) uis.launchee.situation = uis.launcher.situation; //uis.launchee.state = Vessel.State.ACTIVE; uis.launchee.Landed = false; uis.launchee.Splashed = false; uis.launchee.GoOnRails(); uis.launchee.rigidbody.WakeUp(); uis.launchee.ResumeStaging(); uis.launchee.landedAt = "External Launchpad"; //uis.launchee.state = Vessel.State.ACTIVE; InputLockManager.ClearControlLocks(); PopupDialog.SpawnPopupDialog("Off-Kerbin Construction", "The vessel has been constructed successfully and is now " + situationdesc(uis.launchee.situation) + ".", "OK", false, HighLogic.Skin); FlightGlobals.ForceSetActiveVessel(uis.launchee); uis.timer = 9001.0f; // done } } public static void ErrorPopup(string message) { PopupDialog.SpawnPopupDialog("Error", message, "Close", true, HighLogic.Skin); } private static void WarpShip(Vessel vessel, Orbit newOrbit) // from hyperedit { if (newOrbit.getRelativePositionAtUT(Planetarium.GetUniversalTime()).magnitude > newOrbit.referenceBody.sphereOfInfluence) { ErrorPopup("Destination position was above the sphere of influence"); return; } vessel.Landed = false; vessel.Splashed = false; vessel.landedAt = string.Empty; var parts = vessel.parts; if (parts != null) { var clamps = parts.Where(p => p.Modules != null && p.Modules.OfType<LaunchClamp>().Any()).ToList(); foreach (var clamp in clamps) clamp.Die(); } try { OrbitPhysicsManager.HoldVesselUnpack(60); } catch (NullReferenceException) { } foreach (var v in (FlightGlobals.fetch == null ? (IEnumerable<Vessel>)new[] { vessel } : FlightGlobals.Vessels).Where(v => v.packed == false)) v.GoOnRails(); HardsetOrbit(vessel.orbit, newOrbit); vessel.orbitDriver.pos = vessel.orbit.pos.xzy; vessel.orbitDriver.vel = vessel.orbit.vel; } private static void HardsetOrbit(Orbit orbit, Orbit newOrbit) // from hyperedit { orbit.inclination = newOrbit.inclination; orbit.eccentricity = newOrbit.eccentricity; orbit.semiMajorAxis = newOrbit.semiMajorAxis; orbit.LAN = newOrbit.LAN; orbit.argumentOfPeriapsis = newOrbit.argumentOfPeriapsis; orbit.meanAnomalyAtEpoch = newOrbit.meanAnomalyAtEpoch; orbit.epoch = newOrbit.epoch; orbit.referenceBody = newOrbit.referenceBody; orbit.Init(); orbit.UpdateFromUT(Planetarium.GetUniversalTime()); } private void BuildAndLaunchCraft() { // build craft ShipConstruct nship = ShipConstruction.LoadShip(uis.craftfile); Vector3 offset = Vector3.up * SpawnHeightOffset; Transform t = this.part.transform; string landedAt = "External Launchpad"; string flag = uis.flagname; Game state = FlightDriver.FlightStateCache; VesselCrewManifest crew = new VesselCrewManifest (); GameObject launchPos = new GameObject (); launchPos.transform.position = t.position; launchPos.transform.position += t.TransformDirection(offset); launchPos.transform.rotation = t.rotation; ShipConstruction.CreateBackup(nship); ShipConstruction.PutShipToGround(nship, launchPos.transform); Destroy(launchPos); ShipConstruction.AssembleForLaunch(nship, landedAt, flag, state, crew); Vessel vsl = FlightGlobals.Vessels[FlightGlobals.Vessels.Count - 1]; vsl.Landed = false; if (kethane_present && !debug) UseResources(vsl); Staging.beginFlight(); uis.timer = 3.0f; uis.launchee = vsl; uis.launcher = this.vessel; uis.currentorbit = new Orbit(); HardsetOrbit(uis.currentorbit,this.vessel.GetOrbit()); // PopupDialog.SpawnPopupDialog("DERP currentorbit", " " + uis.currentorbit.inclination + " " + uis.currentorbit.altitude + " " + uis.currentorbit.argumentOfPeriapsis + " " + uis.currentorbit.eccentricity + " ", "OK", false, HighLogic.Skin); } private float ResourceLine(string label, string resourceName, float fraction, double minAmount, double maxAmount, double available) { GUILayout.BeginHorizontal(); // Resource name GUILayout.Box(label, Styles.white, GUILayout.Width(120), GUILayout.Height(40)); // Fill amount GUILayout.BeginVertical(); GUILayout.FlexibleSpace(); // limit slider to 0.5% increments if (minAmount == maxAmount) { GUILayout.Box("Must be 100%", GUILayout.Width(300), GUILayout.Height(20)); fraction = 1.0F; } else { fraction = (float)Math.Round(GUILayout.HorizontalSlider(fraction, 0.0F, 1.0F, Styles.slider, GUI.skin.horizontalSliderThumb, GUILayout.Width(300), GUILayout.Height(20)), 3); fraction = (Mathf.Floor(fraction * 200)) / 200; GUILayout.Box((fraction * 100).ToString() + "%", Styles.sliderText, GUILayout.Width(300), GUILayout.Height(20)); } GUILayout.FlexibleSpace(); GUILayout.EndVertical(); double required = minAmount * (1 - fraction) + maxAmount * fraction; // Calculate if we have enough resources to build GUIStyle requiredStyle = Styles.green; if (available < required) { requiredStyle = Styles.red; // prevent building unless debug mode is on, or kethane is not // installed (kethane is required for resource production) uis.canbuildcraft = (!kethane_present || debug); } // Required and Available GUILayout.Box((Math.Round(required, 2)).ToString(), requiredStyle, GUILayout.Width(75), GUILayout.Height(40)); GUILayout.Box((Math.Round(available, 2)).ToString(), Styles.white, GUILayout.Width(75), GUILayout.Height(40)); // Flexi space to make sure any unused space is at the right-hand edge GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); return fraction; } private void WindowGUI(int windowID) { Styles.Init(); /* * ToDo: * can extend FileBrowser class to see currently highlighted file? * rslashphish says: public myclass(arg1, arg2) : base(arg1, arg2); * KSPUtil.ApplicationRootPath - gets KSPO root * expose m_files and m_selectedFile? * fileBrowser = new FileBrowser(new Rect(Screen.width / 2, 100, 350, 500), title, callback, true); * * Style declarations messy - how do I dupe them easily? */ if (uis.init) { uis.init = false; } EditorLogic editor = EditorLogic.fetch; if (editor) return; if (!uis.builduiactive) return; if (padResources != null && padPartsCount != vessel.Parts.Count) { // something docked or undocked, so rebuild the pad's resouces info padResources = null; } if (padResources == null) { padPartsCount = vessel.Parts.Count; padResources = new VesselResources(vessel); } GUILayout.BeginVertical(); GUILayout.BeginHorizontal("box"); GUILayout.FlexibleSpace(); // VAB / SPH selection if (GUILayout.Toggle(uis.showvab, "VAB", GUILayout.Width(80))) { uis.showvab = true; uis.showsph = false; uis.ct = crafttype.VAB; } if (GUILayout.Toggle(uis.showsph, "SPH")) { uis.showvab = false; uis.showsph = true; uis.ct = crafttype.SPH; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); string strpath = HighLogic.SaveFolder; if (GUILayout.Button("Select Craft", Styles.normal, GUILayout.ExpandWidth(true))) { //GUILayout.Button is "true" when clicked uis.craftlist = new CraftBrowser(new Rect(Screen.width / 2, 100, 350, 500), uis.ct.ToString(), strpath, "Select a ship to load", craftSelectComplete, craftSelectCancel, HighLogic.Skin, EditorLogic.ShipFileImage, true); uis.showcraftbrowser = true; } if (uis.craftselected) { GUILayout.Box("Selected Craft: " + uis.craftnode.GetValue("ship"), Styles.white); // Resource requirements GUILayout.Label("Resources required to build:", Styles.label, GUILayout.Width(600)); // Link LFO toggle uis.linklfosliders = GUILayout.Toggle(uis.linklfosliders, "Link RocketFuel sliders for LiquidFuel and Oxidizer"); uis.resscroll = GUILayout.BeginScrollView(uis.resscroll, GUILayout.Width(600), GUILayout.Height(300)); GUILayout.BeginHorizontal(); // Headings GUILayout.Label("Resource", Styles.label, GUILayout.Width(120)); GUILayout.Label("Fill Percentage", Styles.label, GUILayout.Width(300)); GUILayout.Label("Required", Styles.label, GUILayout.Width(75)); GUILayout.Label("Available", Styles.label, GUILayout.Width(75)); GUILayout.EndHorizontal(); uis.canbuildcraft = true; // default to can build - if something is stopping us from building, we will set to false later if (!uis.requiredresources.ContainsKey("RocketParts")) { // if the craft to be built has no rocket parts storage, then the amount to use is not adjustable string resname = "RocketParts"; double available = padResources.ResourceAmount(resname); ResourceLine(resname, resname, 1.0F, uis.hullRocketParts, uis.hullRocketParts, available); } // Cycle through required resources foreach (KeyValuePair<string, double> pair in uis.requiredresources) { string resname = pair.Key; // Holds REAL resource name. May need to translate from "JetFuel" back to "LiquidFuel" string reslabel = resname; // Resource name for DISPLAY purposes only. Internally the app uses pair.Key if (reslabel == "JetFuel") { if (pair.Value == 0f) { // Do not show JetFuel line if not being used continue; } //resname = "JetFuel"; resname = "LiquidFuel"; } if (!uis.resourcesliders.ContainsKey(pair.Key)) { uis.resourcesliders.Add(pair.Key, 1); } // If in link LFO sliders mode, rename Oxidizer to LFO (Oxidizer) and LiquidFuel to LFO (LiquidFuel) if (reslabel == "Oxidizer") { reslabel = "RocketFuel (Ox)"; } if (reslabel == "LiquidFuel") { reslabel = "RocketFuel (LF)"; } double minAmount = 0.0; double maxAmount = uis.requiredresources[resname]; if (resname == "RocketParts") { minAmount += uis.hullRocketParts; maxAmount += uis.hullRocketParts; } double available = padResources.ResourceAmount(resname); // If LFO LiquidFuel exists and we are on LiquidFuel (Non-LFO), then subtract the amount used by LFO(LiquidFuel) from the available amount if (pair.Key == "JetFuel") { available -= uis.requiredresources["LiquidFuel"] * uis.resourcesliders["LiquidFuel"]; if (available < 0.0) available = 0.0; } uis.resourcesliders[pair.Key] = ResourceLine(reslabel, pair.Key, uis.resourcesliders[pair.Key], minAmount, maxAmount, available); if (uis.linklfosliders) { float tmp = uis.resourcesliders[pair.Key]; if (pair.Key == "Oxidizer") { uis.resourcesliders["LiquidFuel"] = tmp; } else if (pair.Key == "LiquidFuel") { uis.resourcesliders["Oxidizer"] = tmp; } } } GUILayout.EndScrollView(); // Build button if (uis.canbuildcraft) { if (GUILayout.Button("Build", Styles.normal, GUILayout.ExpandWidth(true))) { BuildAndLaunchCraft(); // Reset the UI uis.craftselected = false; uis.requiredresources = null; uis.resourcesliders = new Dictionary<string, float>();; // Close the UI HideBuildMenu(); } } else { GUILayout.Box("You do not have the resources to build this craft", Styles.red); } } else { GUILayout.Box("You must select a craft before you can build", Styles.red); } GUILayout.EndVertical(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); if (GUILayout.Button("Close")) { HideBuildMenu(); } uis.showbuilduionload = GUILayout.Toggle(uis.showbuilduionload, "Show on StartUp"); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); //DragWindow makes the window draggable. The Rect specifies which part of the window it can by dragged by, and is //clipped to the actual boundary of the window. You can also pass no argument at all and then the window can by //dragged by any part of it. Make sure the DragWindow command is AFTER all your other GUI input stuff, or else //it may "cover up" your controls and make them stop responding to the mouse. GUI.DragWindow(new Rect(0, 0, 10000, 20)); } // called when the user selects a craft the craft browser private void craftSelectComplete(string filename, string flagname) { uis.showcraftbrowser = false; uis.craftfile = filename; uis.flagname = flagname; uis.craftnode = ConfigNode.Load(filename); ConfigNode[] nodes = uis.craftnode.GetNodes("PART"); // Get list of resources required to build vessel if ((uis.requiredresources = getBuildCost(nodes)) != null) uis.craftselected = true; } // called when the user clicks cancel in the craft browser private void craftSelectCancel() { uis.showcraftbrowser = false; uis.requiredresources = null; uis.craftselected = false; } // ===================================================================================================================================================== // Event Hooks // See http://docs.unity3d.com/Documentation/Manual/ExecutionOrder.html for some help on what fires when // Called each time the GUI is painted private void drawGUI() { GUI.skin = HighLogic.Skin; uis.windowpos = GUILayout.Window(1, uis.windowpos, WindowGUI, "Extraplanetary Launchpad: "+situationdesc(uis.launcher.situation), GUILayout.Width(600)); } // Called ONCE at start private void Start() { // If "Show GUI on StartUp" ticked, show the GUI if (uis.showbuilduionload) { ShowBuildMenu(); } } // Fired maybe multiple times a frame, maybe once every n frames public override void OnFixedUpdate() { // ToDo: Should not be checking this every frame - once per craft switch // OnVesselChange may be ideal but I cannot seem to get it to fire // Landed / Flying check should probably be with this code, but moved it elsewhere while this is firing so often this.Update(); Update(); // Does the UI want to be visible? if (uis.builduiactive) { // Decide if the build menu is allowed to be visible if (this.vessel == FlightGlobals.ActiveVessel) { // Yes - check if it is currently not visible if (!uis.builduivisible) { // Going from invisible to visible uis.builduivisible = true; RenderingManager.AddToPostDrawQueue(3, new Callback(drawGUI)); //start the GUI } } else { // No - check if it is currently visible if (uis.builduivisible) { // Going from visible to invisible uis.builduivisible = false; RenderingManager.RemoveFromPostDrawQueue(3, new Callback(drawGUI)); //stop the GUI } } } } /* // Called when you change vessel // ToDo: Cannot seem to get this code to fire... private void OnVesselChange() { if (this.vessel == FlightGlobals.ActiveVessel) { ShowBuildMenu(); } else { HideBuildMenu(); } } */ public void Update() { if (uis.timer < 1000.0f) { uis.timer -= Time.deltaTime; if (uis.timer <= 0) { FixCraftLock(); } } } // Fired ONCE per frame public override void OnUpdate() { // Update state of context buttons depending on state of UI // ToDo: Move to something fired when the GUI is updated? Events["ShowBuildMenu"].active = !uis.builduiactive; Events["HideBuildMenu"].active = uis.builduiactive; this.Update(); Update(); } // Fired multiple times per frame in response to GUI events private void OnGUI() { if (uis.showcraftbrowser) { uis.craftlist.OnGUI(); } this.Update(); Update(); } /* // ToDo: What Does this Do? private void OnLoad() { bases = FlightGlobals.fetch.vessels; foreach (Vessel v in bases) { print(v.name); } } */ // Fired when KSP saves public override void OnSave(ConfigNode node) { PluginConfiguration config = PluginConfiguration.CreateForType<ExLaunchPad>(); config.SetValue("Window Position", uis.windowpos); config.SetValue("Show Build Menu on StartUp", uis.showbuilduionload); config.save(); } // Fired when KSP loads public override void OnLoad(ConfigNode node) { kethane_present = CheckForKethane(); LoadConfigFile(); } private void LoadConfigFile() { PluginConfiguration config = PluginConfiguration.CreateForType<ExLaunchPad>(); config.load(); uis.windowpos = config.GetValue<Rect>("Window Position"); uis.showbuilduionload = config.GetValue<bool>("Show Build Menu on StartUp"); } // ===================================================================================================================================================== // Flight UI and Action Group Hooks [KSPEvent(guiActive = true, guiName = "Show Build Menu", active = true)] public void ShowBuildMenu() { // Only allow enabling the menu if we are in a suitable place if (((this.vessel.situation == Vessel.Situations.LANDED) || (this.vessel.situation == Vessel.Situations.ORBITING) || (this.vessel.situation == Vessel.Situations.PRELAUNCH) || (this.vessel.situation == Vessel.Situations.SPLASHED))) { uis.launcher = this.vessel; RenderingManager.AddToPostDrawQueue(3, new Callback(drawGUI)); //start the GUI uis.builduiactive = true; } else { PopupDialog.SpawnPopupDialog("Sorry", "Can't build due to not being landed, splashed, or in a stable orbit\n\nCurrent state: " + situationdesc(this.vessel.situation), "OK", false, HighLogic.Skin); } } public String situationdesc(Vessel.Situations i) { switch (i) { case Vessel.Situations.DOCKED: return "Docked"; case Vessel.Situations.ESCAPING: return "Escaping"; case Vessel.Situations.FLYING: return "Flying"; case Vessel.Situations.LANDED: return "Landed"; case Vessel.Situations.ORBITING: return "Orbiting"; case Vessel.Situations.PRELAUNCH: return "Prelaunch"; case Vessel.Situations.SPLASHED: return "Splashed"; case Vessel.Situations.SUB_ORBITAL: return "Suborbital"; } return "Not sure"; } [KSPEvent(guiActive = true, guiName = "Hide Build Menu", active = false)] public void HideBuildMenu() { RenderingManager.RemoveFromPostDrawQueue(3, new Callback(drawGUI)); //stop the GUI uis.builduiactive = false; } [KSPAction("Show Build Menu")] public void EnableBuildMenuAction(KSPActionParam param) { ShowBuildMenu(); } [KSPAction("Hide Build Menu")] public void DisableBuildMenuAction(KSPActionParam param) { HideBuildMenu(); } [KSPAction("Toggle Build Menu")] public void ToggleBuildMenuAction(KSPActionParam param) { if (uis.builduiactive) { HideBuildMenu(); } else { ShowBuildMenu(); } } // ===================================================================================================================================================== // Build Helper Functions private void MissingPopup(Dictionary<string, bool> missing_parts) { string text = ""; foreach (string mp in missing_parts.Keys) text += mp + "\n"; int ind = uis.craftfile.LastIndexOf("/") + 1; string craft = uis.craftfile.Substring (ind); craft = craft.Remove (craft.LastIndexOf(".")); PopupDialog.SpawnPopupDialog("Sorry", "Can't build " + craft + " due to the following missing parts\n\n" + text, "OK", false, HighLogic.Skin); } public Dictionary<string, double> getBuildCost(ConfigNode[] nodes) { float mass = 0.0f; Dictionary<string, double> resources = new Dictionary<string, double>(); Dictionary<string, double> hull_resources = new Dictionary<string, double>(); Dictionary<string, bool> missing_parts = new Dictionary<string, bool>(); foreach (ConfigNode node in nodes) { string part_name = node.GetValue("part"); part_name = part_name.Remove(part_name.LastIndexOf("_")); AvailablePart ap = PartLoader.getPartInfoByName(part_name); if (ap == null) { missing_parts[part_name] = true; continue; } Part p = ap.partPrefab; mass += p.mass; foreach (PartResource r in p.Resources) { if (r.resourceName == "IntakeAir" || r.resourceName == "KIntakeAir") { // Ignore intake Air continue; } Dictionary<string, double> res_dict = resources; PartResourceDefinition res_def; res_def = PartResourceLibrary.Instance.GetDefinition(r.resourceName); if (res_def.resourceTransferMode == ResourceTransferMode.NONE || res_def.resourceFlowMode == ResourceFlowMode.NO_FLOW) { res_dict = hull_resources; } if (!res_dict.ContainsKey(r.resourceName)) { res_dict[r.resourceName] = 0.0; } res_dict[r.resourceName] += r.maxAmount; } } if (missing_parts.Count > 0) { MissingPopup(missing_parts); return null; } // RocketParts for the hull is a separate entity to RocketParts in // storage containers PartResourceDefinition rp_def; rp_def = PartResourceLibrary.Instance.GetDefinition("RocketParts"); uis.hullRocketParts = mass / rp_def.density; // If non pumpable resources are used, convert to RocketParts foreach (KeyValuePair<string, double> pair in hull_resources) { PartResourceDefinition res_def; res_def = PartResourceLibrary.Instance.GetDefinition(pair.Key); double hull_mass = pair.Value * res_def.density; double hull_parts = hull_mass / rp_def.density; uis.hullRocketParts += hull_parts; } // If there is JetFuel (ie LF only tanks as well as LFO tanks - eg a SpacePlane) then split the Surplus LF off as "JetFuel" if (resources.ContainsKey("Oxidizer") && resources.ContainsKey("LiquidFuel")) { double jetFuel = 0.0; // The LiquidFuel:Oxidizer ratio is 9:11. Try to minimize rounding effects. jetFuel = (11 * resources["LiquidFuel"] - 9 * resources["Oxidizer"]) / 11; if (jetFuel < 0.01) { // Forget it. not getting far on that. Any discrepency this // small will be due to precision losses. jetFuel = 0.0; } resources["LiquidFuel"] -= jetFuel; resources["JetFuel"] = jetFuel; } return resources; } } public class Recycler : PartModule { double busyTime; bool recyclerActive; [KSPField] public float RecycleRate = 1.0f; [KSPField (guiName = "State", guiActive = true)] public string status; public void OnTriggerStay(Collider col) { if (!recyclerActive || Planetarium.GetUniversalTime() <= busyTime || !col.CompareTag("Untagged") || col.gameObject.name == "MapOverlay collider") // kethane return; Part p = col.attachedRigidbody.GetComponent<Part>(); Debug.Log(String.Format("[EL] {0}", p)); if (p != null && p.vessel != null && p.vessel != vessel) { float mass; if (p.vessel.isEVA) { mass = RecycleKerbal(p.vessel); } else { mass = RecycleVessel(p.vessel); } busyTime = Planetarium.GetUniversalTime() + mass / RecycleRate; } } private float ReclaimResource(string resource, double amount, string vessel_name, string name=null) { PartResourceDefinition res_def; res_def = PartResourceLibrary.Instance.GetDefinition(resource); VesselResources recycler = new VesselResources(vessel); if (res_def == null) { return 0; } if (name == null) { name = resource; } double remain = amount; // any resources that can't be pumped or don't flow just "evaporate" // FIXME: should this be a little smarter and convert certain such // resources into rocket parts? if (res_def.resourceTransferMode != ResourceTransferMode.NONE && res_def.resourceFlowMode != ResourceFlowMode.NO_FLOW) { remain = recycler.TransferResource(resource, amount); } Debug.Log(String.Format("[EL] {0}-{1}: {2} taken {3} reclaimed, {4} lost", vessel_name, name, amount, amount - remain, remain)); return (float) (amount * res_def.density); } public float RecycleKerbal(Vessel v) { if (!v.isEVA) return 0; // idea and numbers taken from Kethane if (v.GetVesselCrew()[0].isBadass) { v.rootPart.explosionPotential = 10000; } FlightGlobals.ForceSetActiveVessel(this.vessel); v.rootPart.explode(); float mass = 0; mass += ReclaimResource("Kethane", 150, v.name); mass += ReclaimResource("Metal", 1, v.name); return mass; } public float RecycleVessel(Vessel v) { float ConversionEfficiency = 0.8f; double amount; VesselResources scrap = new VesselResources(v); PartResourceDefinition rp_def; rp_def = PartResourceLibrary.Instance.GetDefinition("RocketParts"); float mass = 0; foreach (string resource in scrap.resources.Keys) { amount = scrap.ResourceAmount (resource); mass += ReclaimResource(resource, amount, v.name); } float hull_mass = v.GetTotalMass(); amount = hull_mass * ConversionEfficiency / rp_def.density; mass += ReclaimResource("RocketParts", amount, v.name, "hull"); v.Die(); return mass; } [KSPEvent(guiActive = true, guiName = "Activate Recycler", active = true)] public void Activate() { recyclerActive = true; Events["Activate"].active = false; Events["Deactivate"].active = true; } [KSPEvent(guiActive = true, guiName = "Deactivate Recycler", active = false)] public void Deactivate() { recyclerActive = false; Events["Activate"].active = true; Events["Deactivate"].active = false; } public override void OnLoad(ConfigNode node) { Deactivate(); } public override void OnUpdate() { if (Planetarium.GetUniversalTime() <= busyTime) { status = "Busy"; } else if (recyclerActive) { status = "Active"; } else { status = "Inactive"; } } } }