Jump to content

The official unoffical "help a fellow plugin developer" thread


Recommended Posts

2 hours ago, DeltaDizzy said:

Does anyone know of a way to get an ExperienceTrait from the name of a trait?

Ugly, but I'm not aware of a better way.

            for (int index = 0; index < GameDatabase.Instance.ExperienceConfigs.Categories.Count; ++index)
            {
                if (traitStr == GameDatabase.Instance.ExperienceConfigs.Categories[index].Name)
                {
                    Type type = KerbalRoster.GetExperienceTraitType(traitStr) ?? typeof(ExperienceTrait);
                    return ExperienceTrait.Create(type, GameDatabase.Instance.ExperienceConfigs.Categories[index], null);
                }
            }
Link to comment
Share on other sites

Hey yall, new Kerbaler here.
I think I finally got the right versions of Mono/NET/Unity/PartTool installed...  did a wiki dll & part and it put HelloWorld in the kerbal F12 console. 
great

So many tuts & howtos are outdated,
COULD SOMEONE RECOMMEND a 1.6 small plugin mod that  I could look over the source, to learn how things are c# coded in KSP mods? 

Its so frustrating when you try researching how to do stuff,
and using tutorial code doesnt work because its several years & versions old.

Link to comment
Share on other sites

 

5 hours ago, Bimbie said:

Hey yall, new Kerbaler here.
I think I finally got the right versions of Mono/NET/Unity/PartTool installed...  did a wiki dll & part and it put HelloWorld in the kerbal F12 console. 
great

So many tuts & howtos are outdated,
COULD SOMEONE RECOMMEND a 1.6 small plugin mod that  I could look over the source, to learn how things are c# coded in KSP mods? 

Its so frustrating when you try researching how to do stuff,
and using tutorial code doesnt work because its several years & versions old.

I'm a new modder too, so I think I can give you the answer:  No.  KSP can be extended in so many wildly different ways that any one small mod would very probably not be relevant for anything you'd ever want to do.

KSP has poop for documentation, and the API's are all over the place.  You live and die by examples.  As an illustration of this point, I found people using 3 different API's for getting all the parts of a ship in the VAB.  The most obvious thing (if you just look at the API docs) actually returns not only the parts that are in the ship, but those that are just dragged off to the side.  You just gotta know.  And the means of getting parts in the editor are completely different than what you do in the Flight scene, and what you do in the flight scene varies depending on whether you're talking about loaded or unloaded ships.  There's no end of stuff like that.

There are things that make some sense after you've been at it for a while, but you never know where the next land mine will be.

What you need to do is get good at finding other mods that do similar things to what you want to do and reverse engineering them.  Get good at searching for API's on github.

And then come back here when you get totally stumped.  I never would have guessed that  'KerbalInstructor' was any kind of way to get a talking kerbal in my dialogs, but once Nightingale pointed the way, I was set to go.  I bet there's another way (something similar to how the Kerbals in the lower right of the screen are drawn), but if there's no example of that, I've got absolutely no shot at discovering it myself.

I would say that the tutorials I found in the sticky posts here are the forums have been helpful in getting a basic grounding.  I've not yet found one that was uselessly out of date, either.  If you haven't done it already, you should follow the post on how to set up the Unity debugger.  That's an immense help in mod development.

Link to comment
Share on other sites

  • 4 weeks later...

Does anyone know how to the the anomolies for the transitions between orbits in patchedConicsSolver's flight plan?

 

Edit: To add on, I am programming an external orbit viewer. I can draw individual orbits, but I am not sure where to get all the required data. patchedConicSolver.flightPlan only seems to have the orbits created by maneuver nodes, not by gravity assists and stuff. 

 

Edit 2: I have figured out most of the orbit stuff I need to know, but I still can't figure out how to get the anomalies for the orbit patches ends/starts. 

Edited by c4ooo
Link to comment
Share on other sites

