Jump to content

xEvilReeperx

Members
  • Posts

    894
  • Joined

  • Last visited

Everything posted by xEvilReeperx

  1. I think the logs are telling you the truth here. I checked out the version from SpaceDock and every KFModuleWheel does some really inefficient stuff if the total vehicle's mass has changed by more than a tenth of a unit since the last Update. If the rate of change is fast enough to trigger this very often, your frame rate will get heavily penalized (mainly by the debug statements themselves) by however many KFModuleWheels you have on that vessel
  2. Can you be more specific when you say "not working"? The orientation doesn't change at all? Does it work teleporting around Kerbin? What are you expecting and what are you seeing?
  3. Uh ... you're assigning something to originalUp right? Maybe you forgot to put a 'originalUp = FlightGlobals.getUpAxis()' in your method somewhere?
  4. That's essentially what the above does. I avoided using a PartModule because it has no advantages over a MonoBehaviour in this case while costing memory and performance. Up to you though
  5. Is that the actual vector you supplied? You should be getting a unit length vector, either using the up axis from FlightGlobals if you're only teleporting the active vessel, or else by calculating the normal at the departure/arrival locations yourself
  6. Well ... you could add a fake renderer that will be controlled by the stock highlighting code in Part, then copy its material properties to all your other renderers with a strategically placed MonoBehaviour. That's the best I could come up with that will cover all the edge cases (such as when the editor logic changes highlight colors or freezes Parts). Proof of concept: [KSPAddon(KSPAddon.Startup.MainMenu, true)] class AddHighlightingFix : MonoBehaviour { public const string DummyTransformName = "dummy_highlighter_dont_delete"; private void Start() { DontDestroyOnLoad(this); GetAllPartModelTransforms() .ToList() .ForEach(AddHighlightCopierAndDummyRenderer); } // note: it's important to avoid adding our MonoBehaviour directly to the Part's GO! The game will strip it out // on root parts if it isn't one of the allowed types (PartModule in particular but there are some others) private static IEnumerable<Transform> GetAllPartModelTransforms() { return PartLoader.LoadedPartsList.Where(ap => ap.partPrefab.transform.Find("model") != null) .Select(ap => ap.partPrefab.transform.Find("model")); } private static void AddHighlightCopierAndDummyRenderer(Transform partModelTransform) { partModelTransform.gameObject.AddComponent<CopyHighlightFromDummyRenderer>(); var dummyContainer = new GameObject(DummyTransformName); dummyContainer.transform.parent = partModelTransform; DontDestroyOnLoad(dummyContainer); dummyContainer.AddComponent<MeshRenderer>(); } } class CopyHighlightFromDummyRenderer : MonoBehaviour { private Renderer _dummyRenderer; private readonly MaterialPropertyBlock _propertyBlock = new MaterialPropertyBlock(); private List<Renderer> _allOtherRenderers = new List<Renderer>(); private void Awake() { var dummyTransform = transform.Find(AddHighlightingFix.DummyTransformName); if (!HighLogic.LoadedSceneIsEditor || dummyTransform == null || dummyTransform.renderer == null) { Destroy(this); return; } _dummyRenderer = dummyTransform.renderer; } private void Start() { OnTransformChildrenChanged(); } private void OnTransformChildrenChanged() { _allOtherRenderers = GetComponentsInChildren<Renderer>(true).Except(new[] { _dummyRenderer }).ToList(); } private void LateUpdate() { // grab current highlight colors _propertyBlock.SetColor(HighLogic.ShaderPropertyID_RimColor, _dummyRenderer.material.GetColor(HighLogic.ShaderPropertyID_RimColor)); _propertyBlock.SetFloat(HighLogic.ShaderPropertyID_RimFalloff, _dummyRenderer.material.GetFloat(HighLogic.ShaderPropertyID_RimFalloff)); // apply to all other renderers bool refreshChildren = false; foreach (var r in _allOtherRenderers) { if (r == null) // if somebody destroyed a Renderer, we'll want to refresh the list { refreshChildren = true; continue; } r.SetPropertyBlock(_propertyBlock); } if (refreshChildren) OnTransformChildrenChanged(); } }
  7. Iterating through the MapAttributes works fine for the most part, with the only wrinkle being that it'll miss any "special" biomes that are hard-coded and not on the biome map like the KSC. If you're generating possible subjectids, you'll want to use ResearchAndDevelopment.GetBiomeTags() to include those as well The results are stored in the ScienceExperiment itself as a dictionary with (half of) the subjectid as key foreach (var experiment in ResearchAndDevelopment.GetExperimentIDs().Select(ResearchAndDevelopment.GetExperiment)) foreach (var result in experiment.Results) { // [subjectid] : [result text] print(result.Key + " : " + result.Value); } // crew report default : You record the crew's assessment of the situation. KerbinSrfLandedLaunchpad : We don't seem to be moving very fast right now. KerbinSrfLandedRunway : Reporting in at the Runway. Good thing there's not a lot of air traffic, because I don't think we ever got clearance from the tower to be here. KerbinSrfLandedKSC : This is our Space Center here. We're home. KerbinFlyingLowGrasslands : Hey I can see my house from here, I think. KerbinFlyingLowGrasslands* : It's very comforting to see that much green below you. // snip you get the idea Edit: I just realized the asterisk is for marking results for subjectids that don't exactly match, for instance "BopSrfLanded*" will match any subjectid that contains BopSrfLanded in it. Multiple asterisk-marked entries are randomly selected by ResearchAndDevelopment.GetResults()
  8. Hurray! Why post that kind of info here but not where it could be useful, such as a bug report in the release thread? Users
  9. I wouldn't bother fiddling with the animations. Just disable the antenna script itself temporarily (ModuleRTAntenna.enabled = false) while you move it so it doesn't get the FixedUpdate that is causing it to destroy itself
  10. Looks like it's an oversight in KSP. Particle[Emitter/Animator/Renderer] components work with Icon_Hidden but KSPParticleEmitter and probably SkinnedMeshRenderer do not. The Icon_Only tag functions normally. Well, easily fixed in code: [KSPAddon(KSPAddon.Startup.Instantly, true)] public class FixIconHiddenBug : LoadingSystem { private static bool _hasRun = false; private const string IconHiddenTag = "Icon_Hidden"; private void Start() { if (_hasRun) { Destroy(gameObject); return; } FindObjectOfType<LoadingScreen>().loaders.Add(this); } public override bool IsReady() { return _hasRun; } public override void StartLoad() { _hasRun = true; var startTime = Time.realtimeSinceStartup; foreach ( var iconGo in PartLoader.LoadedPartsList.Where(ap => ap.iconPrefab != null).Select(ap => ap.iconPrefab)) { PartLoader.StripTaggedTransforms(iconGo.transform, IconHiddenTag); } print("Finished fixing tags in " + (Time.realtimeSinceStartup - startTime).ToString("F3") + " sec"); } public override string ProgressTitle() { return "Fixing icon tags"; } } Edit: Just so nobody is surprised by it, this will delete any tagged GO and its children
  11. Add the Icon_Hidden tag to the particle emitter's GameObject inside Unity before exporting it
  12. You'll save yourself a lot of time if you can get debug mode working inside KSP via one of the stickies. Where is the cfg file itself located? That would be my current suspicion. GameDatabase only auto-loads ConfigNodes inside GameData and subdirectories so if you've put it elsewhere (maybe alongside the persistent sfs or .craft you copied its contents from?) it won't be found
  13. It's possible and even probable that the save you loaded up with Kopernicus isn't easily recoverable but the rest should be salvageable. We'll need your logs to even begin to guess at what has broken them though. Edit: Actually I'll just take a swing since it's likely: if the game crashed while saving, it's possible the persistent.sfs of the game you loaded when Kopernicus installed was corrupted and KSP is throwing an unhandled exception while trying to parse it. This will cause a chain reaction of failure: most likely, clicking "Resume game" will appear to do nothing at all while the log (Alt+F2) prints an exception Try moving or deleting that save's folder
  14. This is the best solution I could come up with: [KSPScenario(ScenarioCreationOptions.AddToAllGames, GameScenes.FLIGHT)] public class ScenarioHideTheNut : ScenarioModule { public override void OnSave(ConfigNode node) { base.OnSave(node); foreach (var pps in HighLogic.CurrentGame.flightState.protoVessels.SelectMany(pv => pv.protoPartSnapshots)) pps.modules.RemoveAll( ppms => (ppms.moduleRef != null && ppms.moduleRef.GetType() == typeof (ModuleNut)) || ppms.moduleName == typeof (ModuleNut).Name); } } public class ModuleNut : PartModule { [KSPEvent(guiName = "Hello world", active = true, guiActive = true)] public void HelloWorld() { print("Nut says, \"Hello world!\""); } } public class AddNutToAllParts : VesselModule { private void Start() { if (!HighLogic.LoadedSceneIsFlight) return; var vessel = GetComponent<Vessel>(); foreach (var p in vessel.parts) if (!p.gameObject.GetComponents<ModuleNut>().Any()) { print("Adding " + typeof (ModuleNut).Name + " to " + p.partInfo.name); p.AddModule(typeof (ModuleNut).Name); } } } The ScenarioModule will prevent any PartModule(s) you want from being saved to the ConfigNode without preventing them from working normally otherwise. I used a VesselModule to add the test PartModule to the parts in the flight scene but it should work with MM scripts as well* if one needed to hide those Edit: *It occurs to me that you'd probably want to make sure your MM patch ran last to avoid those mismatch problems if you weren't adding PartModules on the fly
  15. If there were an event that allowed you to intercept (and potentially veto) your target KSPEvent(s), would you still need to define your own? That's how I'd handle this. If you never need any more BaseFields than what your target modules implement, you can get away with just hijacking them and avoid being a PartModule altogether
  16. Do your modules have to be PartModules? What do they do? You might be able to get away with attaching custom MonoBehaviours right to the prefabs. That would definitely be the cleanest solution
  17. [KSPAddon(KSPAddon.Startup.Flight, false)] public class OrbitMouseOver : MonoBehaviour { private bool _mouseOver = false; private PatchedConics.PatchCastHit _mousedOverPatch; private Rect _popup = new Rect(0f, 0f, 120f, 60f); private Texture2D _cross; private Rect _crossRect; private void Awake() { _popup.center = new Vector2(Screen.width * 0.5f - _popup.width * 0.5f, Screen.height * 0.5f - _popup.height * 0.5f); _cross = new Texture2D(32, 32, TextureFormat.ARGB32, false); _crossRect = new Rect(0, 0, _cross.width, _cross.height); var pixels = Enumerable.Repeat(Color.clear, _cross.width * _cross.height).ToArray(); for (int i = 0; i < _cross.width; ++i) for (int j = 0; j < _cross.height; ++j) { if (i == j || j == (_cross.width - i)) pixels[j * _cross.height + i] = Color.red; } _cross.SetPixels(pixels); _cross.Apply(); } private void Update() { _mouseOver = MouseOverOrbit(out _mousedOverPatch); if (!_mouseOver) return; var updatedLocation = _mousedOverPatch.GetUpdatedScreenPoint(); _popup.center = _crossRect.center = new Vector2(updatedLocation.x, Screen.height - updatedLocation.y); } private void OnGUI() { if (!_mouseOver) return; GUI.skin = HighLogic.Skin; GUILayout.BeginArea(GUIUtility.ScreenToGUIRect(_popup)); GUILayout.Label("UT: " + KSPUtil.PrintTime((int)(Planetarium.GetUniversalTime() - _mousedOverPatch.UTatTA), 5, true)); GUILayout.EndArea(); if (Event.current.type == EventType.Repaint) Graphics.DrawTexture(_crossRect, _cross); } private bool MouseOverOrbit(out PatchedConics.PatchCastHit hit) { hit = default(PatchedConics.PatchCastHit); if (FlightGlobals.ActiveVessel == null) return false; var patchRenderer = FlightGlobals.ActiveVessel.patchedConicRenderer; if (patchRenderer == null) return false; var patches = patchRenderer.solver.maneuverNodes.Any() ? patchRenderer.flightPlanRenders : patchRenderer.patchRenders; return PatchedConics.ScreenCast(Input.mousePosition, patches, out hit); } } Here's a rough proof of concept. It works on the active vessel's orbit. If you want to include the other orbits (CelestialBodies at least, anyway), you'll probably want to use OrbitRenderer.OrbitCast. I didn't go far on that path other than yes/no type mouseover debug spam since it became apparent that the maneuver node placement was doing something different and the active vessel's orbit wasn't included by that method
  18. I was referring to the debugging support Contracts are a problem (because unimplemented) but most people don't use a custom Part-derived type in their plugins and custom InternalModules are pretty rare in my experience as well...
  19. It might be the network adapter issue. Try disabling any unused ones (Hamachi is the most likely culprit but there are other possibilities) and see if that helps
  20. AssemblyReloader supports this btw in case it wasn't clear, so you can reload and debug your iterations. Most of its limitations are because I haven't needed that functionality and nobody's asked for it
  21. You seem to have a problem related to a PartTest contract that can't find the Part it was generated for. Deleting all the contracts in your persistent file should've fixed it. Are you sure you got them all?
  22. The editor isn't using pre-rendered icons; there's actually a separate layer where instances of the icon are being rendered (think of the way you're looking at actual pieces of candy in a vending machine). If you wanted to do things with them like rotate or play animations, you'd need to set up a camera and do something similar. If you just need a static image, I've adapted some code from another of my projects that should get you there: [KSPAddon(KSPAddon.Startup.SpaceCentre, false)] public class PartIconSnapshot : MonoBehaviour { private const int IconWidth = 256; private const int IconHeight = 256; private Rect _rect = new Rect(0f, 0f, 120f, 120f); private Texture2D _selectedIcon = new Texture2D(1, 1); private int _partIndex = 0; private void Start() { _rect.center = new Vector2(Screen.width * 0.5f, Screen.height * 0.5f); CreatePartIconForCurrentPart(); } private void OnGUI() { _rect = KSPUtil.ClampRectToScreen(GUILayout.Window(GetInstanceID(), _rect, DrawWindow, "PartIconSnapshot")); } private void DrawWindow(int winid) { GUILayout.BeginHorizontal(); { if (GUILayout.Button("<<")) PreviousIcon(); if (GUILayout.Button(">>")) NextIcon(); GUILayout.FlexibleSpace(); GUILayout.Label(PartLoader.LoadedPartsList[_partIndex].name, GUILayout.ExpandWidth(true)); } GUILayout.EndHorizontal(); var textureRect = GUILayoutUtility.GetRect(IconWidth, IconHeight, GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(false)); if (Event.current.type == EventType.Repaint) Graphics.DrawTexture(textureRect, _selectedIcon); GUI.DragWindow(); } private void PreviousIcon() { if (--_partIndex < 0) _partIndex = PartLoader.LoadedPartsList.Count - 1; CreatePartIconForCurrentPart(); } private void NextIcon() { if (++_partIndex >= PartLoader.LoadedPartsList.Count) _partIndex = 0; CreatePartIconForCurrentPart(); } private void CreatePartIconForCurrentPart() { CreatePartIconSnapshot(PartLoader.LoadedPartsList[_partIndex]); } private void CreatePartIconSnapshot(AvailablePart part) { if (part == null) throw new ArgumentNullException("part"); Destroy(_selectedIcon); _selectedIcon = PartIconGenerator.Create2D(part, IconWidth, IconHeight, Quaternion.AngleAxis(-15f, Vector3.right) * Quaternion.AngleAxis(-30f, Vector3.up), Color.clear); } } public static class PartIconGenerator { private const string IconHiddenTag = "Icon_Hidden"; private const string KerbalEvaSubstring = "kerbal"; private static readonly int GameObjectLayer = LayerMask.NameToLayer("PartsList_Icons"); // note to future: if creating icons inside editor, you might want to choose a different layer or translate the camera and object out of frame private static Camera CreateCamera(int pixelWidth, int pixelHeight, Color backgroundColor) { var camGo = new GameObject("PartIconGenerator.Camera", typeof (Camera)); var cam = camGo.camera; cam.enabled = false; cam.cullingMask = (1 << GameObjectLayer); cam.clearFlags = ~CameraClearFlags.Nothing; cam.nearClipPlane = 0.1f; cam.farClipPlane = 10f; cam.orthographic = true; cam.backgroundColor = backgroundColor; cam.aspect = pixelWidth / (float) pixelHeight; // Camera Size = x / ((( x / y ) * 2 ) * s ) cam.orthographicSize = pixelWidth / (((pixelWidth / (float) pixelHeight) * 2f) * pixelHeight); cam.pixelRect = new Rect(0f, 0f, pixelWidth, pixelHeight); return cam; } private static Light CreateLight() { var light = new GameObject("PartIconGenerator.Light").AddComponent<Light>(); light.type = LightType.Directional; light.color = XKCDColors.OffWhite; light.cullingMask = 1 << GameObjectLayer; light.intensity = 0.5f; return light; } private static GameObject CreateIcon(AvailablePart part) { // kerbalEVA doesn't seem to init at origin if we aren't explicit var go = Object.Instantiate(part.iconPrefab, Vector3.zero, Quaternion.identity) as GameObject; // The kerbals are initially facing along positive Z so we'll be looking at their backs if we don't // rotate them around if (part.name.StartsWith(KerbalEvaSubstring)) go.transform.rotation = Quaternion.AngleAxis(180f, Vector3.up); go.SetLayerRecursive(GameObjectLayer); go.SetActive(true); return go; } private static void AdjustScaleAndCameraPosition(GameObject icon, Camera camera) { // get size of prefab var bounds = CalculateBounds(icon); float sphereDiameter = Mathf.Max(bounds.size.x, bounds.size.y, bounds.size.z); // rescale to size 1 unit so that object will take up as much viewspace as possible in ortho cam (with ortho size = 0.5) var currentScale = icon.transform.localScale; float scaleFactor = 1f / sphereDiameter; icon.transform.localScale = currentScale * scaleFactor; icon.transform.position = -bounds.center * scaleFactor; camera.transform.position = Vector3.zero; // back out, else we'll be inside the model (which is scaled at 1 unit so this should be plenty) camera.transform.Translate(new Vector3(0f, 0f, -5f), Space.Self); camera.transform.LookAt(Vector3.zero, Vector3.up); } public static Texture2D Create2D(AvailablePart part, int width, int height, Quaternion orientation, Color backgroundColor) { var cam = CreateCamera(width, height, backgroundColor); var icon = CreateIcon(part); var light = CreateLight(); var texture = new Texture2D(width, height, TextureFormat.ARGB32, false); var rt = RenderTexture.GetTemporary(width, height, 24); var prevRt = RenderTexture.active; RenderTexture.active = rt; icon.transform.rotation = orientation * icon.transform.rotation; AdjustScaleAndCameraPosition(icon, cam); cam.targetTexture = rt; cam.pixelRect = new Rect(0f, 0f, width, height); // doc says this should be ignored but doesn't seem to be (?) -- rendered area very small once targetTexture is set cam.Render(); texture.ReadPixels(new Rect(0f, 0f, width, height), 0, 0, false); texture.Apply(); RenderTexture.active = prevRt; RenderTexture.ReleaseTemporary(rt); Object.DestroyImmediate(light); Object.DestroyImmediate(cam); Object.DestroyImmediate(icon); return texture; } private static Bounds CalculateBounds(GameObject go) { var renderers = go.GetComponentsInChildren<Renderer>(true).ToList(); if (renderers.Count == 0) return default(Bounds); var boundsList = new List<Bounds>(); renderers.ForEach(r => { if (r.tag == IconHiddenTag) return; if (r is SkinnedMeshRenderer) { var smr = r as SkinnedMeshRenderer; // the localBounds of the SkinnedMeshRenderer are initially large enough // to accomodate all animation frames; they're likely to be far off for // parts that do a lot of animation-related movement (like solar panels expanding) // // We can get correct mesh bounds by baking the current animation into a mesh // note: vertex positions in baked mesh are relative to smr.transform; any scaling // is already baked in var mesh = new Mesh(); smr.BakeMesh(mesh); // while the mesh bounds will now be correct, they don't consider orientation at all. // If a long part is oriented along the wrong axis in world space, the bounds we'd get // here could be very wrong. We need to come up with essentially the renderer bounds: // a bounding box in world space that encompasses the mesh var m = Matrix4x4.TRS(smr.transform.position, smr.transform.rotation, Vector3.one /* remember scale already factored in!*/); var vertices = mesh.vertices; var smrBounds = new Bounds(m.MultiplyPoint3x4(vertices[0]), Vector3.zero); for (int i = 1; i < vertices.Length; ++i) smrBounds.Encapsulate(m.MultiplyPoint3x4(vertices[i])); Object.Destroy(mesh); boundsList.Add(smrBounds); } else if (r is MeshRenderer) // note: there are ParticleRenderers, LineRenderers, and TrailRenderers { r.gameObject.GetComponent<MeshFilter>().sharedMesh.RecalculateBounds(); boundsList.Add(r.bounds); } }); Bounds bounds = boundsList[0]; boundsList.Skip(1).ToList().ForEach(b => bounds.Encapsulate(b)); return bounds; } } Note that doing it this way (ReadPixels into a texture) is quite slow, not to mention the mesh baking it does if the part has an animation so call it as rarely as possible
  23. They're commented in ScienceDefs.cfg: // situation bits: // SrfLanded = 1, // SrfSplashed = 2, // FlyingLow = 4, // FlyingHigh = 8, // InSpaceLow = 16, // InSpaceHigh = 32 It's a simple bitmask. Add the numbers for the flags you want to set and you're good to go. Also applies to the biome mask Edit: It's the ExperimentSituations enum if you're doing something programmatically
  24. @JPLRepoI noticed that ProgressTree has a Deploy and Stow method which traverses the tree and runs some callbacks that hook/unhook each ProgressNode listener from their respective events. You could skip the ProgressNode (CelestialBodyScience) you're accidentally tripping by playing a little GameEvent shell game inside your contract using those callbacks... of course, it's a bit ugly // TSTTelescopeContract private void FakeCallback(float f, ScienceSubject s, ProtoVessel p, bool b) { GameEvents.OnScienceRecieved.Remove(FakeCallback); } protected override void AwardCompletion() { base.AwardCompletion(); var bodyName = GetParameter<TSTTelescopeContractParam>().target.name; var targetBody = FlightGlobals.Bodies.FirstOrDefault(cb => bodyName == cb.name); if (targetBody == null) { Debug.LogWarning("Couldn't find CelestialBody with name " + bodyName + " that " + typeof(TSTTelescopeContractParam).Name + " specifies"); return; } var subtree = ProgressTracking.Instance.GetBodyTree(targetBody); if (subtree.science.Subtree.Count > 0) { Debug.LogWarning("Multiple science subtree nodes for " + bodyName + " -- investigate"); return; } subtree.science.OnStow(); // removes it from onScienceReceived GameEvents.OnScienceRecieved.Add(FakeCallback); // we need a fake callback because the one that triggered this method call was removed by // TSTScienceParam.OnUnregister. GameEvent callbacks are stored in a list and called in reverse order // so the subtree.science callback we're about to add would otherwise be the next called subtree.science.OnDeploy(); // adds to end of onScienceReceived }
  25. Looks like a bug in the stock logic (assuming 1.0.4's Part.OnDestroy is similar to the current version). Seems to be on the bug tracker already so nothing to be done except try and avoid causing it while testing
×
×
  • Create New...