Jump to content

The official unoffical "help a fellow plugin developer" thread


Recommended Posts

Hello, is there a way to use the GenericAppFrame used on the EngineersReport and on the ContractsApp for our mods which has applauncher's button? I've tried to use it but I haven't found how to do it without many errors from many variables used in the GenericAppFrame class.

Thanks ;)

Edited by Malah
Link to comment
Share on other sites

13 minutes ago, Malah said:

Hello, is there a way to use the GenericAppFrame used on the EngineersReport and on the ContractsApp for our mods which has applauncher's button? I've tried to use it but I haven't found how to do it without many errors from many variables used in the GenericAppFrame class.

Thanks ;)

Even if it is possible, there is little point trying to do any sort of complex interaction with stock UI code at this point because KSP 1.1 is just around the corner and the UI code has been totally overhauled.

Edited by Padishar
Link to comment
Share on other sites

1 hour ago, Padishar said:

Even if it is possible, there is little point trying to do any sort of complex interaction with stock UI code at this point because KSP 1.1 is just around the corner and the UI code has been totally overhauled.

Yes but I want to learn how to do it, perhaps that on the 1.1, I could use this new knowledge ;)

Link to comment
Share on other sites

  • 3 weeks later...

While retractable things (say, Solar panels) are moving, the "extend" and "retract" buttons are not visible. Once they stop moving, the appropriate button becomes displayed.

I know that buttons are KSPEvent() and I can make one that shows up or not. I even figured out how to enable and disable them (and change their text, in case that's needed). However, I can't seem to grasp how to change these states based on the status of the part.

Specifically, this is for All Y'All. I'm trying to make it so my buttons work the same way the stock ones do visually. So you only have one button "Extend all" and that only is there if the selected panel is retracted. When you click it, it goes away, the panel (and all panels) extends, then when it's done extending a "Retract all" button appears.

Link to comment
Share on other sites

void ExtendAll()
{
	// stuff
	StartCoroutine(waitForExtended());
}

System.Collections.IEnumerator waitForExtended()
{
	while (module.State != extended) // replace with actual state checks obviously... 
		yield return new WaitForSeconds(1.0); // check once per second whether the tate change has finished
	// set retract event visible
}

If it doesn't have an event, you can always just poll it until it is where you want it

Edited by Crzyrndm
Link to comment
Share on other sites

28 minutes ago, Crzyrndm said:

void ExtendAll()
{
	// stuff
	StartCoroutine(waitForExtended());
}

System.Collections.IEnumerator waitForExtended()
{
	while (module.State != extended) // replace with actual state checks obviously... 
		yield return new WaitForSeconds(1.0); // check once per second whether the tate change has finished
	// set retract event visible
}

If it doesn't have an event, you can always just poll it until it is where you want it

That actually makes sense to me. Thank you. I may need a little more help with the "module.state != extended" part as that looks like pseudocode, especially with the comment :)

