Jump to content

The official unoffical "help a fellow plugin developer" thread


Recommended Posts

How can I put variables in the partmodule in the .cfg file and then read them in the plugin partmodule?

If all you need is simple types, like string, or float, then you can just make them KSPFields. If you need to go more indepth, you need to use custom classes that implement IConfigNode. You can see my adventures learning this here:

http://forum.kerbalspaceprogram.com/threads/86815-Unable-to-read-part-cfg-data-when-part-is-added-to-vessel

Link to comment
Share on other sites

You will want to look at this post from Mu about the introduction of PartModule, ConfigNode, KSPField, etc.. basically all the things you want to do. That said be aware that the developers examples do not work! LOL I know it sucks but its still good reference for 90% of it then you have to fight the rest of the way. But between this post and the one I linked earlier, all the information should be there.

http://forum.kerbalspaceprogram.com/threads/7529-Plugin-Posting-Rules-And-Official-Documentation?p=156430&viewfull=1#post156430

Link to comment
Share on other sites

No clue if this is the correct place to post about this, but...

I've been looking for projects to work on, and am willing to help anyone with developing a plugin for their mod! Whether you want help on the workload, or aren't programming savvy... I'm just looking for things to work on xP

Link to comment
Share on other sites

hi. i'm working on a resource manager module. is there a way to add a resource display to the part's guiActive bubble? i have the module setup so you can add parts from the vessel to a list and select which resource types you want to manage. i want to have it display the resources it manages. (ideally i want to have it do resource transfers like any other two parts. but one step at a time.)

Link to comment
Share on other sites

Hi people, I'm trying to write a plugin that will perform some kind of action when a bool "lifespecimensFound" is true. I want the bool to have just a small chance of returning true each time the player lands the vessel, so I created a keyvaluepair list. Here it is:


void Update()
{
List<KeyValuePair<bool, double>> lifespecimensList = new List<KeyValuePair<bool, double>>();
lifespecimensList.Add(new KeyValuePair<bool, double>(true, 0.1));
lifespecimensList.Add(new KeyValuePair<bool, double>(false, 0.9));
System.Random r = new System.Random();
double lifespecimensChance = r.NextDouble();
double cumulative = 0.0;
for (int i = 0; i < lifespecimensList.Count; i++)
{
cumulative += lifespecimensList[i].Value;
if (lifespecimensChance < cumulative)
{
lifespecimensFound = lifespecimensList[i].Key;
break;
}
}
if(newlandsite)
{

if(lifespecimensFound)
{
lifespecimensStatus = "Life Detected!";
}
else
{
lifespecimensStatus = "No Life Detected.";
}
}
else
{
unitStatus = "Must land at new site!";
}
}

However, as it in void Update(), the bool is constantly returning new values, but I want it to only return once (when a button is pressed via a KSPEvent, for example). But for some reason, when I try to do this, it returns no value at all. I'm really stumped. :huh: Thanks in advance for any advice!

Link to comment
Share on other sites

Well I tried a couple of things. One way I tried was like this:


