Help coding custom menu!

Post » Sun May 05, 2013 6:09 pm

I have been really impressed with what the SkyUI team and others have manager to do with the UI. I have a project that requires a much more simple custom menu to function, however I have little to no experience with the development tools they are using (Adobe Flash and Scaleform).

So, I figured I would explain in detail exactly what I am trying to do, and pray to the gaming gods someone with greater knowledge could help me set it up. I can handle all the scripting/CK implementation.

OK, so to start off with, this would basically be a custom gift menu that could be called to pop in the middle of the screen via script by calling ShowGiftMenu() on an actor with a certain name.

This gift menu would contain a centered, scrollable list of scripted "items" that the player could click on. Instead of adding the clicked item to the player's inventory however, clicking would immediately close the gift menu and run whatever script was on that item. This would be accomplished with a BlockActivation() in an OnInit() event in the script for each item, followed by an OnActivate() event that would handle executing the script.

I have put together a generic example of what I would like the menu to look like:

http://www.iparadigm.org/wp-content/gallery/testing/menuopt.jpg

As you can see, I want to keep it as simple as possible.

  • No category browser: The list should default to showing all item types.
  • Sortable by name only.
  • No icons since the menu is type independent.
  • Scrollable if the list is long enough to require it.

The size of the list could be a bit larger/smaller, however this is the general idea. It is basically intended as a fully scriptable menu alternative.

How it would work in the CK:

Users would create a hidden Actor, and a set of items with all the names of the menu options they would like to see when activating this custom menu script. They would then add these items to their custom actor in the CK. Alternately, it would be possible to dynamically add/remove items from the list using AddItem() and RemoveItem() on this hidden actor via script.

As outlined, clicking an item ignores normal activation, so the item is not removed nor added to the player's inventory. Instead, this custom menu immediately closes and runs whatever is in the OnActivate() event for that item.

EDIT: I would also like a "Cancel" option to appear on the lower right that would close the custom menu. Users could detect this in their script using an if IsInMenuMode() -> return type function upon calling the custom menu, then proceeding with an exit routine once the menu was closed.

This loop could also check for the status of a variable, to detect whether the menu was exited by clicking cancel or by clicking an item. So, the user would set this global/property variable to 1 in the OnActivate() script run on each of their menu items, so clicking cancel would leave it at 0, allowing the user's script to differentiate and take different paths based on that detection.

EDIT EDIT: Updated the sample screen to show the cancel button. I realize it's rough; I couldn't find a font that really matched the SkyUI's default! XD

EDIT EDIT EDIT: Perhaps it would be best to leave "name" off the sorter, since people might use the menu for things that would not seem appropriate to that designation... Just having the icon there would probably be enough. Or what would be REALLY epic is if there were some way to pass a string to the custom menu through text replacement, though I'm not sure how that would work since there's only certain things that accept stored data from a quest alias.

My knowledge is limited however, as I said. Perhaps having an alias to a persistent hidden reference with whatever name a user wanted to appear on the top would be enough, and the code could call a fake menu or something to grab it? That's getting a little fancy though. :wink:

User avatar
Christina Trayler
 
Posts: 3434
Joined: Tue Nov 07, 2006 3:27 am

Post » Sun May 05, 2013 8:49 am

No takers, huh? Look I realize it is redundant, that there are already ways of going about this, but let's be honest shall we? How many modders, even talented modders, are going to:

1) Buy a license for Flash or ActionScript 2 just for modding and
2) Learn a new language just to do one simple function that could easily be coded in five minutes *exaggeration for dramatic effect* by the people already invested and knowledgeable on the subject?

It would literally take all of 20 minutes to an hour to write up the back-end for this, at which point any lay-modder could follow along a wiki article I'd be happy to write and have full access to an easy-to-use interface for massively scalable custom menus.

I really don't see the down side, and you would be hailed as heroes for your generous contribution to expand the useful tools widely available to the community!

I suppose I shall continue to wait patiently, and hope that some kind monk/guru decides to set this up for us.
User avatar
BaNK.RoLL
 
