Beispiel #1
0
        private void SpawnVessel(VesselData vesselData, List <ProtoCrewMember> crewData = null)
        {
            string gameDataDir = KSPUtil.ApplicationRootPath;

            Debug.Log("Spawning a vessel named '" + vesselData.name + "'");

            // Set additional info for landed vessels
            bool landed = false;

            if (!vesselData.orbiting)
            {
                landed = true;
                if (vesselData.altitude == null || vesselData.altitude < 0)
                {
                    vesselData.altitude = 35;//LocationUtil.TerrainHeight(vesselData.latitude, vesselData.longitude, vesselData.body);
                }

                //Vector3d pos = vesselData.body.GetWorldSurfacePosition(vesselData.latitude, vesselData.longitude, vesselData.altitude.Value);
                Vector3d pos = vesselData.body.GetRelSurfacePosition(vesselData.latitude, vesselData.longitude, vesselData.altitude.Value);

                vesselData.orbit = new Orbit(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, vesselData.body);
                vesselData.orbit.UpdateFromStateVectors(pos, vesselData.body.getRFrmVel(pos), vesselData.body, Planetarium.GetUniversalTime());
            }
            else
            {
                vesselData.orbit.referenceBody = vesselData.body;
            }

            ConfigNode[] partNodes;
            //UntrackedObjectClass sizeClass;
            ShipConstruct shipConstruct = null;
            bool          hasClamp      = false;
            float         lcHeight      = 0;
            ConfigNode    craftNode;
            Quaternion    craftRotation = Quaternion.identity;

            if (!string.IsNullOrEmpty(vesselData.craftURL))
            {
                // Save the current ShipConstruction ship, otherwise the player will see the spawned ship next time they enter the VAB!
                ConfigNode currentShip = ShipConstruction.ShipConfig;

                shipConstruct = ShipConstruction.LoadShip(vesselData.craftURL);
                if (shipConstruct == null)
                {
                    Debug.Log("ShipConstruct was null when tried to load '" + vesselData.craftURL +
                              "' (usually this means the file could not be found).");
                    return;//continue;
                }

                craftNode     = ConfigNode.Load(vesselData.craftURL);
                lcHeight      = ConfigNode.ParseVector3(craftNode.GetNode("PART").GetValue("pos")).y;
                craftRotation = ConfigNode.ParseQuaternion(craftNode.GetNode("PART").GetValue("rot"));

                // Restore ShipConstruction ship
                ShipConstruction.ShipConfig = currentShip;

                // Set the name
                if (string.IsNullOrEmpty(vesselData.name))
                {
                    vesselData.name = shipConstruct.shipName;
                }

                // Set some parameters that need to be at the part level
                uint missionID = (uint)Guid.NewGuid().GetHashCode();
                uint launchID  = HighLogic.CurrentGame.launchID++;
                foreach (Part p in shipConstruct.parts)
                {
                    p.flightID  = ShipConstruction.GetUniqueFlightID(HighLogic.CurrentGame.flightState);
                    p.missionID = missionID;
                    p.launchID  = launchID;
                    p.flagURL   = vesselData.flagURL ?? HighLogic.CurrentGame.flagURL;

                    // Had some issues with this being set to -1 for some ships - can't figure out
                    // why.  End result is the vessel exploding, so let's just set it to a positive
                    // value.
                    p.temperature = 1.0;
                }

                //add minimal crew
                //bool success = false;
                Part part = shipConstruct.parts.Find(p => p.protoModuleCrew.Count < p.CrewCapacity);

                // Add the crew member
                if (part != null && VesselMoverToolbar.addCrewMembers)
                {
                    if (VesselMoverToolbar.selectCrewMembers)
                    {
                        ProtoCrewMember crewMember = crewData.FirstOrDefault();
                        if (crewMember != null)
                        {
                            part.AddCrewmemberAt(crewMember, part.protoModuleCrew.Count);
                        }
                        VesselMoverToolbar.SelectedCrewMembers.Clear();
                    }
                    else
                    {
                        // Create the ProtoCrewMember
                        ProtoCrewMember crewMember = HighLogic.CurrentGame.CrewRoster.GetNewKerbal();
                        crewMember.gender = UnityEngine.Random.Range(0, 100) > 50
              ? ProtoCrewMember.Gender.Female
              : ProtoCrewMember.Gender.Male;
                        //crewMember.trait = "Pilot";

                        // Add them to the part
                        part.AddCrewmemberAt(crewMember, part.protoModuleCrew.Count);
                    }
                }

                // Create a dummy ProtoVessel, we will use this to dump the parts to a config node.
                // We can't use the config nodes from the .craft file, because they are in a
                // slightly different format than those required for a ProtoVessel (seriously
                // Squad?!?).
                ConfigNode  empty       = new ConfigNode();
                ProtoVessel dummyProto  = new ProtoVessel(empty, null);
                Vessel      dummyVessel = new Vessel();
                dummyVessel.parts    = shipConstruct.Parts;
                dummyProto.vesselRef = dummyVessel;

                // Create the ProtoPartSnapshot objects and then initialize them
                foreach (Part p in shipConstruct.parts)
                {
                    dummyVessel.loaded = false;
                    p.vessel           = dummyVessel;

                    dummyProto.protoPartSnapshots.Add(new ProtoPartSnapshot(p, dummyProto, true));
                }
                foreach (ProtoPartSnapshot p in dummyProto.protoPartSnapshots)
                {
                    p.storePartRefs();
                }

                // Create the ship's parts

                List <ConfigNode> partNodesL = new List <ConfigNode>();
                foreach (ProtoPartSnapshot snapShot in dummyProto.protoPartSnapshots)
                {
                    ConfigNode node = new ConfigNode("PART");
                    snapShot.Save(node);
                    partNodesL.Add(node);
                }
                partNodes = partNodesL.ToArray();



                // Estimate an object class, numbers are based on the in game description of the
                // size classes.
                //float size = shipConstruct.shipSize.magnitude / 2.0f;
                //if (size < 4.0f)
                //{
                //  sizeClass = UntrackedObjectClass.A;
                //}
                //else if (size < 7.0f)
                //{
                //  sizeClass = UntrackedObjectClass.B;
                //}
                //else if (size < 12.0f)
                //{
                //  sizeClass = UntrackedObjectClass.C;
                //}
                //else if (size < 18.0f)
                //{
                //  sizeClass = UntrackedObjectClass.D;
                //}
                //else
                //{
                //  sizeClass = UntrackedObjectClass.E;
                //}
            }
            else
            {
                // Create crew member array
                ProtoCrewMember[] crewArray = new ProtoCrewMember[vesselData.crew.Count];
                int i = 0;
                foreach (CrewData cd in vesselData.crew)
                {
                    // Create the ProtoCrewMember
                    ProtoCrewMember crewMember = HighLogic.CurrentGame.CrewRoster.GetNewKerbal(ProtoCrewMember.KerbalType.Crew);
                    if (cd.name != null)
                    {
                        crewMember.KerbalRef.name = cd.name;
                    }

                    crewArray[i++] = crewMember;
                }

                // Create part nodes
                uint flightId = ShipConstruction.GetUniqueFlightID(HighLogic.CurrentGame.flightState);
                partNodes    = new ConfigNode[1];
                partNodes[0] = ProtoVessel.CreatePartNode(vesselData.craftPart.name, flightId, crewArray);

                // Default the size class
                //sizeClass = UntrackedObjectClass.A;

                // Set the name
                if (string.IsNullOrEmpty(vesselData.name))
                {
                    vesselData.name = vesselData.craftPart.name;
                }
            }

            // Create additional nodes
            ConfigNode[] additionalNodes = new ConfigNode[0];
            //DiscoveryLevels discoveryLevel = vesselData.owned ? DiscoveryLevels.Owned : DiscoveryLevels.Unowned;
            //additionalNodes[0] = ProtoVessel.CreateDiscoveryNode(discoveryLevel, sizeClass, contract.TimeDeadline, contract.TimeDeadline);

            // Create the config node representation of the ProtoVessel
            ConfigNode protoVesselNode = ProtoVessel.CreateVesselNode(vesselData.name, vesselData.vesselType, vesselData.orbit, 0, partNodes, additionalNodes);

            // Additional seetings for a landed vessel
            if (!vesselData.orbiting)
            {
                Vector3d norm = vesselData.body.GetRelSurfaceNVector(vesselData.latitude, vesselData.longitude);

                double terrainHeight = 0.0;
                if (vesselData.body.pqsController != null)
                {
                    terrainHeight = vesselData.body.pqsController.GetSurfaceHeight(norm) - vesselData.body.pqsController.radius;
                }
                bool splashed = false;// = landed && terrainHeight < 0.001;

                // Create the config node representation of the ProtoVessel
                // Note - flying is experimental, and so far doesn't work
                protoVesselNode.SetValue("sit", (splashed ? Vessel.Situations.SPLASHED : landed ?
                                                 Vessel.Situations.LANDED : Vessel.Situations.FLYING).ToString());
                protoVesselNode.SetValue("landed", (landed && !splashed).ToString());
                protoVesselNode.SetValue("splashed", splashed.ToString());
                protoVesselNode.SetValue("lat", vesselData.latitude.ToString());
                protoVesselNode.SetValue("lon", vesselData.longitude.ToString());
                protoVesselNode.SetValue("alt", vesselData.altitude.ToString());
                protoVesselNode.SetValue("landedAt", vesselData.body.name);

                // Figure out the additional height to subtract
                float lowest = float.MaxValue;
                if (shipConstruct != null)
                {
                    foreach (Part p in shipConstruct.parts)
                    {
                        foreach (Collider collider in p.GetComponentsInChildren <Collider>())
                        {
                            if (collider.gameObject.layer != 21 && collider.enabled)
                            {
                                lowest = Mathf.Min(lowest, collider.bounds.min.y);
                            }
                        }
                    }
                }
                else
                {
                    foreach (Collider collider in vesselData.craftPart.partPrefab.GetComponentsInChildren <Collider>())
                    {
                        if (collider.gameObject.layer != 21 && collider.enabled)
                        {
                            lowest = Mathf.Min(lowest, collider.bounds.min.y);
                        }
                    }
                }

                if (lowest == float.MaxValue)
                {
                    lowest = 0;
                }

                // Figure out the surface height and rotation
                Quaternion normal   = Quaternion.LookRotation((Vector3)norm);// new Vector3((float)norm.x, (float)norm.y, (float)norm.z));
                Quaternion rotation = Quaternion.identity;
                float      heading  = vesselData.heading;
                if (shipConstruct == null)
                {
                    rotation = rotation * Quaternion.FromToRotation(Vector3.up, Vector3.back);
                }
                else if (shipConstruct.shipFacility == EditorFacility.SPH)
                {
                    rotation = rotation * Quaternion.FromToRotation(Vector3.forward, -Vector3.forward);
                    heading += 180.0f;
                }
                else
                {
                    rotation = rotation * Quaternion.FromToRotation(Vector3.up, Vector3.forward);
                    rotation = Quaternion.FromToRotation(Vector3.up, -Vector3.up) * rotation;

                    //rotation = craftRotation;


                    vesselData.heading = 0;
                    vesselData.pitch   = 0;
                }

                rotation = rotation * Quaternion.AngleAxis(heading, Vector3.back);
                rotation = rotation * Quaternion.AngleAxis(vesselData.roll, Vector3.down);
                rotation = rotation * Quaternion.AngleAxis(vesselData.pitch, Vector3.left);

                // Set the height and rotation
                if (landed || splashed)
                {
                    float hgt = (shipConstruct != null ? shipConstruct.parts[0] : vesselData.craftPart.partPrefab).localRoot.attPos0.y - lowest;
                    hgt += vesselData.height;

                    foreach (Part p in shipConstruct.Parts)
                    {
                        LaunchClamp lc = p.FindModuleImplementing <LaunchClamp>();
                        if (lc)
                        {
                            hasClamp = true;
                            break;
                        }
                    }

                    if (!hasClamp)
                    {
                        hgt += 35;
                    }
                    else
                    {
                        hgt += lcHeight;
                    }
                    protoVesselNode.SetValue("hgt", hgt.ToString(), true);
                }
                protoVesselNode.SetValue("rot", KSPUtil.WriteQuaternion(normal * rotation), true);

                // Set the normal vector relative to the surface
                Vector3 nrm = (rotation * Vector3.forward);
                protoVesselNode.SetValue("nrm", nrm.x + "," + nrm.y + "," + nrm.z, true);

                protoVesselNode.SetValue("prst", false.ToString(), true);
            }

            // Add vessel to the game
            ProtoVessel protoVessel = HighLogic.CurrentGame.AddVessel(protoVesselNode);

            //protoVessel.vesselRef.transform.rotation = protoVessel.rotation;


            // Store the id for later use
            vesselData.id = protoVessel.vesselRef.id;

            //protoVessel.vesselRef.currentStage = 0;

            StartCoroutine(PlaceSpawnedVessel(protoVessel.vesselRef, !hasClamp));

            // Associate it so that it can be used in contract parameters
            //ContractVesselTracker.Instance.AssociateVessel(vesselData.name, protoVessel.vesselRef);



            //destroy prefabs
            foreach (Part p in FindObjectsOfType <Part>())
            {
                if (!p.vessel)
                {
                    Destroy(p.gameObject);
                }
            }
        }
