Jump to content

How to manipulate action groups in the editor?


Recommended Posts

A user in the editor can assign a particular action for a part to a particular action group for the vessel. For example, "take this solar panel and assign it to action group Custom1".

How can I do essentially the same thing via code?

Context: I know my way around coding, C#, Visual Studio, etc., but I'm new to KSP modding. I've been tinkering around and I've figured out how to trap editor events. I can write code so that every time a new part is created in the editor, my function gets called. What I'd like to do at that point is tinker with the part so that I can assign a certain action from the part to a certain action group.

I can see the parts, the modules, on them, etc. But I'm darned if I can figure out how to do the above. I've been going round in circles looking at Part, PartModule, BaseActionList, BaseAction, but haven't been able to find how to get at this functionality. Hunting through this forum, I find various references to action groups, but they're usually people who want to [I][U]activate[/U][/I] them programmatically. I'm not trying to do that, I want to tinker with them in the VAB.

Does anyone have any advice?

On a side note: The source of input info I've been working with is F12 in Visual Studio ;) plus the documentation available here: [url]http://anatid.github.io/XML-Documentation-for-the-KSP-API/annotated.html[/url] ...if anyone would like to suggest any additional sources of documentation / tutorials / etc. I'd love to hear it. :)

Thanks for any help!
Link to comment
Share on other sites

Ouch, welcome to the [i]fun times.[/i]

/sarcasm mode off

I've gone through all this for my Action Groups Extended mod and it's not pretty unfortunately.

First, if you are making a new action, you can flag the action as part of the KSPAction attribute to automatically add itself to an action group. This is how brakes and lights automatically add themselves to those groups. If this works for you, you can avoid messing around in code.

If that doesn't work for you and you still need to do it in code, things get interesting.

The action groups an action are assigned to is stored in the BaseAction.actionGroup enum. As this enum is used as multiple independent values, you have to use bitwise operations to set/unset values.

IE: If the action is currently assigned to group 1 and you do this: BaseAction.actionGroup = KSPActionGroup.Custom02; then the assignment to group 1 is lost because this command sets all assignments to false, except what is listed (group 2).

Instead, you have to do: BaseAction.actionsGroup = BaseAction.actionGroup | KSPActionGroup.Custom02; as a bitwise operation so that only the Custom02 bit is changed, the other enum values keep their values this way.

Then use: BaseAction.actionGroup = BaseAction.actionGroup & ~KSPActionGroup.Custom02; to unassign an action.

You can shortcut unassigning all actions though, BaseAction.actionGroup = KSPActionGroup.None; will unassign the action from all groups.

Hope that helps,

D. Edited by Diazo
Link to comment
Share on other sites

Thank you!

[quote name='Diazo']Ouch, welcome to the [I]fun times.[/I]

/sarcasm mode off[/QUOTE]

Yeah, I went round in circles on this for a while before I posted here. :)


[quote name='Diazo']I've gone through all this for my Action Groups Extended mod and it's not pretty unfortunately.[/QUOTE]

Heh, I was kinda wondering whether you'd be the one to respond... when I go hunting for working examples of code that works with action groups, you're pretty much the only name that comes up...


[quote name='Diazo']First, if you are making a new action, you can flag the action as part of the KSPAction attribute to automatically add itself to an action group. This is how brakes and lights automatically add themselves to those groups. If this works for you, you can avoid messing around in code.[/QUOTE]

I saw that, but that's not what I want to do. I'm not introducing any new actions. I have existing stock parts that have their own existing actions. In the editor, I want to be able to say "Take [I][U]this[/U][/I] standard action on [I][U]this[/U][/I] part and assign it to [I][U]that[/U][/I] action group."

[quote name='Diazo']The action groups an action are assigned to is stored in the BaseAction.actionGroup enum. As this enum is used as multiple independent values, you have to use bitwise operations to set/unset values.

IE: If the action is currently assigned to group 1 and you do this: BaseAction.actionGroup = KSPActionGroup.Custom02; then the assignment to group 1 is lost because this command sets all assignments to false, except what is listed (group 2).

Instead, you have to do: ...[/QUOTE]

Yeah, I figured that much out on my own as soon as I saw the "flags" attribute on the KSPActionGroup enum-- I'm a software engineer, I'm comfortable with bitwise operations, and I've got a few years of C# under my belt.

No, my problem is actually dumber than that. :)

How do I:

[LIST]
[*]Find out what action groups a given action on a given part is currently assigned to?
[*]Tell it to assign it to a particular action group?
[/LIST]