I'm trying to create an experience where the player needs to go to a particular spot on a body in order to gather a certain resource.  Two ways to do that present themselves:

  1. Create waypoints, then create a special gathering module that will only work if it's near one of my waypoints.  Can anybody point me at good examples of how to create waypoints?
  2. Do what MKS does and create an actual asteroid-like craft.

#1 was my first thought - but I'd love to find a recent example of code doing a waypoint - both how to create them and how to sense that the ship is near one.

I stumbled on #2 while looking for a way to do #1.  It actually seems like it might be a lot of fun, but I'm worried that the resources will turn up on hillsides and start rolling away from the player as soon as they get near.

Link to comment
Share on other sites

10 hours ago, NermNermNerm said:

I'm trying to create an experience where the player needs to go to a particular spot on a body in order to gather a certain resource.  Two ways to do that present themselves:

  1. Create waypoints, then create a special gathering module that will only work if it's near one of my waypoints.  Can anybody point me at good examples of how to create waypoints?

Look how Waypoint Manager do it. It creates waypoints and tracks distance to them (among other things).

Link to comment
Share on other sites

  • 2 weeks later...

So, I'm trying to read a config node from a file but it fails for no apparent reason :/ I know that the code should work since I've used it for AVC already but it doesn't seem to run for MiniAVC. I've added "a few" debug lines to see whats going on:

public static void LoadConfig()
{
  Debug.Log("Mini-AVC: LoadConfig()");
  if (!File.Exists(configPath))
  {
    Debug.Log("Mini-AVC: Config not found");
    return;
  }

  ConfigNode LoadNodeFromFile = ConfigNode.Load(configPath);
  ConfigNode node = LoadNodeFromFile.GetNode("MINI-AVC");
  Debug.Log($"Mini-AVC: HasNode UPDATE_FREQUENCY: {node.HasNode("UPDATE_FREQUENCY")}");

  var te = node.GetNodes();
  foreach (var item in te)
  {
    var t = item.name;
    Debug.Log($"Mini-AVC: NodeName = {t}");
  }
  if(node.HasNode("UPDATE_FREQUENCY"))
  {
    try
    {
      ConfigNode _temp = new ConfigNode();
      _temp = node.GetNode("UPDATE_FREQUENCY");

      AvcInterval = Int32.Parse(_temp.GetValue("MinTimeBetweenAvcRuns"));
      Debug.Log($"Mini-AVC: HasValue AvcRunsNext: {_temp.HasValue("AvcRunsNext")}");
      if (_temp.HasValue("AvcRunsNext"))
      {
        NextRun = DateTime.Parse(_temp.GetValue("AvcRunsNext"));
        Debug.Log($"Mini-AVC: NextRun = {NextRun}");
      }                    
    }
    catch { }
  }
  ConfigLoaded = true;
  Debug.Log("Mini-AVC: Config loaded");
}

And I got this output:

Mini-AVC: LoadConfig()

Mini-AVC: HasNode UPDATE_FREQUENCY: False

Mini-AVC: NodeName = UPDATE_FREQUENCY

Mini-AVC: HasValue AvcRunsNext: False

Basically, I cannot get the node "UPDATE_FREQUENCY" but when I list every node inside of the "MINI-AVC" config node, it shows me there is one called "UPDATE_FREQUENCY"...

I've already tried to rewrite these lines by hand just to be sure there are no copy&paste issues. Someone got an idea what's going on there and how to solve this issue?

edit: well, the issue disappeared for the same reason it appeared: none o_O

Edited by 4x4cheesecake
Link to comment
Share on other sites

  • 2 weeks later...

So I'm trying to get some old code working for myself, however I'm brand new to Plugin development and may not have my Visual Studio set up properly.  I did get the example "Hello World" code to work in KSP, so that simple little thing is okay.

The code I'm trying to recompile for myself is found here 

As you can see, it's very old, so I'm not 100% sure if everything would work, but at the moment Vis Studio is only giving me 1 error and 2 warnings.

 

Error	CS1061	'Vessel' does not contain a definition for 'findLocalCenterOfMass' and no accessible extension method 'findLocalCenterOfMass' accepting a first argument of type 'Vessel' could be found (are you missing a using directive or an assembly reference?)

