Jump to content

Need some help creating new vessel


Recommended Posts

I'm trying to create a vessel from a part, and have it created about 10-20m in the air, and about 10m away from the current vessel.

  • I have it creating the vessel, but with problems:
  • Modules aren't initialized
  • Additional vessels created will always be created at the same location, even if the current active vessel moves away
  • The new vessel gets created where I expect, but then (maybe when physics kicks in) instantly is on the ground

I've been looking at a number of mods which create vessels, and have so far been unable to figure this out.

Pinging @nightingale @RoverDude @taniwha since I've been looking at your mods for help.

Thanks in advance

 

The code is here:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using KSP;
using System.Collections;

namespace KSPGeoCaching
{
    public static class LocationUtil
    {
        public static double TerrainHeight(double latitude, double longitude, CelestialBody body)
        {
            // Sun and Jool - bodies without terrain
            if (body.pqsController == null)
            {
                return 0;
            }

            // Figure out the terrain height
            double latRads = Math.PI / 180.0 * latitude;
            double lonRads = Math.PI / 180.0 * longitude;
            Vector3d radialVector = new Vector3d(Math.Cos(latRads) * Math.Cos(lonRads), Math.Sin(latRads), Math.Cos(latRads) * Math.Sin(lonRads));
            return Math.Max(body.pqsController.GetSurfaceHeight(radialVector) - body.pqsController.radius, 0.0);
        }
    }

    partial class GeoCacheDriver : Monobehaviour
    {
        const string VesselName = "GeoCache";

        internal class KD
        {
            internal double altitude;
            public double latitude = 0.0;
            public double longitude = 0.0;
            public CelestialBody body = null;
            public Orbit orbit = null;
            public bool landed = false;
            //public float heading;
            public AvailablePart craftPart;

            public KD()
            {
                latitude = FlightGlobals.ActiveVessel.latitude;
                longitude = FlightGlobals.ActiveVessel.longitude;
                body = FlightGlobals.ActiveVessel.mainBody;
                orbit = FlightGlobals.ActiveVessel.orbit;
                landed = FlightGlobals.ActiveVessel.Landed;
                altitude = FlightGlobals.ActiveVessel.altitude; // LocationUtil.TerrainHeight(latitude, longitude, body);
                // heading = FlightGlobals.ActiveVessel.head
                Log.Info("Creation Latitude: " + latitude + ",   Longitude: " + longitude);
               
            }
        }