I don't actually know how to check that state, either on the part or the module or what. I also don't know how to find out what it should be, or even the exact syntax. For fun I just tried typing "module" into VS (which is pretty helpful in at least letting you know you're on the right track) and it doesn't seem to be valid.

Link to comment
Share on other sites

Module is one of the things (Part Modules...) you just called extend on, probably the one on the same part as your module. Using solar panels as an example

// if your module is running alongside the solar panel module (extends PartModule)
// get a solar panel module to work on
ModuleDeployableSolarPanel solarPanel = part.Modules.OfType<ModuleDeployableSolarPanel>().FirstOrDefault();
if (solarPanel == null)
  return; // incase we don't find any (which is stupid, but w/e)

// wait for it to finish extending
while (solarPanel.panelState == ModuleDeployableSolarPanel.panelStates.Extending)
// blah blah blah...

Much easier if you are already running inside the solar panel though because the solarPanel reference above is the object running the code. Objects can access their own properties without all the fuss

// if your module extends ModuleDeployableSolarPanel
while (panelState == panelStates.Extending)
// blah

 

Edited by Crzyrndm
Link to comment
Share on other sites

17 minutes ago, Crzyrndm said:

Much easier if you are already running inside the solar panel though because the solarPanel reference above is the object running the code. Objects can access their own properties without all the fuss


// if your module extends ModuleDeployableSolarPanel
while (panelState == panelStates.Extending)
// blah

 

I think something like this should work for me as this will always be running in a Solar panel. However, it's still not liking the syntax for some reason. I think it's because I'm not actually modifying ModuleDepoloyableSolarPanel, but instead adding a new module. Among all the things I don't know is how to tell it I'm in a solar panel.

Here's the full code I have so far. I put the error as a comment on the line that the compiler's complaining about:

    public class AYA_Solar_Test : PartModule
    {
        public bool togglesolar = true; // may need changed to take into account already open panels
        [KSPEvent(guiActive = true, guiActiveEditor = false, guiName = "Extend All TEST")]
        public void ToggleAllSolar()
        {
            foreach (Part eachPart in vessel.Parts)
            {
                var panel = eachPart.FindModuleImplementing<ModuleDeployableSolarPanel>();
                if (togglesolar) if (panel != null) panel.Extend();
                if (!togglesolar) if (panel != null) panel.Retract();
            }
            Events["ToggleAllSolar"].guiActive = false;
            while (panelState == panelstates.Extending) // CS0103 The name 'panelState' does not exist in th current context (Same with panelStates)
            {
                // do stuff
            }
            if (togglesolar) Events["ToggleAllSolar"].guiName = "Retract All TEST";
            if (!togglesolar) Events["ToggleAllSolar"].guiName = "Extend All TEST";
            togglesolar = !togglesolar;
        }
    }

 

Link to comment
Share on other sites

1) You need to use the first method. Your class is not a solar panel, it operates on objects that are solar panels
2) The "StartCoroutine" and second function in my first answer is extremely important. The structure you have there will freeze the game indefinitely because KSP can't do anything while you're in that while loop. When a Coroutine "yields", the function gets paused and lets KSP continue until some condition is met (the 1 second time period in my example) and then it unpauses the function again (repeat until it reaches the end of the function).

Edited by Crzyrndm
Link to comment
Share on other sites

Just now, Crzyrndm said:

1) You need to use the first method. Your class is not a solar panel, it operates on objects that are solar panels
2) The "StartCoroutine" and second function in my first answer is extremely important. The structure you have there will freeze the game indefinitely because KSP can't do anything while you're in that while loop. When a Coroutine "yields", the function gets paused and lets KSP continue until some condition is met (the 1 second time period in my example) and then it unpauses the function again.

1) Aha. Am I right in thinking that that first method will just pick the first Solar Panel it finds? I don't mind that as a backup, but I'd prefer it pick the actual part we're dealing with if possible. It should be a solar panel, I'm setting that in a modulemanager config.

2) Oops. I thought maybe I could simplify it :)

I'll fiddle a bit more and undoubtedly come up with something else that I can't figure out. As always, thank you very much.

Link to comment
Share on other sites

33 minutes ago, 5thHorseman said:

Am I right in thinking that that first method will just pick the first Solar Panel it finds?

Yes. I was just getting a reference to work with because I wasn't looking at how you'd done it. You can get that reference however you like


// inside the extend loop
// existing code
StartCoroutine(checkState(panel)); // one for each panel we're extending
// end loop

// and the check state function
IEnumerator checkState(ModuleDeployableSolarPanel panel)
{
	while (panel.panelState...)
// change button visibility on this panel
}

 

Edited by Crzyrndm
Link to comment
Share on other sites

Just now, Crzyrndm said:

Yes. I was just getting a reference to work with because I wasn't looking at how you'd done it


// inside the extend loop
// existing code
StartCoroutine(checkState(panel)); // one for each panel we're extending
// end loop

// and the check state function
IEnumerator checkState(ModuleDeployableSolarPanel panel)
{
// change button visibility
}

 

So wait, that would change the visibility of each button in all of the panels' right click menus? That's actually super cool and I didn't even know I wanted it to work that way until just now.

Okay one last thing and I'm (hopefully) out of your hair. VS is complaining that I have to return a value, but I don't even know what an IEnumerator is. I tried 1 and 0 but it says Int doesn't count.

Link to comment
Share on other sites

This is making my brain implode on itself. I really do thank you for this it's no end of help but I think I've reached my limit for the day. I'll go over all of this again after a good sleep and maybe a stiff drink. But probably not in that order.

Edited by 5thHorseman
Link to comment
Share on other sites

Okay as expected some liquidity and sleep has invigorated me anew, and I'm seeming to make progress. However I seem to still be missing some critical things, and would love some more help. @Crzyrndm, If you've said something above that I didn't implement below, don't think I'm ignoring you. I'm just .that. .lost. here.

