![](https://forum.kerbalspaceprogram.com/uploads/set_resources_17/84c1e40ea0e759e3f1505eb1788ddf3c_pattern.png)
![](https://forum.kerbalspaceprogram.com/uploads/set_resources_17/84c1e40ea0e759e3f1505eb1788ddf3c_default_photo.png)
xEvilReeperx
Members-
Posts
894 -
Joined
-
Last visited
Content Type
Profiles
Forums
Developer Articles
KSP2 Release Notes
Everything posted by xEvilReeperx
-
Sorry, I didn't mean to imply anything sinister. Text can make things sound different than what was meant. Getting users to read instructions can be challenging as I'm sure you know Well, the actual meat of the mod is little more than a few handfuls of code. Loop through the various loaded assemblies, get their versions, and then use whatever you're using to serialize it and send it out. I figured since the error reporting would be so similar and related that it'd just make sense to roll it in there too rather than to make an entire second framework where most of that code is going to end up duplicated. "Error reporting" and "usage statistics" are closely related in my mind; that's typically what tools like this do. But you're right, it's your mod and I'm sorry That would be error reporting, apparently a separate and unrelated mod...? This is exactly the kind of thing I'm trying to talk you into adding, so I don't understand if this was sarcastic or what. I'll stay out of your thread if my feedback is unwanted
-
How do you figure? No personal information is collected (and if it is, this could arguably be called spyware) so you couldn't filter out one-time reports. With all the reports being mixed together it would look like people getting tired of your mod as it decreased day by day, not a serious gamebreaking error that you'd missed that's causing users to rage-delete your mod folder after it crashes KSP I can picture a twinkle in your eye as you write that. We both know the average user. Download tracking can and does get you this information, if you have an accurate counter. Writing a plugin like this but saying that the most obvious, useful function that plugin could do is somehow not in its scope is silly and makes the real reason obvious. My opinion is that you need to consider some way to make it useful to the user. If it were me, I'd add a button somewhere logical (options?) that lets a user type an anonymous short message with some tags and an option to attach their compress verbose game log (personally identifying info stripped out). That would make it very useful for every modder everywhere and users wouldn't have to go searching for correct log files or posting the wrong ones and so on. That would make this a slam dunk
-
It would work exactly the opposite the way you think. If 90% of your users were having problems with your mod, most of them will uninstall it and you'll just see an apparently small percentage of people that use your mod with FAR leading you to the wrong conclusion: not many people use my mod with FAR, therefore it's not a big issue. You'll hopefully be reading bug reports and comments you get from your users but if you used the statistics to determine how widespread the bug is as you say, it might lead you to the wrong conclusion again.
-
Assess how? With no error reporting, all you'd be able to tell for certain is that your mod likely works with certain other mods because your user didn't uninstall it due to a conflict. That doesn't help you write better mods. That helps you pander to a larger download count. That only matters if you're the type that wanders around bragging about download counts It might be handy, but is that a reason to distribute this statistics plugin with your own mod? If it's going to be included in Kethane and KAS, surely that'll be a plenty large enough sample? There's no incentive for you to distribute it yourself and you don't have to worry about users who might complain about being what is at this point a plugin specifically designed to spy on their installations
-
Your WIP post made it clearer what you were after. Refer to Unity GUI for related documentation. This should get you off the ground: Rect windowRect = new Rect(120f, 120f, 300f, 500f); Vector2 scrollPos = Vector2.zero; void OnGUI() { windowRect = GUI.Window(12345, windowRect, MyWindowFunction, "Test Window"); } void MyWindowFunction(int winid) { GUI.skin = HighLogic.Skin; GUILayout.BeginVertical(GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true)); // center label GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true)); GUILayout.FlexibleSpace(); GUILayout.Label("Available Targets"); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); // if there are a lot of vessels, it's going to make the window // stupid huge so let's make more efficient use of the space with // a scrolling section scrollPos = GUILayout.BeginScrollView(scrollPos); foreach (var vessel in FlightGlobals.Vessels) if (GUILayout.Button(vessel.name)) { ScreenMessages.PostScreenMessage(string.Format("Setting target to {0}", vessel.name), 3f, ScreenMessageStyle.UPPER_CENTER); FlightGlobals.fetch.SetVesselTarget(vessel); } GUILayout.EndScrollView(); GUILayout.EndVertical(); GUI.DragWindow(); }
-
[WIP] FTL: Faster Than Light - Kestrel Cruiser
xEvilReeperx replied to Cooly568's topic in KSP1 Mod Development
Just for your guys' reference, the 78-part stock Kerbal-X on the launchpad has 31k vertices 57k faces So keeping face and vertex limits to the basic part level is kind of silly unless these are going to be mini-Kestrels you link together to pull your rocket like it was Santa's sled -
Asteroid Spawn API?
xEvilReeperx replied to Starstrider42's topic in KSP1 C# Plugin Development Help and Support
Those urls lead to ProceduralAsteroid objects loaded with Resource.Load. Unfortunately, I couldn't find any way to edit asset bundles at runtime and couldn't get Resource.Load to load anything that didn't exist when the bundles were created so that's a bit of a dead end. On the other hand, modifying the actual mesh and/or texture is fairly easy. Here's a dump of an asteroid vessel: [LOG 02:15:26.881] Transform: PotatoRoid (Ast. QFG-283) [LOG 02:15:26.882] ---> Transform: model [LOG 02:15:26.882] ------> Transform: Asteroid [LOG 02:15:26.883] ---------> Transform: Visual [LOG 02:15:26.883] ---------> Transform: Collider [LOG 02:15:26.884] ---------> Transform: ColliderConvex [LOG 02:15:26.885] PotatoRoid (Ast. QFG-283) has components: [LOG 02:15:26.886] ...c: UnityEngine.Transform [LOG 02:15:26.886] ...c: Vessel [LOG 02:15:26.887] ...c: OrbitDriver [LOG 02:15:26.887] ...c: OrbitRenderer [LOG 02:15:26.888] ...c: FlightIntegrator [LOG 02:15:26.888] ...c: Part [LOG 02:15:26.889] ...c: ModuleAsteroid [LOG 02:15:26.890] ...c: UnityEngine.AudioSource [LOG 02:15:26.891] ...c: UnityEngine.Rigidbody [LOG 02:15:26.891] ...c: CollisionEnhancer [LOG 02:15:26.892] ...c: PartBuoyancy [LOG 02:15:26.892] --->model has components: [LOG 02:15:26.893] ......c: UnityEngine.Transform [LOG 02:15:26.893] ------>Asteroid has components: [LOG 02:15:26.894] .........c: UnityEngine.Transform [LOG 02:15:26.895] .........c: PAsteroid [LOG 02:15:26.895] --------->Visual has components: [LOG 02:15:26.896] ............c: UnityEngine.Transform [LOG 02:15:26.896] ............c: UnityEngine.MeshFilter [LOG 02:15:26.897] ............c: UnityEngine.MeshRenderer [LOG 02:15:26.898] --------->Collider has components: [LOG 02:15:26.898] ............c: UnityEngine.Transform [LOG 02:15:26.899] ............c: UnityEngine.MeshCollider [LOG 02:15:26.900] --------->ColliderConvex has components: [LOG 02:15:26.901] ............c: UnityEngine.Transform { for (int i = 0; i < FlightGlobals.Vessels.Count; ++i) { Vessel target = FlightGlobals.Vessels[i]; if (target.packed || target.rootPart.FindModulesImplementing<ModuleAsteroid>().Count == 0) continue; Log.Write("Working with {0}", target.name); Part root = target.rootPart; //root.transform.PrintHierarchy(); //root.gameObject.PrintComponents(); var newTex = new Texture2D(32, 32); var pixels = newTex.GetPixels32(); for (int p = 0; p < pixels.Length; ++p) pixels[p] = new Color32((byte)UnityEngine.Random.Range(0, 255), (byte)UnityEngine.Random.Range(0, 255), (byte)UnityEngine.Random.Range(0, 255), 255); newTex.SetPixels32(pixels, 0); newTex.Apply(); Transform visual = root.gameObject.transform.Find("model/Asteroid/Visual"); Transform collider = root.gameObject.transform.Find("model/Asteroid/Collider"); // change mesh var cubeGo = GameObject.CreatePrimitive(PrimitiveType.Cube); MeshFilter asteroidMf = visual.gameObject.GetComponent<MeshFilter>(); if (asteroidMf == null) Log.Error("null asteroid meshfilter"); asteroidMf.sharedMesh = cubeGo.GetComponent<MeshFilter>().sharedMesh; asteroidMf.gameObject.transform.localScale = Vector3.one * 10f; // default is a little low GameObject.DestroyImmediate(cubeGo); // change texture var mat = new Material(Shader.Find("KSP/Emissive/Diffuse")); mat.SetColor("_EmissiveColor", Color.blue); mat.mainTexture = newTex; visual.renderer.material = mat; visual.renderer.castShadows = true; visual.renderer.receiveShadows = true; // modify collider MeshCollider coll = collider.gameObject.collider as MeshCollider; coll.sharedMesh = asteroidMf.sharedMesh; // be mindful of 255 tri limit collider.localScale = visual.localScale; } } } if (GUI.Button(new Rect(100, 400, 128, 32f), "Test asteroid")) As for size class ... That looks hardcoded I'm afraid You'd probably have to replace the scenario and ModuleAsteroid entirely -
It might be because KSP's camera logic is overriding your rotation changes. Try putting your changes into LateUpdate rather than Update
-
How do I use a custom font
xEvilReeperx replied to Xty's topic in KSP1 C# Plugin Development Help and Support
All signs point to using a custom font sheet image and doing the rendering yourself unfortunately -
Components can't exist on their own. They need to be part of a GameObject. You can just change your code to: var someGo = new GameObject("GUIDropDown", typeof(GUIDropDown)); // or var someGo = new GameObject(); someGo.AddComponent<GUIDropDown>(); Edit: You can just add it directly to your mod's existing gameObject instead of creating its own GameObject as well
-
MonoBehaviour.Update() (and almost all other MonoBehaviour functions) are actually called by Unity using some reflection magic. I don't know what's causing your issue, but keep in mind that your callback is one of many and any previous code reacting to OnFlyByWire is also capable of causing the variable to be null
-
Limiting science gain via ModuleManager config.
xEvilReeperx replied to Superfluous J's topic in KSP1 Mods Discussions
{ LandedDataValue = 1 SplashedDataValue = 1 FlyingLowDataValue = 1 FlyingHighDataValue = 1 InSpaceLowDataValue = 11 InSpaceHighDataValue = 2 RecoveredDataValue = 4 FlyingAltitude = 18000 SpaceAltitude = 1E+09 } Kerbin { LandedDataValue = 0.3 SplashedDataValue = 0.4 FlyingLowDataValue = 0.7 FlyingHighDataValue = 0.9 InSpaceLowDataValue = 1 InSpaceHighDataValue = 1.5 RecoveredDataValue = 1 FlyingAltitude = 18000 SpaceAltitude = 250000 } Mun { LandedDataValue = 4 SplashedDataValue = 1 FlyingLowDataValue = 1 FlyingHighDataValue = 1 InSpaceLowDataValue = 3 InSpaceHighDataValue = 2 RecoveredDataValue = 2 FlyingAltitude = 18000 SpaceAltitude = 60000 } Minmus { LandedDataValue = 5 SplashedDataValue = 1 FlyingLowDataValue = 1 FlyingHighDataValue = 1 InSpaceLowDataValue = 4 InSpaceHighDataValue = 2.5 RecoveredDataValue = 2.5 FlyingAltitude = 18000 SpaceAltitude = 30000 } Moho { LandedDataValue = 10 SplashedDataValue = 1 FlyingLowDataValue = 1 FlyingHighDataValue = 1 InSpaceLowDataValue = 8 InSpaceHighDataValue = 7 RecoveredDataValue = 7 FlyingAltitude = 18000 SpaceAltitude = 80000 } Eve { LandedDataValue = 8 SplashedDataValue = 8 FlyingLowDataValue = 6 FlyingHighDataValue = 6 InSpaceLowDataValue = 7 InSpaceHighDataValue = 5 RecoveredDataValue = 5 FlyingAltitude = 22000 SpaceAltitude = 400000 } Duna { LandedDataValue = 8 SplashedDataValue = 1 FlyingLowDataValue = 5 FlyingHighDataValue = 5 InSpaceLowDataValue = 7 InSpaceHighDataValue = 5 RecoveredDataValue = 5 FlyingAltitude = 12000 SpaceAltitude = 140000 } Ike { LandedDataValue = 8 SplashedDataValue = 1 FlyingLowDataValue = 1 FlyingHighDataValue = 1 InSpaceLowDataValue = 7 InSpaceHighDataValue = 5 RecoveredDataValue = 5 FlyingAltitude = 18000 SpaceAltitude = 50000 } Jool { LandedDataValue = 30 SplashedDataValue = 1 FlyingLowDataValue = 12 FlyingHighDataValue = 9 InSpaceLowDataValue = 7 InSpaceHighDataValue = 6 RecoveredDataValue = 6 FlyingAltitude = 120000 SpaceAltitude = 4000000 } Laythe { LandedDataValue = 14 SplashedDataValue = 12 FlyingLowDataValue = 11 FlyingHighDataValue = 10 InSpaceLowDataValue = 9 InSpaceHighDataValue = 8 RecoveredDataValue = 8 FlyingAltitude = 10000 SpaceAltitude = 200000 } Vall { LandedDataValue = 12 SplashedDataValue = 1 FlyingLowDataValue = 1 FlyingHighDataValue = 1 InSpaceLowDataValue = 9 InSpaceHighDataValue = 8 RecoveredDataValue = 8 FlyingAltitude = 18000 SpaceAltitude = 90000 } Bop { LandedDataValue = 12 SplashedDataValue = 1 FlyingLowDataValue = 1 FlyingHighDataValue = 1 InSpaceLowDataValue = 9 InSpaceHighDataValue = 8 RecoveredDataValue = 8 FlyingAltitude = 18000 SpaceAltitude = 25000 } Tylo { LandedDataValue = 12 SplashedDataValue = 1 FlyingLowDataValue = 1 FlyingHighDataValue = 1 InSpaceLowDataValue = 10 InSpaceHighDataValue = 8 RecoveredDataValue = 8 FlyingAltitude = 18000 SpaceAltitude = 250000 } Gilly { LandedDataValue = 9 SplashedDataValue = 1 FlyingLowDataValue = 1 FlyingHighDataValue = 1 InSpaceLowDataValue = 8 InSpaceHighDataValue = 6 RecoveredDataValue = 6 FlyingAltitude = 18000 SpaceAltitude = 6000 } Pol { LandedDataValue = 12 SplashedDataValue = 1 FlyingLowDataValue = 1 FlyingHighDataValue = 1 InSpaceLowDataValue = 9 InSpaceHighDataValue = 8 RecoveredDataValue = 8 FlyingAltitude = 18000 SpaceAltitude = 22000 } Dres { LandedDataValue = 8 SplashedDataValue = 1 FlyingLowDataValue = 1 FlyingHighDataValue = 1 InSpaceLowDataValue = 7 InSpaceHighDataValue = 6 RecoveredDataValue = 6 FlyingAltitude = 18000 SpaceAltitude = 25000 } Eeloo { LandedDataValue = 15 SplashedDataValue = 1 FlyingLowDataValue = 1 FlyingHighDataValue = 1 InSpaceLowDataValue = 12 InSpaceHighDataValue = 10 RecoveredDataValue = 10 FlyingAltitude = 18000 SpaceAltitude = 60000 } Sun If I interpreted your format correctly. If not, creating a new one should be speedy public class DumpScienceParams : MonoBehaviour { public void Start() { /* Body { //Body Name = Kerbin Body_Index = 1 LandedDataValue = 1 SplashedDataValue = 1 FlyingLowDataValue = 2 FlyingHighDataValue = 3 InSpaceLowDataValue = 1 InSpaceHighDataValue = 2 RecoveredDataValue = 1 FlyingAltitude = 18000 SpaceAltitude = 250000 } * */ var root = new ConfigNode(); foreach (var body in FlightGlobals.Bodies) { var node = root.AddNode(body.name); var sci = body.scienceValues; node.AddValue("LandedDataValue", sci.LandedDataValue); node.AddValue("SplashedDataValue", sci.SplashedDataValue); node.AddValue("FlyingLowDataValue", sci.FlyingLowDataValue); node.AddValue("FlyingHighDataValue", sci.FlyingHighDataValue); node.AddValue("InSpaceLowDataValue", sci.InSpaceLowDataValue); node.AddValue("InSpaceHighDataValue", sci.InSpaceHighDataValue); node.AddValue("RecoveredDataValue", sci.RecoveryValue); node.AddValue("FlyingAltitude", sci.flyingAltitudeThreshold); node.AddValue("SpaceAltitude", sci.spaceAltitudeThreshold); } root.Save("GameData/scienceValues.txt"); Log.Write("root = {0}", root.ToString()); } }[KSPAddon(KSPAddon.Startup.Flight, false)] -
Limiting science gain via ModuleManager config.
xEvilReeperx replied to Superfluous J's topic in KSP1 Mods Discussions
I came up with something the other day that might make this simpler. It allows you to specify multiple scenes to have your addon loaded in, so in this case instead of doing an "every" startup, you could just do something like: [KSPAddonImproved(KSPAddonImproved.Startup.Flight | KSPAddonImproved.Startup.SpaceCenter | KSPAddonImproved.Startup.TrackingStation, true)] public class EditScienceParams : MonoBehaviour { // etc } It might make sidestepping the autoloading plugin possibility very easy to solve -
Expanded KSPAddon modes
xEvilReeperx replied to Starstrider42's topic in KSP1 Suggestions & Development Discussion
You're right! It worked for the thing I was doing at the time, but due to dumb luck apparently. Strange choice by Squad. Maybe I can redeem myself. I came up with this (public domain, KSPAddonFixed by Majiir): [AttributeUsage(AttributeTargets.Class)] internal class KSPAddonImproved : Attribute { [Flags] public enum Startup { // KSPAddon.Startup values: /* Instantly = -2, EveryScene, EditorAny = -3, MainMenu = 2, Settings, SpaceCentre = 5, Credits = 4, EditorVAB = 6, EditorSPH = 9, Flight = 7, TrackingStation, PSystemSpawn = 10 */ None = 0, MainMenu = 1 << 0, Settings = 1 << 1, SpaceCenter = 1 << 2, Credits = 1 << 3, EditorVAB = 1 << 4, EditorSPH = 1 << 5, Flight = 1 << 6, TrackingStation = 1 << 7, PSystemSpawn = 1 << 8, Instantly = 1 << 9, EditorAny = EditorVAB | EditorSPH, TimeElapses = Flight | TrackingStation | SpaceCenter, RealTime = TimeElapses, EveryScene = ~0 } public bool runOnce; public Startup scenes; public KSPAddonImproved(Startup mask, bool once = false) { runOnce = once; scenes = mask; } } /// <summary> /// KSPAddon with equality checking using an additional type parameter. Fixes the issue where AddonLoader prevents multiple start-once addons with the same start scene. /// By Majiir /// </summary> public class KSPAddonFixed : KSPAddon, IEquatable<KSPAddonFixed> { private readonly Type type; public KSPAddonFixed(KSPAddon.Startup startup, bool once, Type type) : base(startup, once) { this.type = type; } public override bool Equals(object obj) { if (obj.GetType() != this.GetType()) { return false; } return Equals((KSPAddonFixed)obj); } public bool Equals(KSPAddonFixed other) { if (this.once != other.once) { return false; } if (this.startup != other.startup) { return false; } if (this.type != other.type) { return false; } return true; } public override int GetHashCode() { return this.startup.GetHashCode() ^ this.once.GetHashCode() ^ this.type.GetHashCode(); } } // note: this needs to be KSPAddonFixed; don't change it [KSPAddonFixed(KSPAddon.Startup.Instantly, true, typeof(CustomAddonLoader))] internal class CustomAddonLoader : MonoBehaviour { // What's improved? The KSPAddon.Startup is now a bitmask so you can // use logical operations to specify which scenes you want your addon // to be loaded in // master list to keep track of addons in our assembly List<AddonInfo> addons = new List<AddonInfo>(); private string _identifier; // Mainly required so we can flag addons when they've // been created in the case of runOnce = true class AddonInfo { public readonly Type type; public readonly KSPAddonImproved addon; public bool created; internal AddonInfo(Type t, KSPAddonImproved add) { type = t; created = false; addon = add; } internal bool RunOnce { get { return addon.runOnce; } } internal KSPAddonImproved.Startup Scenes { get { return addon.scenes; } } } void Awake() { DontDestroyOnLoad(this); // multiple plugins using this source will create their own instances // of the loader; the log can get confusing pretty fast without some // way of telling them apart _identifier = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + "." + GetType().ToString(); // examine our assembly for loaded types foreach (var ourType in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()) { var attr = ((KSPAddonImproved[])ourType.GetCustomAttributes(typeof(KSPAddonImproved), true)).SingleOrDefault(); if (attr != null) { Debug.Log(string.Format("Found KSPAddonImproved in {0}", ourType.FullName)); addons.Add(new AddonInfo(ourType, attr)); } } // special case here: since we're already in the first scene, // OnLevelWasLoaded won't be invoked so we need to fire off any // "instant" loading addons now OnLevelWasLoaded((int)GameScenes.LOADING); } void OnLevelWasLoaded(int level) { GameScenes scene = (GameScenes)level; KSPAddonImproved.Startup mask = 0; if (scene == GameScenes.LOADINGBUFFER) return; Debug.Log(string.Format("{1}: {0} was loaded; instantiating addons...", scene.ToString(), _identifier)); // Convert GameScenes => SceneMask switch (scene) { case GameScenes.EDITOR: mask = KSPAddonImproved.Startup.EditorVAB; break; case GameScenes.SPH: mask = KSPAddonImproved.Startup.EditorSPH; break; case GameScenes.CREDITS: mask = KSPAddonImproved.Startup.Credits; break; case GameScenes.FLIGHT: mask = KSPAddonImproved.Startup.Flight; break; case GameScenes.LOADING: mask = KSPAddonImproved.Startup.Instantly; break; case GameScenes.MAINMENU: mask = KSPAddonImproved.Startup.MainMenu; break; case GameScenes.SETTINGS: mask = KSPAddonImproved.Startup.Settings; break; case GameScenes.SPACECENTER: mask = KSPAddonImproved.Startup.SpaceCenter; break; case GameScenes.TRACKSTATION: mask = KSPAddonImproved.Startup.TrackingStation; break; case GameScenes.PSYSTEM: mask = KSPAddonImproved.Startup.PSystemSpawn; break; case GameScenes.LOADINGBUFFER: // intentionally left unset break; default: Debug.LogError(string.Format("{1} unrecognized scene: {0}", scene.ToString(), _identifier)); break; } int counter = 0; for (int i = 0; i < addons.Count; ++i) { var addon = addons[i]; if (addon.created && addon.RunOnce) continue; // this addon was already loaded // should this addon be initialized in current scene? if ((addon.Scenes & mask) != 0) { Debug.Log(string.Format("ImprovedAddonLoader: Creating addon '{0}'", addon.type.Name)); GameObject go = new GameObject(addon.type.Name); go.AddComponent(addon.type); addon.created = true; ++counter; } } Debug.Log(string.Format("{1} finished; created {0} addons", counter, _identifier)); } } It's basically a drop-in replacement for KSPAddon. Include the source and change your KSPAddons into KSPAddonImproveds //[KSPAddon(KSPAddon.Startup.Flight, false)] [KSPAddonImproved(KSPAddonImproved.Startup.RealTime | KSPAddonImproved.Startup.EditorAny /* and so on */, false)] public class SingleTest : MonoBehaviour { public void Start() { Log.Warning("Test has run"); } } It might do in the meantime -
Expanded KSPAddon modes
xEvilReeperx replied to Starstrider42's topic in KSP1 Suggestions & Development Discussion
Not necessary in my opinion. KSPAddon.Startup enum is treated as a mask, so you can just use logical operators to select the scenes you want: // Tracking station, space center, flight [KSPAddon(KSPAddon.Startup.TrackingStation | KSPAddon.Startup.SpaceCentre | KSPAddon.Startup.Flight, false)] -
One option is to stuff your extra information at the end of the subject id the way stock does (I borrowed crewReport here): // your experiment here, available through ModuleScienceExperiment var exp = ResearchAndDevelopment.GetExperiment("crewReport"); // when player deploys experiment: // generate an id for this experiment // another note: if you don't want vessel situation to matter, // just consistently provide an appropriate one (InSpaceHigh probably) var subject = ResearchAndDevelopment.GetExperimentSubject(exp, ScienceUtil.GetExperimentSituation(FlightGlobals.ActiveVessel), FlightGlobals.currentMainBody, "ClassC.SomeType"); subject.subjectValue = 1f; subject.scienceCap = 220f; // you can set this by class subject.dataScale = 1f; subject.title = "Sample of a Class C YourType asteroid"; // submit initial report: science value = 110 * 1 = 110 points ResearchAndDevelopment.Instance.SubmitScienceData(110f, subject); // stock game will reduce value for us automatically, but // it's overridden easily subject.scientificValue = .8f; // let's make this next report 20% less // note: R&D will store this value for us persistently // this report is now worth 20% less = 88 science ResearchAndDevelopment.Instance.SubmitScienceData(110f, subject); // test diminishing returns // third experiment will be worth 60% subject.scientificValue = .6f; You'd probably want to look for subjects for a given experiment for every body if you don't want actual location to matter, but that's trivial. You already do your own work with ScienceData so things should be quite straightforward
-
1 & 2 are due to the custom ModuleScienceExperiment he's using. The stock game has no concept of requirements other than a bitmask for vessel situation, a bitmask for when biomes matter and a flag for atmospheres. I'll ask him and see if he can make some functions available to me that would let ScienceAlert ignore the invalid ones. 3 sounds like a bug, will fix