Warning	CS0618	'Vector3.Exclude(Vector3, Vector3)' is obsolete: 'Use Vector3.ProjectOnPlane instead.'

I can't find anything about FindLocalCenterOfMass or FindWorldCenterOfMass in the API documentation other than I should use it to get the center of mass....

Link to comment
Share on other sites

23 minutes ago, Benjamin Kerman said:

Looks like you're going to use [VesselName].localCoM to get the center of mass. 

https://kerbalspaceprogram.com/api/class_vessel.html#a7271d800ed0f4b07598f41d0026c15a5

We have results!  Just not correct results xD

bpMXUlB.pngX87EI9u.png

So it looks like it's gimbaling at the right angle, just not into the center of mass.  Unfortunately I'm not sure where it's getting the wrong rotation, or vector.

Link to comment
Share on other sites

So I've decided to rewrite the Gimbal Auto Trim code.  My current dilemma is I want to set the gimbal rotation into the center of mass, but everything I've tried so far isn't perfect.

 

So my module inherits from ModuleGimbal.  I've tried rotating the gimbaltransforms, but that produces no results.  I also did the following:

vector3 com;

com = this.vessel.localCoM;


for (int i = 0; i < this.initRots.Count(); i++)
{
       this.initRots[i] = Quaternion.FromToRotation(gimbalTransforms[i].localPosition, com);
}

However I think initRots is rotation relative to the gimbal transform.  I can get the new poodle engine to rotate at a correct angle, but the user would have to turn the part around in the VAB.  And the Main Sail engine gimbals perpendicular to the COM so I need a solution for all engines at any rotation. 

Should I get the FromToRotation from the Gimbal transform to the COM, and do something complicated to affect the initRots?  I'm not sure how that would look.

Link to comment
Share on other sites

I’m writing a new plugin mod, and encountered some strange behavior with respect to the KSPAddon attribute.
I have a workaround for it, but also wanted to see what other more experienced modders thought re: whether this is a bug of some sort that ought to be filed in the bugtracker.

It is my understanding that the boolean arg once in [KSPAddon(Startup startup, bool once)] is used to specify that the addon should only ever be started once, e.g.:

[KSPAddon(KSPAddon.Startup.MainMenu, true)]
public class MyAddon : MonoBehaviour
{ ... }

I am aware that this used to be [KSPAddon(Startup startup, bool Dont_Destroy)] and Dont_Destroy was bugged, but IIRC that was supposed to have been fixed a few versions ago.

So, I write an addon that should start only once, at the first moment that the game loads into the main menu.
In Awake() it sets itself up as a singleton and tells Unity not to destroy it between scene changes, so it’ll stay loaded.
I register for OnGameDatabaseLoaded to allow me to react to any modifications in the reloaded configs. onLevelWasLoaded is for debug/illustrative purposes.

Code:

[KSPAddon(KSPAddon.Startup.MainMenu, true)]
public class MyAddon : MonoBehaviour
{
    public static MyAddon Instance;

    private void Awake()
    {
        Debug.Log("[MyAddon] [DEBUG] MyAddon AWAKE");
        Instance = this;
        DontDestroyOnLoad(gameObject);

        GameEvents.OnGameDatabaseLoaded.Add(OnGameDatabaseLoadedHandler);
        GameEvents.onLevelWasLoaded.Add(OnLevelWasLoadedHandler);
        // ...
    }

    private void OnLevelWasLoadedHandler(GameScenes gs)
    {
        Debug.Log("[MyAddon] [DEBUG] MyAddon OnLevelWasLoadedHandler");
        // ...
    }
}

This behaves correctly most of the time. However, when the game database has been reloaded (either via the stock debug menu or module manager), KSPAddon's once is no longer respected. Or rather, KSP forgets whatever flag it used to keep track of whether the addon has already been created or not. At the next occurrence of main menu loading, a fresh instance of the addon will be created. Is this the expected / intended / intuitive behavior? I certainly got caught out by it.

Log snippets:

// < game start, loading screen >