Here's the code I have. It technically works but not the way I want. the text ("extend all" and "retract all") don't change and the buttons don't disappear during movement, but the panels DO all extend and retract:

    public class AYA_Solar_Test : PartModule
    {
        [KSPEvent(guiActive = true, guiActiveEditor = false, guiName = "UNINITIALIZED!")]
        public void ToggleAllSolar()
        {
            foreach (Part eachPart in vessel.Parts)
            {
                var panel = eachPart.FindModuleImplementing<ModuleDeployableSolarPanel>();
                if (panel != null)
                {
                    if (panel.panelState == ModuleDeployableSolarPanel.panelStates.RETRACTED) { panel.Extend(); }
                    if (panel.panelState == ModuleDeployableSolarPanel.panelStates.EXTENDED)  { panel.Retract(); }
                    StartCoroutine(waitForExtendRetractSolar(panel));
                }
            }
        }
        public System.Collections.IEnumerator waitForExtendRetractSolar(ModuleDeployableSolarPanel solarPanel)
        {
            solarPanel.Events["ToggleAllSolar"].guiActive = false;
            while (solarPanel.panelState == ModuleDeployableSolarPanel.panelStates.EXTENDING || solarPanel.panelState == ModuleDeployableSolarPanel.panelStates.RETRACTING)
            {
                yield return new WaitForSeconds(1);
            }
            if (solarPanel.panelState == ModuleDeployableSolarPanel.panelStates.RETRACTED) { solarPanel.Events["ToggleAllSolar"].guiName = "Extend All TEST"; }
            if (solarPanel.panelState == ModuleDeployableSolarPanel.panelStates.EXTENDED) { solarPanel.Events["ToggleAllSolar"].guiName = "Retract All TEST"; }
            solarPanel.Events["ToggleAllSolar"].guiActive = true;
            yield return new WaitForSeconds(0) ; // This is totally a kludge I have it in here because it doesn't error out. I'll happily fix it if someone tells me how.
        }
    }

Earlier, I had this code and it worked perfectly. The hide/reveal part worked, and the text changed from "extend" to "retract" as expected. However, it only did it on the panel I right-clicked on. The other panels extended and retracted, but their buttons stayed visible and their text didn't change. It's why I made the changes in the first code block:

    public class AYA_Solar_Test : PartModule
    {
        [KSPEvent(guiActive = true, guiActiveEditor = false, guiName = "Extend All TEST")]
        public void ToggleAllSolar()
        {
            foreach (Part eachPart in vessel.Parts)
            {
                var panel = eachPart.FindModuleImplementing<ModuleDeployableSolarPanel>();
                if (panel != null)
                {
                    if (panel.panelState == ModuleDeployableSolarPanel.panelStates.RETRACTED) { panel.Extend(); }
                    if (panel.panelState == ModuleDeployableSolarPanel.panelStates.EXTENDED)  { panel.Retract(); }
                    StartCoroutine(waitForExtendRetractSolar(panel));
                }
            }
        }
        System.Collections.IEnumerator waitForExtendRetractSolar(ModuleDeployableSolarPanel solarPanel)
        {
            Events["ToggleAllSolar"].guiActive = false;
            while (solarPanel.panelState == ModuleDeployableSolarPanel.panelStates.EXTENDING || solarPanel.panelState == ModuleDeployableSolarPanel.panelStates.RETRACTING)
            {
                yield return new WaitForSeconds(1);
            }
            if (solarPanel.panelState == ModuleDeployableSolarPanel.panelStates.RETRACTED) { Events["ToggleAllSolar"].guiName = "Extend All TEST"; }
            if (solarPanel.panelState == ModuleDeployableSolarPanel.panelStates.EXTENDED) { Events["ToggleAllSolar"].guiName = "Retract All TEST"; }
            Events["ToggleAllSolar"].guiActive = true;
            // blah blah blah...
            yield return new WaitForSeconds(0) ; // This is totally a kludge I have it in here because it doesn't error out. I'll happily fix it if someone tells me how.
        }
    }