        internal void Spawn()
        {
            KD kd = new KD();
            Log.Info("Spawning a GeoCache" );

            // Set additional info for landed 
            if (kd.landed)
            {
                Vector3d pos = kd.body.GetWorldSurfacePosition(kd.latitude, kd.longitude, kd.altitude);

                kd.orbit = new Orbit(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, kd.body);
                kd.orbit.UpdateFromStateVectors(pos, kd.body.getRFrmVel(pos), kd.body, Planetarium.GetUniversalTime());
               
            }
            else
            {
                // Update the reference body in the orbit
                kd.orbit.referenceBody = kd.body;
            }

            uint flightId = ShipConstruction.GetUniqueFlightID(HighLogic.CurrentGame.flightState);


            kd.craftPart = PartLoader.getPartInfoByName("geocache");
            // Create part nodes
            ConfigNode[] partNodes = new ConfigNode[1];
            partNodes[0] = ProtoVessel.CreatePartNode(kd.craftPart.name, flightId, null);
            

            // Create additional nodes
            ConfigNode[] additionalNodes = new ConfigNode[0];
            // Create the config node representation of the ProtoVessel        
            ConfigNode protoVesselNode = ProtoVessel.CreateVesselNode(VesselName, VesselType.Probe, kd.orbit, 0, partNodes);

            // Additional seetings for a landed vessel
            if (kd.landed)
            {
                bool splashed = kd.altitude < 0.001 && kd.body.ocean;

                // Add a bit of height for landed
                if (!splashed)
                {
                    kd.altitude += 40.2;
                    Log.Info("Adding 40.2 to altitude");
                }
                //Guid vesselId = vessel.id;

                // Figure out the appropriate rotation
                Vector3d norm = kd.body.GetRelSurfaceNVector(kd.latitude, kd.longitude);
                double terrainHeight = 0.0;
                if (kd.body.pqsController != null)
                {
                    terrainHeight = kd.body.pqsController.GetSurfaceHeight(norm) - kd.body.pqsController.radius;
                }

                // Create the config node representation of the ProtoVessel

                protoVesselNode.SetValue("sit", (splashed ? Vessel.Situations.SPLASHED : Vessel.Situations.LANDED).ToString());
                protoVesselNode.SetValue("landed", (!splashed).ToString());
                protoVesselNode.SetValue("splashed", splashed.ToString());
                protoVesselNode.SetValue("lat", kd.latitude);
                protoVesselNode.SetValue("lon", kd.longitude);
                protoVesselNode.SetValue("alt", kd.altitude);                
                protoVesselNode.SetValue("landedAt", kd.body.name);

                float lowest = float.MaxValue;
                foreach (Collider collider in kd.craftPart.partPrefab.GetComponentsInChildren<Collider>())
                {
                    if (collider.gameObject.layer != 21 && collider.enabled)
                    {
                        lowest = Mathf.Min(lowest, collider.bounds.min.y);
                    }
                }
                if (Mathf.Approximately(lowest, float.MaxValue))
                {
                    lowest = 0;
                }

                float hgt = kd.craftPart.partPrefab.localRoot.attPos0.y - lowest;
                hgt += 10;
                protoVesselNode.SetValue("hgt", hgt);

                // Set the normal vector relative to the surface
                Quaternion normal = Quaternion.LookRotation(new Vector3((float)norm.x, (float)norm.y, (float)norm.z));
                Quaternion rotation = Quaternion.identity;
                rotation = rotation * Quaternion.FromToRotation(Vector3.up, Vector3.back);
                Vector3 nrm = (rotation * Vector3.forward);
                protoVesselNode.SetValue("prst", false.ToString());
                protoVesselNode.SetValue("nrm", nrm.x + "," + nrm.y + "," + nrm.z);
            }
            ProtoVessel protoVessel = HighLogic.CurrentGame.AddVessel(protoVesselNode);
            //protoVessel.Load(HighLogic.CurrentGame.flightState);

            protoVessel.vesselName = VesselName;
           
            vessel = protoVessel.vesselRef;
            if (vessel != null)
            {
                //var mode = OrbitDriver.UpdateMode.UPDATE;
                //vessel.orbitDriver.SetOrbitMode(mode);

                vessel.Load();
                launchPoint = vessel.GetWorldPos3D();
                // Offset the position by 10m in y and z to have it created away from the current vessel
                launchPoint.y += 10;
                launchPoint.z += 10;
                
                StartCoroutine("SpawnCoRoutine");
            }

        }
        Vessel vessel;
        Vector3 launchPoint;
        IEnumerator SpawnCoRoutine()
        {
            if ((vessel.Parts[0] != null) && (vessel.Parts[0].Rigidbody != null))
            {
                vessel.Parts[0].Rigidbody.isKinematic = true;
            }
            //vessel.GoOnRails();
            while ((vessel.packed) && (vessel.acceleration.magnitude == 0))
            {
                vessel.SetPosition(launchPoint);
                yield return null;
            }
            while (true)
            {
                bool partsInitialized = true;
                foreach (Part p in vessel.parts)
                {
                    if (!p.started)
                    {
                        partsInitialized = false;
                        break;
                    }
                }
                vessel.SetPosition(launchPoint, true);
                //vessel.SetWorldVelocity(Vector3d.up * 0);
                if (partsInitialized)
                {
                    break;
                }
                OrbitPhysicsManager.HoldVesselUnpack(2);
                yield return null;
            }
            vessel.GoOffRails();

            //flare.IgnoreGForces(250);
            Log.Info("Final Latitude: " + vessel.latitude + ",   Longitude: " + vessel.longitude);

        }
    }

}

 

Link to comment
Share on other sites