...
[LOG 23:25:18.329] [HighLogic]: =========================== Scene Change : From LOADING to MAINMENU =====================
...
[LOG 23:25:19.451] [AddonLoader]: Instantiating addon 'MyAddon' from assembly 'MyAddon'
[LOG 23:25:19.456] [MyAddon] [DEBUG] MyAddon AWAKE
...
[LOG 23:25:21.710] [MyAddon] [DEBUG] MyAddon OnLevelWasLoadedHandler
...

// < reload game database via debug menu (Alt-F12) >

...
[LOG 23:34:16.000] [HighLogic]: =========================== Scene Change : From MAINMENU to CREDITS =====================
[LOG 23:34:17.038] [UIMasterController]: ShowUI
[LOG 23:34:17.670] [MyAddon] [DEBUG] MyAddon OnLevelWasLoadedHandler
[LOG 23:34:17.670] [ApplicationLauncher] OnSceneLoadedGUIReady: scene CREDITS ShouldBeVisible() False ShouldBeOnTop() False iIsPositionedAtTop True
[LOG 23:34:19.886] [UIMasterController]: HideUI
[LOG 23:34:19.887] [HighLogic]: =========================== Scene Change : From CREDITS to MAINMENU =====================
[LOG 23:34:20.828] [AddonLoader]: Instantiating addon 'MyAddon' from assembly 'MyAddon'
[LOG 23:34:20.829] [MyAddon] [DEBUG] MyAddon AWAKE
...
[LOG 23:34:21.613] [MyAddon] [DEBUG] MyAddon OnLevelWasLoadedHandler
[LOG 23:34:21.613] [MyAddon] [DEBUG] MyAddon OnLevelWasLoadedHandler
...

// < reload game database via module manager (Alt-F11) >

...
[LOG 23:37:22.978] [HighLogic]: =========================== Scene Change : From MAINMENU to CREDITS =====================
[LOG 23:37:23.946] [UIMasterController]: ShowUI
[LOG 23:37:24.474] [MyAddon] [DEBUG] MyAddon OnLevelWasLoadedHandler
[LOG 23:37:24.474] [MyAddon] [DEBUG] MyAddon OnLevelWasLoadedHandler
[LOG 23:37:24.474] [ApplicationLauncher] OnSceneLoadedGUIReady: scene CREDITS ShouldBeVisible() False ShouldBeOnTop() False iIsPositionedAtTop True
[LOG 23:37:25.477] [UIMasterController]: HideUI
[LOG 23:37:25.478] [HighLogic]: =========================== Scene Change : From CREDITS to MAINMENU =====================
[LOG 23:37:26.421] [AddonLoader]: Instantiating addon 'MyAddon' from assembly 'MyAddon'
[LOG 23:37:26.421] [MyAddon] [DEBUG] MyAddon AWAKE
...
[LOG 23:37:27.242] [MyAddon] [DEBUG] MyAddon OnLevelWasLoadedHandler
[LOG 23:37:27.242] [MyAddon] [DEBUG] MyAddon OnLevelWasLoadedHandler
[LOG 23:37:27.242] [MyAddon] [DEBUG] MyAddon OnLevelWasLoadedHandler
...

The workaround, of course, is to kill any subsequent instances of the addon when they surface:

private void Awake()
{
    Debug.Log("[MyAddon] [DEBUG] MyAddon AWAKE");
    if (Instance != null)
    {
        Debug.Log("[MyAddon] [DEBUG] kill me!");
        Destroy(gameObject);
        return;
    }
    Instance = this;
    DontDestroyOnLoad(gameObject);

    // ...
}

 

Edited by cakepie
Link to comment
Share on other sites

@cakepie I think it's just an unfortunate oversight.  When the game database is reloaded, it actually loads a new copy of the plugin and instantiates addons from it.  Really it shouldn't reload plugins at all, since it doesn't exactly work anyway, but I doubt Squad put too much thought into this code.

Link to comment
Share on other sites

  • 2 weeks later...