I went round and round with debug-log messages in the editor on this. For example, let's say I'm in the editor and I add a Mk1 spotlight to my ship. It gets put in the "Lights" action group, right? So let's say I trap the editor event when the part is created (i.e. when I click on the button in the parts tab), and tell it to iterate through part.Actions to see all the actions. I'd expect to see the "Lights" action for it, right?

Except that I don't see anything. Its part.Actions is empty.

Maybe "create" is too soon and I have to wait until I attach it to the ship? So I tried trapping that event and checking the actions then, too. Still no dice. Empty list. I'm sitting there in the editor, if I bring up the action groups UI I can see that the part is in the Lights group, but I can't seem to find that information programmatically anywhere.

Is there something simple and stupid that I'm missing? Edited by Snark
Link to comment
Share on other sites

Heh, I do seem to have made something of a study on how actions work in KSP.

[quote name='Snark']
Except that I don't see anything. Its part.Actions is empty.[/quote]

I think this is the largest source of confusion, parts don't have actions, partModules have actions.

So the lights action you are looking for lives in ModuleLightsPartModule.Actions

PartModule.Actions being a list of BaseActions, then you use the name so ModuleLights.Actions["ActionName"] returns the BaseAction obejct that is the lights action. You'll have to list all actions on the partModule to the log to get the names, Squad is unfortunately inconsistent with their naming. A toggle action will probably be called "Toggle" or at least have toggle in its name though, I know the ModuleDeployableSolarPanel toggle action is named "Toggle Panel".

This means to get all actions on a part, you have to .AddRange each partModule's .Actions list into one big list of your own. For completions sake I also add the part.Actions list, but I'm not aware of any part in the game with actions attached to it, as so:
[code]
Part p = //part reference you establish in code.
List<BaseAction> allActionsOnPart = new List<BaseAction>();
allActionsOnPart.AddRange(p.Actions);
foreach(PartModule pm in p.Modules)
{
allActionsOnPart.AddRange(pm.Actions);
}
//allActionsOnPart now contains all actions on the part in one place and you can work with them.[/code]



[quote]
[LIST]
[*]Tell it to assign it to a particular action group?
[/LIST]
[/quote]

This was in my previous post, once you have a BaseAction reference: BaseAction.actionsGroup = BaseAction.actionGroup | KSPActionGroup.Custom02; will assign it to Custom Actiongroup 2.

[quote]
[LIST]
[*]Find out what action groups a given action on a given part is currently assigned to?
[/LIST]
[/quote]

This one was tricky, I [i]think[/i] the following works:

[code]
if (KSPActionGroup.Custom01 == (BaseAction.ActionGroup ^ KSPActionGroup.Custom01))
{
//run code here if Custom1 is assigned to the action, IF statement above returns false if it isn;t.
}
[/code]

I do use the code above for checking a group's state myself, but in stripping out my own references I'm not 100% confident I kept the bitwise syntax intact so do be sure to test it.

Hope that helps,

D. Edited by Diazo
Link to comment
Share on other sites

[quote name='Diazo']I think this is the largest source of confusion, parts don't have actions, partModules have actions.[/QUOTE]

That was the secret sauce I was missing. All the bitwise manipulations of the enums I can handle in my sleep, that sort of thing has been my bread and butter for years. It's the fact that [I]actions live on the modules[/I] that was throwing me. That, and I also got a wire crossed in my head and was thinking of BaseAction as a by-value thing rather than a by-reference thing (i.e. thinking "I need to add a new BaseAction object" rather than "I need to find the existing BaseAction and modify it").

That's all I needed to get unwedged. I'm now discovering all sorts of interesting things about editor event logic, such as:

[LIST]
[*]you don't get a "created" event when you copy a part; that's a "copy" event [I][U]instead of[/U][/I] "created"
[*]you don't get "created" or "copied" events for all the symmetry counterparts when you add a symmetry group; you have to wait for the "attached" event
[*]...and only the object you're actually attaching gets an "attached" event. Not any of its children. Not any of its symmetry counterparts.
[/LIST]

So I've been able to figure out how to work around all those things, but just discovering the hard way that I [I][U]need[/U][/I] to work around them has been a long but highly educational voyage of discovery.

Anyway, I'm now pretty close to putting the finishing touches on my next mod. Thank you for the excellent help, it was just what I needed!
Link to comment
Share on other sites

Good to hear you seem to have figured it out.

[quote name='Snark'][LIST]
[*]you don't get "created" or "copied" events for all the symmetry counterparts when you add a symmetry group; you have to wait for the "attached" event
[/LIST][/QUOTE]