Beispiel #2
0
        public void Update()
        {
            Vessel vessel = FlightGlobals.ActiveVessel;             // easier to use vessel

            // safety check
            if (vessel == null || !HighLogic.LoadedSceneIsFlight)
            {
                return;
            }

            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            //
            // atmospheric shake
            //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            // up the shake based on atmopshereic density and surface speed
            float spdDensity = (float)(vessel.atmDensity) * (float)FlightGlobals.ship_srfSpeed;

            // limit here, mainly for space planes
            spdDensity = Mathf.Clamp(spdDensity, 0, maxSpdDensityEarly);

            // exagerate shake if semideployed, dampen if deployed
            foreach (Part part in vessel.Parts)
            {
                foreach (PartModule module in part.Modules)
                {
                    if (module.moduleName.Contains("ModuleParachute"))
                    {
                        ModuleParachute p = module as ModuleParachute;

                        if (p.deploymentState == ModuleParachute.deploymentStates.SEMIDEPLOYED && !vessel.LandedOrSplashed)
                        {
                            spdDensity *= 1.25f;
                        }

                        if (p.deploymentState == ModuleParachute.deploymentStates.DEPLOYED && !vessel.LandedOrSplashed)
                        {
                            spdDensity *= 0.75f;
                        }
                    }
                    // RealChute Support, reworked to be compatible with RealChute v1.2
                    if (module.moduleName.Contains("RealChuteModule"))
                    {
                        PartModule p          = part.Modules["RealChuteModule"];
                        Type       pType      = p.GetType();
                        object     parachutes = pType.GetField("parachutes").GetValue(p);
                        foreach (object parachute in (parachutes as IEnumerable))
                        {
                            Type cType = parachute.GetType();
                            if (cType.GetField("depState").GetValue(parachute) == "PREDEPLOYED")
                            {
                                spdDensity *= 1.25f;
                            }
                            if ((cType.GetField("depState").GetValue(parachute) == "DEPLOYED") || (cType.GetField("depState").GetValue(parachute) == "LOWDEPLOYED"))
                            {
                                spdDensity *= 0.75f;
                            }
                        }
                    }
                }
            }

            // lifted from DRE (thanks r4m0n), gets the mach / reentry fx
            if (afx == null)
            {
                GameObject fx = GameObject.Find("FXLogic");
                if (fx != null)
                {
                    afx = fx.GetComponent <AerodynamicsFX>();
                }
            }

            // sirhaxington special: use weird values I found to determine if mach or reentry, there has to be a better way...
            if ((afx != null) && (afx.FxScalar > 0.01))
            {
                // hack, whatever the .b color value is, always is this for re-entry, .11 something
                if (afx.fxLight.color.b < 0.12f)
                {
                    burnDownTime = atmoBurnDownTimes[0];
                }

                // hack, whatever the .b color value is, always is this for mach fx, .21 something
                if (afx.fxLight.color.b > 0.20f)
                {
                    // since we * 10, only do this if it's going to increase...
                    if (afx.FxScalar > 0.1)
                    {
                        spdDensity *= (afx.FxScalar * 10);
                    }
                }
            }

            // ease back into normal atmophere from re-entry
            if (burnDownTime > 0)
            {
                spdDensity   *= (afx.FxScalar * burnDownTime * 1000);
                burnDownTime -= Time.deltaTime;
            }

            // dont go too crazy...
            spdDensity = Mathf.Clamp(spdDensity, 0, maxSpdDensity);

            if (!vessel.isEVA)
            {
                shakeAmt = ReturnLargerAmt((UnityEngine.Random.insideUnitSphere * spdDensity) / 500000, shakeAmt);
                shakeRot = ReturnLargerRot(Quaternion.Euler(0, 0, (UnityEngine.Random.Range(-0.1f, 0.1f) * spdDensity) / 5000), shakeRot);
            }

            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            //
            // parachute open shake
            //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            int chuteCheck = 0;                                 // re-do this every frame, check against previous frame to see if chute opened

            // a chute has popped...
            // Note: Don't need to do this with realChutes as they tend to open slow anyway
            foreach (Part part in vessel.parts)
            {
                foreach (PartModule module in part.Modules)
                {
                    if (module.moduleName.Contains("ModuleParachute"))
                    {
                        ModuleParachute p = module as ModuleParachute;

                        if (p.deploymentState != ModuleParachute.deploymentStates.DEPLOYED)
                        {
                            chuteCheck++;
                        }
                    }
                }
            }

            // check against previous frames' chutes, then prep shake event
            if (chuteCheck < totalUndeployedChutes)
            {
                doParaFull    = true;
                paraShakeTime = paraShakeTimes[0];
            }

            // set this at end of check for next frame
            totalUndeployedChutes = chuteCheck;

            // do the parachute pop shake
            if (paraShakeTime > 0 && doParaFull)
            {
                shakeAmt       = ReturnLargerAmt(UnityEngine.Random.insideUnitSphere / 500, shakeAmt);
                shakeRot       = ReturnLargerRot(Quaternion.Euler(0, 0, UnityEngine.Random.Range(-0.5f, 0.5f)), shakeRot);
                paraShakeTime -= Time.deltaTime;
            }
            else if (paraShakeTime <= 0)
            {
                doParaFull = false;
            }

            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            //
            // decoupler shakes
            //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            // total is grabbed from event handler
            if (ejectionForceTotal > 0)
            {
                // playing around with set shake timers, not sure if I like this way but it works for now
                if (ejectionForceTotal <= 15)
                {
                    decoupleShakeTime = decoupleShakeTimes[0];
                }
                else if (ejectionForceTotal <= 250)
                {
                    decoupleShakeTime = decoupleShakeTimes[1];
                }
                else if (ejectionForceTotal <= 500)
                {
                    decoupleShakeTime = decoupleShakeTimes[2];
                }
                else if (ejectionForceTotal <= 1000)
                {
                    decoupleShakeTime = decoupleShakeTimes[3];
                }
                else
                {
                    decoupleShakeTime = decoupleShakeTimes[4];
                }

                decoupleShakeForce = ejectionForceTotal;

                doDecoupleShake    = true;
                ejectionForceTotal = 0;
            }

            // do the decoupler shake
            if (decoupleShakeTime > 0 && doDecoupleShake)
            {
                shakeAmt           = ReturnLargerAmt(UnityEngine.Random.insideUnitSphere * decoupleShakeForce / 500000, shakeAmt);
                shakeRot           = ReturnLargerRot(Quaternion.Euler(0, 0, UnityEngine.Random.Range(-0.5f, 0.5f)), shakeRot);
                decoupleShakeTime -= Time.deltaTime;
            }
            else if (decoupleShakeTime <= 0)
            {
                doDecoupleShake = false;
            }

            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            //
            // docking shakes
            //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            // do the dock shake...we set the time from the handler, no need for other checks
            if (dockShakeTime > 0)
            {
                shakeAmt       = ReturnLargerAmt(UnityEngine.Random.insideUnitSphere / 1000, shakeAmt);
                shakeRot       = ReturnLargerRot(Quaternion.Euler(0, 0, UnityEngine.Random.Range(-0.07f, 0.07f)), shakeRot);
                dockShakeTime -= Time.deltaTime;
            }

            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            //
            // launch clamp shakes
            //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            int clampCheck = 0;

            // a clamp has detached...
            foreach (Part part in vessel.parts)
            {
                foreach (PartModule module in part.Modules)
                {
                    if (module.moduleName.Contains("LaunchClamp"))
                    {
                        LaunchClamp lc = module as LaunchClamp;

                        if (lc.enabled)
                        {
                            clampCheck++;
                        }
                    }
                }
            }

            // check against previous frames' chutes, then prep shake event
            if (clampCheck < totalClampedClamps)
            {
                doClamp        = true;
                clampShakeTime = clampShakeTimes[0];
            }

            // set this at end of check for next frame
            totalClampedClamps = clampCheck;

            // do the parachute pop shake
            if (clampShakeTime > 0 && doClamp)
            {
                shakeAmt        = ReturnLargerAmt(UnityEngine.Random.insideUnitSphere / 500, shakeAmt);
                shakeRot        = ReturnLargerRot(Quaternion.Euler(0, 0, UnityEngine.Random.Range(-0.7f, 0.7f)), shakeRot);
                clampShakeTime -= Time.deltaTime;
            }
            else if (clampShakeTime <= 0)
            {
                doClamp = false;
            }

            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            //
            // engine shakes
            //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            // check both engine types (rapier uses ModuleEnginesFX, most use ModuleEngines) then base shake on thrust amount
            foreach (Part part in vessel.Parts)
            {
                foreach (PartModule module in part.Modules)
                {
                    if (module.moduleName.Contains("ModuleEnginesFX"))
                    {
                        ModuleEnginesFX e = module as ModuleEnginesFX;

                        if (e.isOperational)
                        {
                            float solidScalar = 1.0f;            // scale up SRBs

                            if (e.propellants.Count > 0)
                            {
                                foreach (Propellant p in e.propellants)
                                {
                                    if (p.name == "SolidFuel")
                                    {
                                        solidScalar = 2.5f;
                                    }
                                }
                            }
                            engineThrustTotal += (e.finalThrust * solidScalar);
                        }

                        if (engineThrustTotal > 0)
                        {
                            doEngineShake = true;
                        }
                    }
                    else if (module.moduleName.Contains("ModuleEngines"))
                    {
                        ModuleEngines e = module as ModuleEngines;

                        if (e.isOperational)
                        {
                            float typeScalar = 1.0f;            // scale up SRBs, down jets

                            if (e.propellants.Count > 0)
                            {
                                foreach (Propellant p in e.propellants)
                                {
                                    if (p.name == "SolidFuel")
                                    {
                                        typeScalar = 2.5f;
                                    }
                                    else if (p.name == "IntakeAir")
                                    {
                                        typeScalar = 0.01f;
                                    }
                                }
                            }
                            engineThrustTotal += (e.finalThrust * typeScalar);
                        }

                        if (engineThrustTotal > 0)
                        {
                            doEngineShake = true;
                        }
                    }

                    // don't go too crazy...
                    engineThrustTotal = Mathf.Clamp(engineThrustTotal, 0, maxEngineForce);
                }
            }

            // do engine shake...
            if (engineThrustTotal > 0 && doEngineShake)
            {
                shakeAmt = ReturnLargerAmt((UnityEngine.Random.insideUnitSphere * (engineThrustTotal / 1000)) / 800, shakeAmt);
                shakeRot = ReturnLargerRot(Quaternion.Euler(0, 0, UnityEngine.Random.Range(-0.8f, 0.8f) * (engineThrustTotal / 1000)), shakeRot);
            }
            else if (engineThrustTotal <= 0)
            {
                doEngineShake = false;
            }

            // reset every frame
            engineThrustTotal = 0;

            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            //
            // nearby collision shakes
            //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            // do the collision shake...we set the time from the handler, no need for other checks
            if (collisionShakeTime > 0)
            {
                shakeAmt            = ReturnLargerAmt(UnityEngine.Random.insideUnitSphere / 50, shakeAmt);
                shakeRot            = ReturnLargerRot(Quaternion.Euler(0, 0, UnityEngine.Random.Range(-1.5f, 1.5f)), shakeRot);
                collisionShakeTime -= Time.deltaTime;
            }

            // reset for next frame, use negative since we're looking for distance now
            collisionClosest = -1;

            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            //
            // rover ground shakes
            //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            float spdRover = (float)FlightGlobals.ship_srfSpeed;

            doRover = false;
            float roverScalar = 1.0f;

            foreach (Part part in vessel.Parts)
            {
                foreach (PartModule module in part.Modules)
                {
                    if (module.moduleName.Contains("ModuleWheel"))
                    {
                        if (part.GroundContact)
                        {
                            if (vessel.landedAt.Length == 0 || vessel.landedAt.ToString() == "KSC")
                            {
                                roverScalar = 2.0f;
                            }
                            // maybe later, do biome specific shakes
                            //CBAttributeMap currentBiome = vessel.mainBody.BiomeMap;
                            //print(currentBiome.GetAtt(vessel.latitude * Mathf.Deg2Rad, vessel.longitude * Mathf.Deg2Rad).name);
                            //print(currentBiome.ToString());

                            doRover = true;
                        }
                    }
                    if (module.moduleName.Contains("ModuleLandingGear"))
                    {
                        if (part.Landed)
                        {
                            if (vessel.landedAt.Length == 0 || vessel.landedAt.ToString() == "KSC")         // basically, in and around KSC
                            {
                                roverScalar = 2.0f;
                            }

                            doRover = true;
                        }
                    }
                }
            }

            spdRover *= roverScalar;

            // dont go too crazy...
            spdRover = Mathf.Clamp(spdRover, 0, maxSpdRover);

            if (doRover && !vessel.isEVA)
            {
                shakeAmt = ReturnLargerAmt((UnityEngine.Random.insideUnitSphere * spdRover) / 50000, shakeAmt);
                shakeRot = ReturnLargerRot(Quaternion.Euler(0, 0, (UnityEngine.Random.Range(-0.1f, 0.1f) * spdRover) / 500), shakeRot);
            }

            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            //
            // landing shakes
            //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            // note, parts that break off may throw this off
            int landedCurParts = 0;

            foreach (Part part in vessel.Parts)
            {
                if (part.GroundContact || part.WaterContact)
                {
                    landedCurParts++;
                }
            }

            // if more parts are touching the ground this frame...
            if (landedCurParts > landedPrevParts)
            {
                doLanded = true;

                // do I need horizontal surface speed as well? hmmm...
                landedShakeForce = landedPrevSrfSpd;

                if (landedShakeForce <= 0.5)
                {
                    landedShakeTime = landedShakeTimes[0];
                }
                else if (landedShakeForce <= 1.5)
                {
                    landedShakeTime = landedShakeTimes[1];
                }
                else if (landedShakeForce <= 3.0)
                {
                    landedShakeTime = landedShakeTimes[2];
                }
                else if (landedShakeForce <= 5.0)
                {
                    landedShakeTime = landedShakeTimes[3];
                }
                else
                {
                    landedShakeTime = landedShakeTimes[4];
                }

                if (doRover)
                {
                    landedShakeForce /= 2;
                }

                landedShakeForce = Mathf.Clamp(landedShakeForce, 0, maxLandedForce);
            }

            // set the current parts for the next frame
            landedPrevParts = landedCurParts;

            // do the landing / touching ground / water shake
            if (doLanded && !vessel.isEVA)
            {
                if (landedShakeTime > 0)
                {
                    shakeAmt         = ReturnLargerAmt((UnityEngine.Random.insideUnitSphere * landedShakeForce) / 3600, shakeAmt);
                    shakeRot         = ReturnLargerRot(Quaternion.Euler(0, 0, UnityEngine.Random.Range(-0.1f, 0.1f) * landedShakeForce), shakeRot);
                    landedShakeTime -= Time.deltaTime;
                }
                else
                {
                    doLanded = false;
                }
            }

            // set the speed for the next frame
            landedPrevSrfSpd = (float)FlightGlobals.ActiveVessel.srfSpeed;

            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            //
            // EVA Shakes (under construction)
            //
            // to do: polish shakes (check on a few planets), rotation add back in
            //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            if (vessel.isEVA)
            {
                KerbalEVA eva = (KerbalEVA)vessel.rootPart.Modules["KerbalEVA"];

                // if an anim has changed
                if (eva.fsm.currentStateName != evaAnimState)
                {
                    //print(evaSrfSpedPrev);
                    if (eva.fsm.currentStateName == "Landing")                              // standard landing from a jump / RCS
                    {
                        evaShakeTime       = evaShakeTimes[2];
                        evaAnimShakeAmount = (int)(45000 / evaSrfSpedPrev);
                    }
                    else if (eva.fsm.currentStateName == "Ragdoll")                         // we landed too hard / jumped and landed at an odd angle
                    {
                        evaShakeTime       = evaShakeTimes[5];
                        evaAnimShakeAmount = (int)(8000 / evaSrfSpedPrev);
                    }
                    else if (eva.fsm.currentStateName == "Low G Bound (Grounded - FPS)")    // each step on a low g world should be felt
                    {
                        evaShakeTime       = evaShakeTimes[2];
                        evaAnimShakeAmount = (int)(50000);
                    }
                    else if (eva.fsm.currentStateName == "Ladder (Acquire)")                 // feel the grab a bit more
                    {
                        evaShakeTime       = evaShakeTimes[5];
                        evaAnimShakeAmount = (int)(1000000);
                    }
                    evaAnimState = eva.fsm.currentStateName;
                    //print(evaAnimState);
                }
                else if (evaAnimState == "Ragdoll" && vessel.Landed && evaShakeTime <= 0)    // when ragging and sliding along the surface, keep shaking
                {
                    evaShakeTime       = evaShakeTimes[5];
                    evaAnimShakeAmount = (int)(8000 / evaSrfSpedPrev);
                }
                else if (evaAnimState == "Ladder (Acquire)" && evaShakeTime >= 0)           // when ragging and sliding along the surface or falling, update shaking
                {
                    if (evaShakeTime < 0.3)
                    {
                        evaAnimShakeAmount = (int)(4000);
                    }
                }

                // update shake based on timers
                if (evaShakeTime > 0)
                {
                    shakeAmt      = ReturnLargerAmt(UnityEngine.Random.insideUnitSphere / evaAnimShakeAmount, shakeAmt);
                    evaShakeTime -= Time.deltaTime;
                }

                // RCS Cam Shake
                if (Math.Round(eva.Fuel, 3) != Math.Round(evaFuel, 3))
                {
                    evaFuel  = eva.Fuel;
                    shakeAmt = ReturnLargerAmt(UnityEngine.Random.insideUnitSphere / evaRCSShakeAmount, shakeAmt);
                    Math.Round(eva.Fuel, 3);
                }

                // grab frame two behind to test against
                evaSrfSpedPrev = evaSrfSped;
                evaSrfSped     = (float)vessel.srfSpeed;
            }

            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            //
            // DO THE HARLEMSHAKE! o/\o
            //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            // Set isCrewed to true if not unmanned, used to remove shake in control room when using ProbeControlRoom mod
            bool isCrewed = false;

            foreach (Part part in vessel.Parts)
            {
                if (part.protoModuleCrew.Count >= 1)
                {
                    isCrewed = true;
                }
            }

            // hopefully we've picked the largest values... also, don't shake while paused, looks dumb
            if (InternalCamera.Instance != null)
            {
                if (!gamePaused && InternalCamera.Instance.isActive && isCrewed) // isCrewed for shake only if not in control room
                {
                    InternalCamera.Instance.camera.transform.localPosition  = shakeAmt;
                    InternalCamera.Instance.camera.transform.localRotation *= shakeRot;
                }
            }


            // for a different first person EVA mod...
            bool FPEVAMod = false;

            foreach (Part part in vessel.Parts)
            {
                foreach (PartModule module in part.Modules)
                {
                    if (module.moduleName.Contains("EVACamera"))
                    {
                        FPEVAMod = true;
                    }
                }
            }

            // rotation is wonky in EVA, skip
            if (vessel.isEVA && FlightCamera.fetch.minDistance == 0.01f && FlightCamera.fetch.maxDistance == 0.01f)
            {
                if (shakeAmt.x != 0 && shakeAmt.y != 0 && shakeAmt.z != 0)
                {
                    FlightCamera.fetch.transform.localPosition += shakeAmt;
                }
            }
            else if (vessel.isEVA && FPEVAMod)
            {
                FlightCamera.fetch.transform.localPosition += (shakeAmt * 1000);
            }

            // reset the shake vals every frame and start over...
            shakeAmt = new Vector3(0.0f, 0.0f, 0.0f);
            shakeRot = new Quaternion(0.0f, 0.0f, 0.0f, 0.0f);
        }