In my mod, I've got cases where I want to consume and create resources.  I've been using ResourceConverter.ProcessRecipe, but I believe that may not be the right API because (I'm pretty sure), it runs into trouble if the resources involved in the recipe are located in different stages.  E.g. LF in a tank in the booster stage, ore in an orbital stage (just for example).

I got my example from USI-LS, which uses this api for snack consumption.  It does something vastly different for production (and it's hard to unwind from local logistics).  Anybody know a better API (or a better way to call it) so that I don't have this trouble?  Or examples of mods that do this kind of thing and aren't broken in this way would be good too.

Link to comment
Share on other sites

  • 2 weeks later...

Been having difficulty trying to get programmatically generated UI canvas to work in ScreenSpaceCamera mode with the flight mode camera ( FlightCamera.fetch.mainCamera ).

I wrote some PartModule code that MM attaches to each kerbal, it generates a canvas for UI elements in OnStart and tracks kerbal's position on screen in LateUpdate.

Shows up properly if canvas is in ScreenSpaceOverlay mode. Silly prototype putting censor bars over kerbals:

Jcr4GpA.png

However I don't want my UI elements overlaid on objects that are closer, e.g.: the censor bars should be occluded behind the capsule in this shot

1SEk5QE.png

Therefore I should be using ScreenSpaceCamera mode rather than ScreenSpaceOverlay.

But when the canvas mode is ScreenSpaceCamera and set to use the flight camera (FlightCamera.fetch.mainCamera) my stuff doesn't show up.

If the canvas mode is set to ScreenSpaceCamera and set to use the UI camera ( UIMasterController.Instance.uiCamera ) stuff shows up but it isn't the behavior I want because of how KSP uses its multiple cameras to render separately then combine. It is pretty much same outcome as using ScreenSpaceOverlay.

 

Code snippets for both render modes here.

 

I have considered WorldSpace canvas and am sure I would prefer not to use that. I want to control pixel size of the UI elements, not have it sized by the camera's perspective depending on how far away it is. Also with a world space canvas, would need to do billboarding and repositioning of the canvas, messy stuff.

 

Wrote a bit of code that pulled info about the FightCameras, code snippet and output here. Pretty sure I've got the right camera, it works great for tracking the kerbal positions and based on the clipping distances is indeed the one responsible for the nearer portion of local space. Although its cullingMask excludes the UI layer 5, according to Unity documentation it should not matter:

Quote

Be aware that UI elements aren’t culled. Screen space canvas children do not respect the camera’s culling mask.

 

Things I have tried to no avail:

  • remove the positioning code in LateUpdate and just plonk the image in the middle of the canvas based on half of screen width/height.
  • put the canvas and image GameObjects in Default layer 0 instead of UI layer 5
  • modify the camera cullingMask to include UI layer

 

What might I have missed? Any insight appreciated.

 

Link to comment
Share on other sites

19 hours ago, cakepie said:

Wrote a bit of code that pulled info about the FightCameras, code snippet and output here. Pretty sure I've got the right camera, it works great for tracking the kerbal positions and based on the clipping distances is indeed the one responsible for the nearer portion of local space. Although its cullingMask excludes the UI layer 5, according to Unity documentation it should not matter:

It doesn't matter for the Canvas' children, but it does for the Canvas itself. Also, the Canvas appears to copy the camera's transform and rotation which means the position and orientation of the Image you're adding are relevant. Here's a fixed version:

private void Start()
{
    GameObject canvasGO = new GameObject("censorcanvas");
    canvasGO.layer = 0; // must be in camera's culling mask
    canvasGO.AddComponent<RectTransform>();
    canvas = canvasGO.AddComponent<Canvas>();
    canvas.renderMode = RenderMode.ScreenSpaceCamera;

    // UI elements do not show up if we use the local space camera
    Camera flightCam = FlightCamera.fetch.mainCamera;
    canvas.worldCamera = flightCam;
    canvas.planeDistance = flightCam.nearClipPlane + 0.5f;

    canvas.pixelPerfect = true;
    CanvasScaler canvasScalar = canvasGO.AddComponent<CanvasScaler>();
    canvasScalar.uiScaleMode = CanvasScaler.ScaleMode.ConstantPixelSize;
    canvasGO.AddComponent<GraphicRaycaster>();

    GameObject panelGO = new GameObject("censorbar");
    panelGO.layer = LayerMask.NameToLayer("UI");
    panel = panelGO.AddComponent<RectTransform>();
    panelGO.transform.SetParent(canvasGO.transform, false); // gets moved
    Image censorbar = panelGO.AddComponent<Image>();
    Texture2D tex = GameDatabase.Instance.GetTexture("Harm/harm", false);
    censorbar.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));
    panel.anchorMin = new Vector2(0, 0);
    panel.anchorMax = new Vector2(0, 0);
    panel.pivot = new Vector2(0.5f, 0.5f);
    panel.sizeDelta = new Vector2(tex.width, tex.height);
}