You might find it easier to create a .craft config node (or, if it's always the same part, a .craft file you can load into a config node), then use ShipConstruct to "build" the ship, something like PlaceShip in EL's SurveyStation.cs (to get position and orientation correct: the key points for you are the two rootPart.transform manipulation lines at the end), then use ShipConstruction.AssembleForLaunch to get the all the vessel and part modules running. There is some extra jiggling needed(? was in the past) to deal with the ship being put to ground correctly, which you'll find in BuildControl.BuildAndLaunchCraft (follow the builder.capture == false paths: capturing a built vessels is another barrel of worms). There's also some stuff in there for staging and current vessel switching.

Getting a spawned vessel up and running is a complex task and it is best to let KSP itself do as much of the work as possible.

I hope that helps. If you have more questions, feel free to ask.

Link to comment
Share on other sites

1 hour ago, Benjamin Kerman said:

I don't know if this will work for your case, but here's how I'm doing it:

(I'll ping @wasml, as he can probably speak better about his answer)

I started with that, but it has some issues.

I've rewritten what I have using code from an old mod KSP-Smelter.  It's working much better, the only thing that is still not working properly is this:

  • The new vessel gets created where I expect, but then (maybe when physics kicks in) instantly is on the ground

I want the new vessel to be created in the air and offset from the vessel

 

Link to comment
Share on other sites

The current working code (except for the positioning in the air) is here:

    partial class GeoCacheDriver
    {
        const string VesselName = "GeoCache";

        public class HideConfigNode
        {
            private ConfigNode node = null;
            public HideConfigNode(ConfigNode node)
            {
                this.SetConfigNode(node);
            }
            public HideConfigNode()
            {

            }
            public void SetConfigNode(ConfigNode node)
            {
                this.node = node;
            }
            public void SetValue(string name, string value)
            {
                node.SetValue(name, value);
            }
            public ConfigNode GetConfigNode()
            {
                return this.node;
            }
        }

        /// <summary>
        /// This class is pretty much built on what KAS uses to pull parts out of containers
        /// All credit to https://github.com/KospY/KAS
        /// https://github.com/KospY/KAS/blob/master/LICENSE.md
        /// </summary>
        public class VesselSpawner
        {
            const string PluginPath = "/GameData/KSPGeoCaching/PluginData/Ships/";

            private HideConfigNode _OldVabShip;
            private string _craftFile = "";
            private Part _srcPart = null;
            private Vector3 _spawnOffset = new Vector3(0, 0, 0);
            /// <summary>
            /// 
            /// </summary>
            /// <param name="_craftFile">Craft file name as it appears in the PluginPath folder</param>
            /// <param name="_srcPart">Source part to spawn relative to</param>
            /// <param name="_spawnOffset">Offset spawn from Source part position</param>
            public VesselSpawner(string _craftFile, Part _srcPart, Vector3 _spawnOffset)
            {
                //Store paths
                this._craftFile = _craftFile;
                this._srcPart = _srcPart;
                this._spawnOffset = _spawnOffset;

                if (this._srcPart == null)
                    Debug.Log("Relative source part can't be null");
                if ((_craftFile != String.Empty))
                    Debug.Log("Source part path can't be null");

            }
            /// <summary>
            /// Spawns the vessel
            /// </summary>
            public void SpawnVessel()
            {
                Log.Info("SpawnVessel");
                //Load craft file
                ShipConstruct _ship = LoadVessel(this._craftFile);
                if (_ship != null)
                    SpawnVessel(_ship, this._srcPart, this._spawnOffset);
                else
                    Debug.Log("Failed to load the vessel");
            }
            /// <summary>
            /// Attempt vessel load
            /// </summary>
            /// <param name="_craftFile"></param>
            /// <returns></returns>
            private ShipConstruct LoadVessel(string _craftFile)
            {
                //Get path to vessel
                string BasePath = Environment.CurrentDirectory + PluginPath;
                string path = BasePath + _craftFile;

                Log.Info("LoadVessel, full path: " + path);
                //Save old ship for later, else player will see it in the VAB
                _OldVabShip = new HideConfigNode(ShipConstruction.ShipConfig);

                //Load craft file
                ShipConstruct _shipConstruct = ShipConstruction.LoadShip(path);

                //Check load
                if (_shipConstruct == null)
                {
                    // Restore ShipConstruction ship, otherwise player sees loaded craft in VAB
                    ShipConstruction.ShipConfig = _OldVabShip.GetConfigNode();
                    Log.Info("vessel not loaded");
                    return null; //Fail
                }
                return _shipConstruct;
            }
            /// <summary>
            /// Spawn ship construct
            /// https://github.com/KospY/KAS/blob/master/Plugin/KAS_Shared.cs
            /// </summary>
            /// <param name="_shipConstruct">Shipconstruct to spawn</param>
            /// <param name="_srcPart">Source part to spawn relative to</param>
            /// <param name="_spawnOffset">Offset spawn from Source part position</param>
            private void SpawnVessel(ShipConstruct _shipConstruct, Part _srcPart, Vector3 _spawnOffset)
            {
                //Store construct root
                Part _newConstructRootPart = _shipConstruct.parts[0];

                //Center rootpart
                Vector3 offset = _newConstructRootPart.transform.localPosition;
                _newConstructRootPart.transform.Translate(-offset);

                //Get launch spawn point, relative to part
                Transform t = _srcPart.transform;
                GameObject launchPos = new GameObject();
                launchPos.transform.parent = _srcPart.transform;
                launchPos.transform.position = t.position;
                launchPos.transform.position += t.TransformDirection(_spawnOffset);
                launchPos.transform.rotation = t.rotation;
                //Store our launch / spawn position
                Transform launchTransform = launchPos.transform;
                //Kill original object
                launchPos.DestroyGameObject();
                //Set rootpart origin
                _shipConstruct.Parts[0].localRoot.transform.Translate(launchPos.transform.position, Space.World);
                //Position
                float angle;
                Vector3 axis;
                //Extract ToAngleAxis data from selected spawning location
                launchTransform.rotation.ToAngleAxis(out angle, out axis);
                //TRANSFORM Rotate localRootPart in relation to root
                _shipConstruct.Parts[0].localRoot.transform.RotateAround(launchTransform.position, axis, angle);

                //Create vessel object
                Vessel _newVessel = _newConstructRootPart.localRoot.gameObject.AddComponent<Vessel>();
                //Attach vessel information
                _newVessel.id = Guid.NewGuid();
                _newVessel.vesselName = _srcPart.vessel.vesselName + " - " + _shipConstruct.shipName;
                _newVessel.landedAt = _srcPart.vessel.vesselName;

                //Store backup
                ShipConstruction.CreateBackup(_shipConstruct);

                //Init from VAB
                _newVessel.Initialize(true);
                //Set Landed
                _newVessel.Landed = true;

                //Set Orbit
                InitiateOrbit(launchTransform.position, _srcPart.vessel, _newVessel);

                //Set Mission info
                uint missionId = (uint)Guid.NewGuid().GetHashCode();
                string flagUrl = _srcPart.flagURL;
                uint launchId = HighLogic.CurrentGame.launchID++;

                //Set part mission info
                for (int i = 0; i < _newVessel.parts.Count; i++)
                {
                    Part part = _newVessel.parts[i];
                    part.flightID = ShipConstruction.GetUniqueFlightID(FlightDriver.FlightStateCache.flightState);
                    part.flagURL = flagUrl;
                    part.launchID = launchId;
                    part.missionID = missionId;
                }

                //Generate staging
                KSP.UI.Screens.StageManager.BeginFlight();
                _newConstructRootPart.vessel.ResumeStaging();
                KSP.UI.Screens.StageManager.GenerateStagingSequence(_newConstructRootPart.localRoot);
                KSP.UI.Screens.StageManager.RecalculateVesselStaging(_newConstructRootPart.vessel);

                //Set position, again
                _newVessel.SetPosition(launchTransform.position);
                _newVessel.SetRotation(launchTransform.rotation);

                ////Get allll the parts, does it even matter since we launch from VAB?
                //for (int i = 0; i < _newVessel.parts.Count; i++)
                //{
                //    // Solar panels from containers (we dont have no bloody containers here tho) don't work otherwise
                //    for (int j = 0; j < _newVessel.parts[i].Modules.Count; j++)
                //    {
                //        ConfigNode node = new ConfigNode();
                //        node.AddValue("name", _newVessel.parts[i].Modules[j].moduleName);
                //        _newVessel.parts[i].LoadModule(node, ref j);
                //    }
                //}


                //Save Protovessel
                ProtoVessel _newProto = new ProtoVessel(_newVessel);

                //Kill and remove spawned vessel, had some serious problems with spawn position warping/glitching
                _newVessel.Die();

                //Set the protovessels position to the relative one we found, maybe redundant
                _newProto.position = launchPos.transform.position;

                //If you check this value, you will see the height change from launch scene to resume scene, extra dafuq
                //float height = _newProto.height;

                if (FlightDriver.StartupBehaviour == FlightDriver.StartupBehaviours.RESUME_SAVED_FILE ||
                    FlightDriver.StartupBehaviour == FlightDriver.StartupBehaviours.RESUME_SAVED_CACHE)
                {
                    //Odd behaviour with positioning during different flight scenes, workaround awaaaay
                    _newProto.height = TrueAlt(launchTransform.position, _srcPart.vessel);
                }
                _newProto.altitude += 10;
                _newProto.height += 10;
                //Load Protovessel
                _newProto.Load(HighLogic.CurrentGame.flightState);

                // Restore ShipConstruction ship, otherwise player sees loaded craft in VAB
                ShipConstruction.ShipConfig = _OldVabShip.GetConfigNode();

                //Fix Control Lock
                FlightInputHandler.ResumeVesselCtrlState(FlightGlobals.ActiveVessel);
                //Fix active vessel staging
                FlightGlobals.ActiveVessel.ResumeStaging();

            }
            /// <summary>
            /// http://forum.kerbalspaceprogram.com/threads/111116-KSP-Altitude-Calculation-Inquiry
            /// </summary>
            /// <returns></returns>
            private float TrueAlt(Vector3 _LauncPos, Vessel _srcVessel)
            {
                //Vector3 pos = _srcPart.transform.position; //or this.vessel.GetWorldPos3D()
                float ASL = FlightGlobals.getAltitudeAtPos(_LauncPos);
                if (_srcVessel.mainBody.pqsController == null) { return ASL; }
                float terrainAlt = Convert.ToSingle(_srcVessel.pqsAltitude);
                if (_srcVessel.mainBody.ocean && _srcVessel.heightFromTerrain <= 0) { return ASL; } //Checks for oceans
                return ASL - terrainAlt;
            }
            /// <summary>
            /// https://github.com/taniwha-qf/Extraplanetary-Launchpads/blob/master/Source/BuildControl.cs
            /// https://github.com/taniwha-qf/Extraplanetary-Launchpads/blob/master/License.txt
            /// </summary>
            /// <param name="_newVessel"></param>
            /// <param name="_srcVessel"></param>
            private void InitiateOrbit(Vector3 _spawnPoint, Vessel _srcVessel, Vessel _newVessel)
            {
                var mode = OrbitDriver.UpdateMode.UPDATE;
                _newVessel.orbitDriver.SetOrbitMode(mode);

                var craftCoM = GetVesselWorldCoM(_newVessel);
                var vesselCoM = _spawnPoint;
                var offset = (Vector3d.zero + craftCoM - vesselCoM).xzy;

                var corb = _newVessel.orbit;
                var orb = _srcVessel.orbit;
                var UT = Planetarium.GetUniversalTime();
                var body = orb.referenceBody;
                corb.UpdateFromStateVectors(orb.pos + offset, orb.vel, body, UT);

                Debug.Log(String.Format("[EL] {0} {1}", "orb", orb.pos));
                Debug.Log(String.Format("[EL] {0} {1}", "corb", corb.pos));

            }
            public Vector3 GetVesselWorldCoM(Vessel v)
            {
                var com = v.CoM;
                return v.rootPart.partTransform.TransformPoint(com);
            }
        }


    }

@taniwha Any ideas?

Link to comment
Share on other sites

@linuxgurugamer: if everything else is working nicely, then I suspect you may be fighting physics itself. I know that sometime recently, JPLRepo worked on the ground-placement code in KSP to get vessel placement more reliable, but KSP has always tried to force landed vessels to ground, and unlanded vessels would get deleted if they were on rails (this caused some fun when getting captured spawning working in EL). It sounds to me like you are essentially trying to create a gravity-defying object (and gravity is the core game element in KSP).

Maybe a good question to ask: why do you want the new vessel to spawn in the air? If it is for some sort of display, you might want to look into EL's survey plaques (SurveyStake.cs). Then you could put a small vessel on the surface and have it show a plaque at an arbitrary height, or even an arbitrary 3d offset.

Another thought is a two-part vessel with a "cheaty" separation of the parts: one part is on the ground, the other is in the air supported by the ground part (since KSP's intra-vessel physics cares about only the joints, not the colliders).

Link to comment
Share on other sites

3 minutes ago, taniwha said:

@linuxgurugamer: if everything else is working nicely, then I suspect you may be fighting physics itself. I know that sometime recently, JPLRepo worked on the ground-placement code in KSP to get vessel placement more reliable, but KSP has always tried to force landed vessels to ground, and unlanded vessels would get deleted if they were on rails (this caused some fun when getting captured spawning working in EL). It sounds to me like you are essentially trying to create a gravity-defying object (and gravity is the core game element in KSP).

Maybe a good question to ask: why do you want the new vessel to spawn in the air? If it is for some sort of display, you might want to look into EL's survey plaques (SurveyStake.cs). Then you could put a small vessel on the surface and have it show a plaque at an arbitrary height, or even an arbitrary 3d offset.

Another thought is a two-part vessel with a "cheaty" separation of the parts: one part is on the ground, the other is in the air supported by the ground part (since KSP's intra-vessel physics cares about only the joints, not the colliders).

The basic idea is to allow the player to place a geocache part anywhere they want.  So putting it in the air was a first step.

I looked at the Vessel Mover code, and was able to incorporate it into my code, with minor modifications.  This actually does what I want, so I'll be moving ahead with this.

I learned a lot, thanks for your help

 

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...