Posts: 3451
Joined: Sun Nov 18, 2007 3:55 pm

Post » Sun May 05, 2013 3:01 am

I have no intellectual capability atm to offer any technical info, but I think expired is working on a similar way of implementing these types of menu over control of his upcoming release version of http://skyrim.nexusmods.com/mods/12933 - http://imgur.com/a/ECpJq, I think he is well veresed on the subject and you two can have a talk. :D

User avatar
Antonio Gigliotta
 
Posts: 3439
Joined: Fri Jul 06, 2007 1:39 pm

Post » Sun May 05, 2013 5:31 pm


Hm frankly, I find it hard to give any constructive feedback on this suggestion, because it's really far off. It's like you're trying to set up a hack for something that doesn't exist.
There is no need to use actors or OnActivate events, you can just pass your menu entries as a string array. And when a button is clicked, you'd receive a callback event.
Basically, you tried to design a framework, but you completely ignored the technical facts and problems associated with the domain. Nothing good can come of that.

You completely misjudge the difficulties associated with the task. I'm left wondering, how would you even assume it's trivial and takes 20 minutes, when you earlier admitted that you are clueless on the details.

1. enthusiasm 2. disappointment 3. bargaining?
Adding that little drama spin makes it much less likely that any of the people who I know of could help you with this (including me) will bother much.
User avatar
Patrick Gordon
 
Posts: 3366
Joined: Thu May 31, 2007 5:38 am

Post » Sun May 05, 2013 6:12 am


Fair enough. I'll be the first to admit I know nothing about ActionScript, well, nothing as of yet. I am however quite familiar with arrays and callbacks from other programming languages, so it is somewhat encouraging to see such options exist in this scenario.

PurpleLunchbox was actually explaining to me a bit in PM's about the technical limitations of the UI, and how it is actually a simpler task to do replacements than to inject entirely new functions. In this scenario, it would indeed be an entirely new function, and I realize my estimates as to the complexity of the task were most likely radically understated.

I certainly didn't intend my enthusiasm to come off as offensive or insulting in any way. That's just my personality/sense of humor, and it doesn't always translate well to text.

The use of Actors idea was just a way I saw to make the whole process ridiculously simple for the end-user once the back-end was compiled. Adding/removing items to an inventory GUI would be far simpler to grasp for most than learning about arrays and getting the syntax down to push string variables to a list that needs to be dynamically updated in real-time by a mod.

I am still interested in getting this going, even if I have to learn ActiveScript to do it. I've taught myself other programming languages in weeks, so I'm sure I could handle it. I just need to look into whether I can get CS4 on a trial or student discount.
User avatar
Flutterby
 
Posts: 3379
Joined: Mon Sep 25, 2006 11:28 am

Post » Sun May 05, 2013 4:45 am

Ok, I don't know how much PLB already explained, but I can give you a quick summary of one potential approach.

First of all, some basics:
UI -> Papyrus communication is realized with custom SKSE mod events. A papyrus script registers for an event (RegisterForModEvent), and from ActionScript you can trigger it (SendModEvent). At the moment, you can pass single numbers or strings with the event.

Papyrus -> UI communication uses the SKSE UI.* functions. Usually this means calling AS functions and passing parameters (strings, numbers, arrays, ...). Overall, the API is more powerful in this direction. The reason is that the usual flow of information is
1. Papyrus script holds data, sends it to UI for presentation. So this requires to be able pass a lot of data quickly. Example: Send a list of entries as a string array.
2. UI sends back events when user actions happen. The important information here is the fact that the event happened itself ("user clicked entry"), and maybe some additional data ("which entry index?"), but that's it.

PLB probably mentioned that adding a completely new menu is difficult and IMHO not even necessary, so we think it's best to hijack the messagebox menu.