If you find a graceful solution to this please let me know. My workaround was to save the part reference, start a delay timer and then run the code I needed to on the symmetry counterparts once the timer had expired. Works, but it feels like a very hacky way to do things.

D.
Link to comment
Share on other sites

[quote name='Diazo']Good to hear you seem to have figured it out.

If you find a graceful solution to this please let me know. My workaround was to save the part reference, start a delay timer and then run the code I needed to on the symmetry counterparts once the timer had expired. Works, but it feels like a very hacky way to do things.[/QUOTE]

Got it all worked out, and the [URL="http://forum.kerbalspaceprogram.com/threads/139920"]mod is posted[/URL]. :) Thanks again for all the help!

I started by just wanting "make it so that cockpit lights are added to the Light action group by default", and ended up making the mod more general than that, so that it's possible for anyone to write a snippet of ModuleManager config to put any action from any part into whatever default action group they want.

The logic I finally settled on to "handle each new part when it's added" was:
[LIST]
[*]Handle "on create" event (this is needed for the initial root part that's placed in the editor)
[*]Handle "on attach" event. In this event, handle the part itself, and recurse through all of its children. Also iterate through all of the part's symmetry counterparts and recurse through [I]their[/I] children.
[/LIST]

That got me what I wanted.

It has a wart, in that if you move a part after placing it, the logic gets executed a second time. For example, using my mod: you place a cockpit part, it gets its "toggle lights" action added to the Light action group. You decide you don't like that, you go and manually remove that action group assignment. Then you move the part and re-attach it. The wart is that it will get re-added. That bothered me, and I was about to start tearing my hair out to figure out how to work around it, but then on a whim I tested the behavior of a stock Mk1 spotlight and it did the same thing! So I guess I'm off the hook, I don't feel the need to be smarter than stock in this regard. ;)
Link to comment
Share on other sites

[quote name='Snark']The logic I finally settled on to "handle each new part when it's added" was:
[LIST]
[*]Handle "on attach" event. In this event, handle the part itself, and recurse through all of its children. Also iterate through all of the part's symmetry counterparts and recurse through [I]their[/I] children.
[/LIST]

That got me what I wanted.
[/QUOTE]

You actually got this to work?

When I tried that, i found that the .symmetryCounterparts field was empty on the Update() frame that the .onEditorPartAttach event fired, it wasn't until the next Update() frame that .symmetryCounterparts was populated. Hence my hack with the timer above.

This was KSP 0.90 or so though, maybe the behavior's changed.

edit: [url=https://github.com/KSPSnark/DefaultActionGroups/blob/master/src/DefaultActionGroupBehaviour.cs#L43]This bit here always returned empty unless I delayed by one Update() frame.[/url]

[quote]It has a wart, in that if you move a part after placing it, the logic gets executed a second time. For example, using my mod: you place a cockpit part, it gets its "toggle lights" action added to the Light action group. You decide you don't like that, you go and manually remove that action group assignment. Then you move the part and re-attach it. The wart is that it will get re-added. That bothered me, and I was about to start tearing my hair out to figure out how to work around it, but then on a whim I tested the behavior of a stock Mk1 spotlight and it did the same thing! So I guess I'm off the hook, I don't feel the need to be smarter than stock in this regard.[/quote]

This is a quirk of how the editor actually deletes a part when you remove it from the vessel and makes a new part when you reattach it. (KSPFields may retain their values but not sure, nothing else does.) Even when the part's 'ghost' is attached to your mouse cursor, that isn't an actual part you can interact with. (Well, it might be, but it's not the part that gets attached to the vessel as that is created new when you click the mouse button to attach it.)

D. Edited by Diazo
Link to comment
Share on other sites

[quote name='Diazo']You actually got this to work?

When I tried that, i found that the .symmetryCounterparts field was empty on the Update() frame that the .onEditorPartAttach event fired, it wasn't until the next Update() frame that .symmetryCounterparts was populated. Hence my hack with the timer above.[/QUOTE]

I'm not using onEditorPartAttached, I'm using onEditorPartEvent with a ConstructionEventType of partAttached. That seems to work.

([URL="https://github.com/KSPSnark/DefaultActionGroups/blob/master/src/DefaultActionGroupBehaviour.cs"]source code[/URL])
Link to comment
Share on other sites

Hmm, must be a timing thing.

Something like OnEditorPartAttached firing immediately after attaching the part, so before the symmetry counterparts are created, but OnEditorPartEvent fires later (at the end up the update frame?) so symmetryCounterparts is populated by that time.

Interesting factoid I'll have to keep in mind for future, no plans to go back and rewrite my code that is working.

D.
Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

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