Also, note that final "yield return." If someone could tell me the correct way to get out of that function (just "return" doesn't fly with Visual Studio) I'd appreciate it, though I'll happily keep the code there if it works. I don't think it's causing any problems (though my opinion is at best suspect on these matters)

Edited by 5thHorseman
Link to comment
Share on other sites

35 minutes ago, blowfish said:

@5thHorseman I don't think you need a statement there at all. But for future reference yield break will end the enumeration.

Weird. I had no statement there and VS was giving me an error. I just removed it to get the error, and VS is totally fine with it.

#NoobModder

UPDATE: After much fiddling I'm almost positive I just don't have the correct syntax for something. Nothing I do will modify the text or the visibility of the button on other solar panels. All I can do is modify those on the CURRENT one (my first code block above). Other than knowing that, though, I've got no clue where to proceed.

UPDATE 2: More fiddling, and digging through logging. I found that in the coroutine the following line gets a null reference exception:

            solarPanel.Events["ToggleAllSolar"].guiActive = false;

            solarPanel.Events["ToggleAllSolar"].guiActive = false;

I think it's because this is the "ModuleDeployableSolarPanel" module and I need to reference, on the same part with that module, the AYA_Solar_Test module. I just (as always) don't know how to do that.

Edited by 5thHorseman
Link to comment
Share on other sites

Just now, Crzyrndm said:

Aya_Solar_Test myModule = solarPanel.part.Modules.OfType<AYA_Solar_Test>()[0];

Assuming your module is always on the same part as the solar panel

It should be, unless the user screws around with the modulemanager config. I suppose I could check for it and then skip it if the module's not there? Check if it's null?

That code gets this error:

Error    CS0021    Cannot apply indexing with [] to an expression of type 'IEnumerable<AYA_Solar_Test>'

Removing the [0] at the end gets:

Error    CS0266    Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<AllYAll.AYA_Solar_Test>' to 'AllYAll.AYA_Solar_Test'. An explicit conversion exists (are you missing a cast?)

 

Link to comment
Share on other sites

...deleted...

You need to get the instance of your module that is on the same part as the solarPanel and change the events on that.  What Crzyrndm said looks like it should work...

Edited by Padishar
Link to comment
Share on other sites

Hello fellow modders!

I'm pretty new to modding KSP and working with Unity so please forgive me if I ask really stupid questions. :wink:

I would like to interact with the stock GUI (if that is at all possible and allowed?), more specifically I would like to suppress (or close) a window to show my own window instead. Searching the forums for combinations of "stock", "GUI", "interact with", "modify", "customize", didn't bring up anything that helped me. I just found some statements that "working/interacting with" the stock GUI tends to be PITA from a modding perspective, but no detailed informations and/or examples.

Any advise regarding this matter would be greatly appreciated. Even if the answer is "that is impossible" I would be happy, since that means I can focus my energy on other aspects. :)

Thanks!

(For those with signatures disabled: I would like to make KISS replace the default "quicksave as..." dialog, so it doesn't need an additional keybinding.)

Link to comment
Share on other sites

3 hours ago, Aerospike said:

I would like to interact with the stock GUI (if that is at all possible and allowed?), more specifically I would like to suppress (or close) a window to show my own window instead. Searching the forums for combinations of "stock", "GUI", "interact with", "modify", "customize", didn't bring up anything that helped me. I just found some statements that "working/interacting with" the stock GUI tends to be PITA from a modding perspective, but no detailed informations and/or examples.

A lot of the interesting GUI stuff can be private (and therefore not directly accessible without breaking rules) so you might have to jump through some hoops to make things work. In 1.0.5, I'd say your best bet would be registering for GameEvents.onInputLocksModified and checking for the quicksave dialog control lock. You might be forced to use FindObjectOfType<PopupDialog> afterwards to get the dialog itself since I don't see any convenient references anywhere.

The same tactic should work fine for 1.1. The advantage in 1.1 is that the dialog uses the new GUI system and exposes the dialog prefab (PopupDialogController.PopupDialogBase) which allows you to do almost anything you want to it, although I'd still go down the "replace it with own version" route than trying to tweak it to do what I want

Link to comment
Share on other sites

Thank you xEvilReeperx!

Now once again I just wish there was an official API documentation to find out more about this stuff :wink: (I do know the community-generated one). I have already "ported" the KISS dialog to the new GUI (not made a pre-release of the new one yet), so I'm fine with only adding that feature to the KSP 1.1 branch if that makes things easier.

Since you brought up control locks: do I have to implement control locks myself in some way? Currently I pause the game before showing the dialog (as the stock "quicksave as" does) and so far I have not had an issue with accidentally controlling the vessel.

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