Anyway, so a proposed course of action:
1. Create a lightweight messagebox menu loader. See https://github.com/s.../src/MessageBox , should be already done and is generally not difficult.
2. Create a Papyrus wrapper for this loader (optional, I guess)
3. Create your custom dialog menu(s) in a new swf. You could reuse the MCM dialogs for example. Everything should be already there, both visually and the respective code, you just have to modify so it runs standalone.
4. Create a Papyrus wrapper to hide the UI communcation details and the menu loader. This is how MCM works, too. Example:

Have a look at https://github.com/s..._ConfigBase.psc , search the implementation of function ShowMessage.

The provided API function is
bool function ShowMessage(string a_message, bool a_withCancel = true, string a_acceptLabel = "$Accept", string a_cancelLabel = "$Cancel")
What happens internally is:
bool function ShowMessage(string a_message, bool a_withCancel = true, string a_acceptLabel = "$Accept", string a_cancelLabel = "$Cancel")if (_waitForMessage)Error("Called ShowMessage() while another message was already open")return falseendIf_waitForMessage = true_messageResult = false; Wrap arguments in a string array so it can be passed to UI.Invokestring[] params = new string[3]params[0] = a_messageparams[1] = a_acceptLabelif (a_withCancel)params[2] = a_cancelLabelelseparams[2] = ""endIf; Register for the menu close event, which is sent by the UI via skse.SendModEvent("SKICP_messageDialogClosed", ...)RegisterForModEvent("SKICP_messageDialogClosed", "OnMessageDialogClose")UI.InvokeStringA(JOURNAL_MENU, MENU_ROOT + ".showMessageDialog", params); Suspend this thread, wait for result. This means ShowMessage() is blocking just like the regular Message.Show()while (_waitForMessage)Utility.WaitMenuMode(0.1)endWhileUnregisterForModEvent("SKICP_messageDialogClosed")return _messageResultendFunction; This event is received in a second thread, resumes the waiting thread and sets the result based on the passed number (clicked button index)event OnMessageDialogClose(string a_eventName, string a_strArg, float a_numArg, Form a_sender)_messageResult = a_numArg as bool_waitForMessage = falseendEvent
So yeah, this is generally a nice example because it shows how UI<->Papyrus communication works. It won't be as simple though, because here MCM is already open and you can just call an AS function on it. You'd also have to take care of getting your custom menu loaded via messagebox first. So that would involve something like