if(testing && statusInt == 0)
{
List<KeyValuePair<bool, double>> lifespecimensList = new List<KeyValuePair<bool, double>>();
lifespecimensList.Add(new KeyValuePair<bool, double>(true, 0.1));
lifespecimensList.Add(new KeyValuePair<bool, double>(false, 0.9));
System.Random r = new System.Random();
double lifespecimensChance = r.NextDouble();
double cumulative = 0.0;
for (int i = 0; i < lifespecimensList.Count; i++)
{
cumulative += lifespecimensList[i].Value;
if (lifespecimensChance < cumulative)
{
lifespecimensFound = lifespecimensList[i].Key;
break;
}
}
if(newlandsite)
{

if(lifespecimensFound)
{
lifespecimensStatus = "Life Detected!";
statusInt = 2;
}
else
{
lifespecimensStatus = "No Life Detected.";
statusInt = 1;
}
}
else
{
unitStatus = "Must land at new site!";
statusInt = 0;
}

With these KSPFields and KSPEvent so that it knows which value of the lifespecimensStatus string is returned and so in theory doesn't repeat the action but again, it doesn't do anything:



[KSPField(guiActive = false, isPersistant = true)]
public int statusInt = 0;

[KSPField(guiActive = false, isPersistant = true)]
public bool testing = false;

[KSPEvent(guiActive = true, guiName = "", active = true)]
public void Activate()
{
Events["Activate"].active = false;
Events["Deactivate"].active = true;
testing = true;
}

Link to comment
Share on other sites

Nor would it work with a simpler code with the list in another bool I made: "unitActive" I'll just give all the code in case I've done something stupid somewhere else in the code.


using UnityEngine;
using System;
using System.Collections.Generic;

namespace RUILifeTech005
{
public class RUILifeTech005 : PartModule
{
[KSPField(guiActive = false, isPersistant = true)]
public bool newlandsite;

[KSPField(guiActive = false, isPersistant = true)]
public bool lifespecimensFound;

[KSPField(guiActive = false, isPersistant = true)]
public bool unitActive = false;

[KSPField(guiActive = true, guiName = "", isPersistant = true)]
public string lifespecimensStatus;

[KSPField(guiActive = true, guiName = "", isPersistant = true)]
public string unitStatus;

[KSPEvent(guiActive = true, guiName = "", active = true)]
public void Activate()
{
Events["Activate"].active = false;
Events["Deactivate"].active = true;
unitActive = true;
}

void Update()
{
switch(this.vessel.checkLanded())
{
case true :
newlandsite = true;
break;
case false :
newlandsite = false;
break;
}

if(unitActive)
{
List<KeyValuePair<bool, double>> lifespecimensList = new List<KeyValuePair<bool, double>>();
lifespecimensList.Add(new KeyValuePair<bool, double>(true, 0.1));
lifespecimensList.Add(new KeyValuePair<bool, double>(false, 0.9));
System.Random r = new System.Random();
double lifespecimensChance = r.NextDouble();
double cumulative = 0.0;
for (int i = 0; i < lifespecimensList.Count; i++)
{
cumulative += lifespecimensList[i].Value;
if (lifespecimensChance < cumulative)
{
lifespecimensFound = lifespecimensList[i].Key;
break;
}
}
if(newlandsite)
{

if(lifespecimensFound)
{
lifespecimensStatus = "Life Detected!";
}
else
{
lifespecimensStatus = "No Life Detected.";
}
}
else
{
unitStatus = "Must land at new site!";
}
}
}
}
}

Link to comment
Share on other sites

Does anyone know how I would go about defining a science experiment via a plugin. I know that usually, one would use a science cfg file but I was wondering if I'd be able to do the same thing via a KSPEvent button in a plugin?

Link to comment
Share on other sites

Hi.

I'm trying to use triggers in my mod/plugin to detect the parts of a vessel, or even just the vessel, as they move through an area. I've set up the collider I'm using as istriggered and set up my OnTriggerEnter(Collider). Unfortunately it only seems to recognize a few cetain parts (RCS blocks, landing legs and EVAkerbals) but nothing much else. Anyone know how to solve this? I've attempted adding rigidbodies and colliders to all loaded parts in the area but this has had limited success and I have no idea how to set these up so that they don't mess up the physics of the part/vessel, but still get recognized by the trigger.

If I can't get this to work I'll give up trying to use triggers and use a calulation based on the position vector of the parts and vessels.

I'd appreciate any help or suggestions,

Thanks.

Link to comment
Share on other sites

Hi, can anyone help me with how to use Part.AddModule()? I'm trying to add a new ScienceExperiment module to a part on a KSPEvent button press. I tried this:


Part.AddModule("lifetech", ModuleScienceExperiment);

but it didn't work. The API I looked at says it should be Part.AddModule(string partModule) but not sure what I'm doing wrong. Any help? Thanks

Link to comment
Share on other sites

Okay, config nodes time.

In a scenario module, I have the following:

public override void OnSave(ConfigNode node)
{
if (HighLogic.LoadedSceneIsEditor)
{
if (!node.nodes.Contains("EDITOR"))
{
node.AddNode("EDITOR");
}
AGXEditorNode = node.GetNode("EDITOR");
node.SetNode("EDITOR",EditorSave(AGXEditorNode));
}
}

and then EditorSave() is:

 public ConfigNode EditorSave(ConfigNode AGXEditorNode)
{
if (!AGXEditorNode.nodes.Contains(EditorLogic.fetch.shipNameField.Text))
{
AGXEditorNode.AddNode(EditorLogic.fetch.shipNameField.Text);
}
ConfigNode thisVsl = new ConfigNode();
thisVsl.AddValue("AGXNames","namesTest");
thisVsl.AddValue("AGXKeySet", "KeysetTest");

foreach (Part p in EditorLogic.SortedShipList)
{
if(!thisVsl.HasNode(p.name.Remove(p.name.Length - 8)))
{
thisVsl.AddNode(p.name.Remove(p.name.Length - 8));
}
ConfigNode partTemp = new ConfigNode();
partTemp.AddValue("relLoc",(p.transform.position-EditorLogic.startPod.transform.position).ToString());
thisVsl.SetNode(p.name.Remove(p.name.Length - 8), partTemp);
}
AGXEditorNode.SetNode(EditorLogic.fetch.shipNameField.Text, thisVsl);
return AGXEditorNode;
}

and I expect the following (actual strings replaced with the calculation values from the previous code block):

SCENARIO
{
name = AGextScenario
scene = 7, 6, 9
EDITOR
{
EditorLogic.fetch.shipNameField.Text //new module, using the ship name
{
AGXNames = namesTest
AGXKeySet = KeysetTest
p.name.Remove(p.name.Length - 8) //new confignode level, partname (less the (CLONE) added in the editor)
{
relLoc = Vector3 numbers as string
}
//repeat for as many parts on the vessel
}
}

Instead I get:

SCENARIO
{
name = AGextScenario
scene = 7, 6, 9
EDITOR
{
}
}

So the code in the actual OnSave method is running, but my call to EditorSave does not seem to process. There are no errors thrown to the log, can anyone point out to me what I am doing wrong? Can I not call ConfigNodes to other methods like this or something?

D.

Link to comment
Share on other sites

AGXEditorNode.AddNode(EditorLogic.fetch.shipNameField.Text);

You need to be very careful when doing that. Config node names and key names do not allow all characters. You may instead want to go for this structure:


EDITOR
{
ship // fixed node name
{
name = <ship name>
}
}

(Of course, the values also don't allow all characters, most notably '/'.)

The same goes for your part sub-node's name.

Link to comment
Share on other sites

A method with that signature does not exist. You either need to provide the part module name, or a config node that has it (along with other module parameters you want.)

Well, first I found it was in fact part rather than Part. So I now have this:


public class RUILifeTech005 : PartModule
{
[KSPEvent(guiActive = true, guiName = "Activate", active = true)]
public void Activate()
{
Events["Activate"].active = false;
Events["Deactivate"].active = true;
part.AddModule("lifetech");
}
}

So, then your saying I should make another class within the plugin like:


public class lifetech : ModuleScienceExperiment
{

}

So then I somehow need to define the experiment id, title, baseValue, etc? Any idea how I'd do that? I know it sounds kinda bad asking for code details but I've only made ScienceExperiment modules in a cfg file, which I don't want to do with this mod. Thanks.

Link to comment
Share on other sites

Okay, I must be missing something fundamental to how confignodes are supposed to work.

In my scenario module:

public class AGextScenario : ScenarioModule //this runs on flight scene start
{
public ConfigNode AGXBaseNode = new ConfigNode(); //initialize the three configNodes I am using
public ConfigNode AGXFlightNode = new ConfigNode();
public ConfigNode AGXEditorNode = new ConfigNode();


public void Start()
{
try
{
if(File.Exists(new DirectoryInfo(KSPUtil.ApplicationRootPath).FullName + "saves/" + HighLogic.SaveFolder + "/AGExt.cfg")) //does AGExt.cfg exist for this save game? It saves to the same folder as persistent.sfs
{
AGXBaseNode = ConfigNode.Load(new DirectoryInfo(KSPUtil.ApplicationRootPath).FullName + "saves/" + HighLogic.SaveFolder + "/AGExt.cfg"); //file exists, load it
print("AGX ConfigNode Load Okay!");
}
else
{
print("AGX ConfigNode not found, creating.....");
AGXBaseNode.AddNode("FLIGHT");
AGXBaseNode.AddNode("EDITOR");
AGXBaseNode.Save(new DirectoryInfo(KSPUtil.ApplicationRootPath).FullName + "saves/" + HighLogic.SaveFolder + "/AGExt.cfg"); //file does not exist, create it with empty FLIGHT and EDITOR nodes
}
AGXFlightNode = AGXBaseNode.GetNode("FLIGHT"); //load FLIGHT and EDITOR nodes
AGXEditorNode = AGXBaseNode.GetNode("EDITOR");
}
catch (Exception e)
{
print("AGXScenStart Fail " + errLine + " " + e);
}
}

This works. If AGExt.cfg does not exist in the save folder, it creates it with a FLIGHT and EDITOR node that are empty. It then loads the AGXFlightNode and AGXEditorNode configNodes from the file. (They will be empty nodes if I just created the AGExt.cfg file.)

However, this appears to do nothing: (still in the scenario module)


public override void OnSave(ConfigNode node)
{
print("AGXScenSaving"); //this prints to the log so the method excutes
if (HighLogic.LoadedSceneIsEditor) //this code can only run in the editor, will be different for flight mode
{
try
{
string hashedShipName = EditorHashShipName(EditorLogic.fetch.shipNameField.Text); //as per my previous post, I hash the shipname to a string of numbers so I'm guaranteed to not have any illegal characters
//I have a print() in the EditorHashShipName method that prints the number so I know this is working.
if (!AGXEditorNode.nodes.Contains(hashedShipName))
{
AGXEditorNode.AddNode(hashedShipName); //make the node for the vessel, using the hashed ship name as node name
}
ConfigNode thisVsl = new ConfigNode(); //new config node for the vessel
thisVsl.AddValue("AGXNames", "namesTest"); //add my test values
thisVsl.AddValue("AGXKeySet", "KeysetTest");
AGXEditorNode.SetNode(hashedShipName, thisVsl); //save vessel config node as sub-node of the EDITOR node
AGXBaseNode.SetNode("EDITOR", AGXEditorNode); //save the modified EDITOR node to the root node of the AGExt.cfg file
AGXBaseNode.Save(new DirectoryInfo(KSPUtil.ApplicationRootPath).FullName + "saves/" + HighLogic.SaveFolder + "/AGExt.cfg"); //the actual write command to the disk
}
catch (Exception e)
{
print("AGXScen EditorSave Error " + errLine + " " + e);
}
}
}

I am expecting this to add a sub-node in the EDITOR node in my AGExt.cfg file with a node name as a string of numbers and two test values, but it does nothing that I can see. Notably however, there are no errors in the log so I'm not sure where things are going wrong.

I'm sure it is something simple, but where is my assumptions about how ConfigNodes work wrong?

D.

Edited by Diazo
Link to comment
Share on other sites

Diazo, nothing in particular jumps out at me in your code. However, I don't use node.nodes.Contains(), or node.SetNode(). Can you retry using node.HasNode() and simply .AddNode() in combination with removal for replacing an existing node? This always worked for me.

https://github.com/blizzy78/ksp_toolbar/blob/master/Toolbar/Internal/Extensions.cs#L61-L88

Link to comment
Share on other sites

Alright, I'll try that.

I'm out of other ideas at this point, I spent all my time last night on this one issue and I'm starting to wonder if I'm using using confignodes correctly.

For the record, I'm trying to make an AGExt.cfg file in the same directory as your persistance.sfs file that has this format:


FLIGHT
{
Vessel1
{
Part1
{
Action1
{
//data
}
Action2
{
//data
}
}
Part2
{
Action1
{
//data
}
}
}
}

Confignodes can do this correct? When I open the persistent.sfs file I see that there are nested config nodes this deep so I assume I can do it also?

D.

Link to comment
Share on other sites

Hello, I have a question about ScenarioModule.

Is it possible to instantiate a class based on ScenarioModule? I tried it may ways around and it still gets destroyed every time the scene changes :( I'm using construction like:


[KSPScenario(ScenarioCreationOptions.AddToNewCareerGames, new GameScenes[] { GameScenes.FLIGHT, GameScenes.SPACECENTER, GameScenes.SPH, GameScenes.EDITOR })]
public partial class MyClass : ScenarioModule

I even use DontDestroyOnLoad(this) in my OnAwake() method but I still get all static class data wiped at the scene change.

So is it possible at all to get an instance of the ScenarioModule based plugin? Or I should rewrite all that I have to KSPAddon format? I started to use ScenarioModule since it have easy save/load functionality but I didn't think it wouldn't be able to save static data between scenes :(

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