private void LateUpdate()
{

    // this is the nearer of the two local space cameras
    Camera cam = FlightCamera.fetch.mainCamera;

    // make image track kerbal position on screen
    Vector2 screenPoint = (Vector2)cam.WorldToScreenPoint(transform.position);
    panel.anchoredPosition = screenPoint;

    // eventual goal is to also put image just in front of kerbal so it gets occluded by other closer objects
    // canvas.planeDistance = Vector3.Dot((transform.position - cam.transform.position), cam.transform.forward) - 0.5;
    canvas.planeDistance = Vector3.Distance(transform.position, cam.transform.position);
}

 

Link to comment
Share on other sites

I'm writing a mod that replaces EPL's production chain...  Or maybe can replace it.  Honestly, I'm not really sure what people will want.  My mod requires a whole lot of kerbals to produce rocket parts and lots of production infrastructure to keep the kerbals all fed & such, but no infrastructure like you see with EPL, where you have to math out all the production chains and heat and whatnot, which, if you do all your math right and apply sufficient hardware to the problem, allows you to generate rocket parts quickly.

I figure that some folks, particularly those who've been using EPL for a long time and have a good grip on the math would prefer EPL's production chain, but other folks, who maybe find my mod's simple production chain and requirement for a big, balanced non-automated base more compelling will prefer mine.

Right now, I effectively support both because I left the recipe for building rockets with RocketParts as the main ingredient.  (My production chain ends in RocketParts too).

Right now, I lose either way:

  1. My core constituency are annoyed and confused because of all the "useless" EPL production parts
  2. EPL adherents are happy to have their old friend around, but maybe a little confused that there are two ways of achieving the same ends

I'd really rather give users a choice - either they get one way or the other.  But, since this involves parts getting deleted, this is a tricky thing to make into a setting.

What to do?

  1. Just carry on like it is and document the feature away?
  2. Maybe instead of deleting offending the parts, I could shunt them into a  "EL Legacy Production" category in the Editor?
  3. Let the presence or absence of a mod be the decider (e.g. if SimpleProduction is present, then let that nuke the EL production parts?)

None of those answers seem really satisfying.  Right now my leaning is towards 2, especially given SimpleProduction is not maintained (or at least it's last KSP version listed in CKAN is 1.3.1...)

Link to comment
Share on other sites

  • 3 weeks later...
19 minutes ago, NermNermNerm said:

Anybody know how to inject code into the "Recover Vessel" scenario?  I want to give some extra prestige rewards for certain cargos.

I would suggest registering for these events:

  • GameEvents.OnVesselRecoveryRequested 
  • GameEvents.onVesselRecovered
  • GameEvents.onVesselRecoveryProcessing
  • GameEvents.onVesselRecoveryProcessingComplete
  • GameEvents.onGUIRecoveryDialogSpawn

Confirm for yourself what order they are called and what each one does. There's a couple of them that give a handle on the ProtoVessel being recovered as well as the MissionRecoveryDialog so you may be able to do something there, update some values to reflect your extra rewards, maybe inject some additional UI into the dialog to provide feedback to player, all that jazz.

 

 

Link to comment
Share on other sites

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...