1. myDummyMsg.Show(), myDummyMsg containing a string that prompts a modified messagemenu.swf to hide the regular messagebox and load an external swf instead (it's name being passed as a parameter).
2. RegisterForModEvent("MyDialogLoaded",...), to catch when your menu has been loaded.
3. Wait until this event is received, you can then proceed and call a function to pass parameters like shown in the example above.
4. Wait for the result, return it.


Alright, that should give you a general idea. Overall, it's doable. Most of the code already exists somewhere in SkyUI, and no additional reverse engineering necessary, which could otherwise be a major showstopper.
User avatar
Life long Observer
 
Posts: 3476
Joined: Fri Sep 08, 2006 7:07 pm

Post » Sun May 05, 2013 8:07 am

Thanks schlangster, that is good information.

I've managed to install a CS4 trial and the free UDK, and grabbed the http://skyrim.nexusmods.com/mods/216 from the nexus. I have the giftmenu.swf.fla file and giftmenu.swf_as folder in C:\UDK\Custom\Development\Flash\AS2\CLIK and can open it in CS4. I used the font fix talked about http://www.gamesas.com/topic/1267229-modding-the-gui/?p=19255191 which seems to have fixed my font missing problems, though publish does still complain about a few Scaleform functions it can't find.

At this point however, I really have no idea how to go about manipulating the menu layout, removing buttons, or changing how the scripts work. Documentation is pretty sparse but maybe I can find a SAMS Scaleform or AS/CS4 ebook somewhere... XD
User avatar
Etta Hargrave
 
Posts: 3452
Joined: Fri Sep 01, 2006 1:27 am

Post » Sun May 05, 2013 8:23 am

You don't need the UDK. Also these Flash files are very outdated, so rather use this: https://github.com/Mardoxx/skyrimui
I recommend to learn both Flash and how everything works in general, review the SkyUI sources: https://github.com/schlangster/skyui

Try setting that up locally so you can recompile the .fla files. For example the messagebox I mentioned earlier: https://github.com/schlangster/skyui/tree/master/src/MessageBox
User avatar
des lynam
 
Posts: 3444
Joined: Thu Jul 19, 2007 4:07 pm

Post » Sun May 05, 2013 6:17 pm

OK, so I have all the files from the repository you linked installed and can re-compile the .FLA files without errors in CS4. Doesn't mean I know enough about Flash to modify their layouts yet, but it's a start.

The first stumbling block I'm hitting is your quoted step one. I guess I am just not seeing how your ShowMessage function is intercepting the normal MessageBox in your .AS code. I guessed that is because it isn't, and is running out of MCM directly, so there must be some other (journal?) object you've modified to invoke that (MCM) before this part.

If I understand correctly though, there is some ActionScript event receiving data from the SKSE communication that either fires the normal Journal object or the modified (MCM) version, but ONLY fires the later if a specific string is passed? Some of your links were broken so I'll have to do some digging into the SKYUI source to see how these commands look in ActionScript.

I am also somewhat confused about where you are setting public/private variables and functions in your modified MessageBox...

It is difficult to see how a custom list of options would be sent to this new message box function, and how they could be easily updated. Say I have a list of menu options that is going to be constantly changing, and will be different every time the message is called. Not only am I unclear how to pass this updated list as an array of strings, but also how to get it set up as in the mockup (that part is more Flash editing though.)

That is why I wanted to handle it through the GiftMenu. It just seems like for custom MessageBoxes you would need to code a separate .SWF for each layout of options, which in the case of a dynamically changing list without a predictable set of contents, would not really be feasible. Unless I am totally misunderstanding and there is a way to pass menu options as an array, and receive the selected option as an index of an ActionScript array in return.

As an alternative I was thinking of modifying GiftMenu much as you have the Journal to invoke MCM. I wanted to have this modified GiftMenu hide the normal gift menu and show a custom layout version if passed one of several actors as the "giving" party. I then wanted to code ActionScript on the new menu to send the first item clicked on back to Papyrus as a reference, which I am not sure is possible. Basically some way to detect which object is clicked.

I also want to automatically close this custom gift menu either when the player hits Tab, or as soon as they click on any of the items in the list, which I am not sure how to code in ActionScript either. Probably by redirecting the function call on the "Exit" button to apply to the entire item list, after returning the clicked reference. Again, I plan to completely hide just about all of the normal buttons.

Then, I would have Papyrus look at this reference to what item was clicked and do something based on the choice. I was also thinking of an OnItemRemoved event on the "giver" container to detect which menu item was removed, and silently re-add it and remove the grabbed item from the player. Of course a way to block actual taking of the item and simply sending the reference from the custom menu in ActionScript as a string before self-killing of the custom menu would be preferred but again, very little ActionScript knowledge here.

If I am way off-base please let me know. I really appreciate your example, I am just not seeing how it would work for this specific scenario where I have a dynamic list of menu options that will never be the same twice.

User avatar
phillip crookes
 
Posts: 3420
Joined: Wed Jun 27, 2007 1:39 pm

Post » Sun May 05, 2013 3:32 pm

The messagebox.fla/as I linked is not used by MCM. The whole MCM sits in the journal menu (it's a separate .swf, but the journal is its root).
The relevant code locations:
(https://github.com/schlangster/skyui/blob/master/dist/Data/Scripts/Source/SKI_ConfigBase.psc)713: bool function ShowMessage(string a_message, bool a_withCancel = true, string a_acceptLabel = "$Accept", string a_cancelLabel = "$Cancel")The Papyrus function MCM menu clients can use to show a custom message.732: UI.InvokeStringA(JOURNAL_MENU, MENU_ROOT + ".showMessageDialog", params)Call AS function from Papyrus to open the actual dialog. Pass message text is passed in params.(https://github.com/schlangster/skyui/blob/master/src/ModConfigPanel/ConfigPanel.as)408: public function showMessageDialog(a_text: String, a_acceptLabel: String, a_cancelLabel: String): Void427: var dialog = DialogManager.open(this, "MessageDialog", initObj);DialogManager is a generic SkyUI helper class to handle fading and focus for dialogs. "MessageDialog" is the name of a library object in the configpanel.fla. Its type is https://github.com/schlangster/skyui/blob/master/src/ModConfigPanel/MessageDialog.as
Private variables are usually set from code. In the MessageBox() constructor for example. Stage elements are objects assigned on stage. So if I place a MovieClip named icon inside some other MovieClip which has a custom class assigned to it, I'd declare var icon: MovieClip in the respective .as file. Public variables can be set from code as well, or sometimes via component settings (it's a context menu in the library).

You can pass menu options as an array, see
setMenuDialogOptions and setMenuDialogParams in ConfigPanel.as,
https://github.com/schlangster/skyui/blob/master/src/ModConfigPanel/MenuDialog.as for the respective dialog.
RequestMenuDialogData...

- How to open the GiftMenu with your actor from Papyrus?
- Creating menu entries by creating items, having to add them to an actor, it's very hackish.
- Returning the clicked item as reference - should work.

1. Papyrus client:
string[] entries = new string[3]entries[0] = "One"entries[1] = "Two"entries[2] = "Three"int resultIndex = SKI_DialogManagerInstance.ShowMenu(entries)
2. Papyrus dialog manager:
scriptname SKI_DialogManager extends SKI_QuestBase hidden ...int function ShowMenu(string[] a_entries)  dummyMessageMenu.Show() ; this is a dummy messagebox form, containing "$$loadmovie=extdialog.swf$$". this triggers our modified MessageBox to hide the original messagebox and load extdialog.swf  ; wait until custom menu is loaded, i explained this earlier  UI.InvokeStringA(MESSAGE_MENU, MENU_ROOT + ".showMenuDialog", a_entries)  ; wait for result, also explained in my last posting  ...  return resultendFunction
3. The modified messagebox which understands $$loadmovie=...$: Already covered by the MessageBox implementation in SkyUI repository.

4. extdialog.swf:
class ExtendedDialog extends MovieClip{  public function showMenuDialog()  {    ...    DialogManager.open(this, "MenuDialog", initObj);    ...  }}
5. MenuDialog: Pretty much covered by https://github.com/schlangster/skyui/blob/master/src/ModConfigPanel/MenuDialog.as
User avatar
Anthony Rand
 
Posts: 3439
Joined: Wed May 09, 2007 5:02 am

Post » Sun May 05, 2013 2:32 pm

What you are describing doesn't seem terribly complex conceptually. I think where I am getting hung up is the syntax/use/terminology.

Here for example. Am I correct in assuming that ShowMenu() is a function on a Papyrus quest script linked in this calling script as a Property called SKI_DialogManagerInstance? Where does this SKI_DialogManagerInstance actually reside, on a tracking quest?

Again, where does this script reside? What does "extends SKI_QuestBase hidden" indicate?

When you say "dummyMessageMenu.Show() ; this is a dummy messagebox form, containing "$$loadmovie=extdialog.swf$$". this triggers our modified MessageBox to hide the original messagebox" where is "$$loadmovie=extdialog.swf$$" actually declared? Since the only parameters of Show() are numerical text replacement values, would it be in the message field of the dummy Message form itself in CK?

EDIT: It appears I was correct that this just goes verbatim in the message field, and the processMessage function in the modified MessageBox handles what to do based on that. Is this correct?

Where are MESSAGE_MENU and MENU_ROOT defined? According to http://www.creationkit.com/UI_Script#Valid_Menu_Names those are MenuName and Target respectively, passed as strings.

So MenuDialog would be where I would need to have my own customized Flash SWF to define layout and button functions, and pass data back to Papyrus based on user interaction with the menu list?

User avatar
lacy lake
 
Posts: 3450
Joined: Sun Dec 31, 2006 12:13 am


Return to V - Skyrim