MODULE+PLUGIN: Interfacer v0.80 Beta 4 - Scripting your interface, simplified!

Started by monkey0506, Thu 17/05/2012 02:25:55

Previous topic - Next topic

monkey0506

I've been putting some thought into it, and most adventure game interfaces have quite a few similar elements, but despite all the modules and templates floating around, it often creates a daunting and unfriendly barrier for newcomers who have little to no programming experience. Between on_key_press, on_mouse_click, repeatedly_execute, GUIControl event handlers, and the like, there's a lot to consider when first starting out.

So, why not take those similar elements from various interfaces and provide a way to define their relations more generically? This was the basic premise of the Interfacer module - to provide a generic script structure for handling a graphical user interface (meaning not just a single GUI, but the game's interface as a whole).

With the Interfacer module you can handle most of your interface's scripting all from one place, say, game_start.

While you will need some basic programming to use the module, I've now included a plugin so you can set the interactions graphically. I hope that this can simplify things for those who are starting out. The module isn't just for newcomers though. Imagine for a moment trying to include a 9-verb LucasArts interface, Sierra icon bar, and a verbcoin interface all within the same game. Even if you used the modules that are around, combining them might not be so straight-forward.

The Interfacer module also makes it easy to implement multiple types of interface within your game, simply by swapping out its settings. With a few custom functions you can implement various interfaces with relatively little code, and without having to worry about any issues that might otherwise arise from using multiple modules.

In any case, that's enough talking.

[DOWNLOAD v0.80 Beta 4] (Includes module+plugin!)
Interfacer v0.80 Beta 4 Module and Plugin Source
InterfacerModes* (Demo code and GUI)
Interfacer Demo (includes InterfacerModes) (press 1, 2, 3, and 4 to switch between 9-verb LA, Sierra, standard verbcoin, and static verbcoin)
Interfacer v0.80 Beta 3
Interfacer v0.80 Beta 2
Interfacer v0.80 Beta 1

I plan to include more documentation, but for now you can check out InterfacerModes for some example code, including this function which implements a basic statically-positioned verbcoin interface (similar to Gemini Rue):

Code: ags
void LoadStaticVerbcoinInterface()
{
  Interfacer.Clear();
  Interfacer.AddButton(btnUse, eButtonActionProcessLastClick, eModeInteract);
  Interfacer.AddButton(btnLookat, eButtonActionProcessLastClick, eModeLookat);
  Interfacer.AddButton(btnTalkto, eButtonActionProcessLastClick, eModeTalkto);
  //Interfacer.AddButton(btnResume, eButtonActionHideOwner); // requires gPanel from default template
  Interfacer.MouseButtonAction[eMouseLeft] = eMouseButtonActionProcessClick;
  Interfacer.MouseButtonDefaultMode[eMouseLeft] = eModeWalkto;
  Interfacer.MouseButtonAction[eMouseRight] = eMouseButtonActionShowGUI;
  Interfacer.MouseButtonGUIToShow[eMouseRight] = gInterface;
  Interfacer.MouseButtonGUIPosition[eMouseRight] = eShowGUIRelativeToMouse;
  //Interfacer.KeycodeGUIToShow[eKeyEscape] = gPanel; // requires gPanel
  //Interfacer.AddQuitButton(btnQuit, true); // requires gPanel
}


Edit: 9 June 2012 - After just over two grueling weeks of bashing my brain in, trying to wrap my mind around the best way to encapsulate and keep up-to-date all the relevant data, Beta 4 is here. So what's changed in the past couple of weeks? The biggest changes are on the plugin side. Previously there was an issue with the plugin not being able to refresh the project tree, so it was hard (impossible) to tell that scripts were being added/removed to the project. Also, when you made changes to the auto-generated script ("InterfacerInteractions.asc"), they were being read back, but the plugin wasn't updating immediately, which could cause confusion and frustration. Furthermore, when you made changes to the plugin data, the auto-generated script wasn't reflecting those changes until you saved the game. How annoying!

So, with Beta 4 I prioritized keeping you in the loop of when things are changing. It's considered "bad practice", but I plan to look into modifying the editor for future versions of the plugin. For now, I'm using a fair bit of reflection to keep things updated. What that means for you though, is that when the scripts are added to your project for the first time, you'll see them right away. It also means that if you make changes to the plugin data that the script can be updated right away, and if you make changes to the script, the plugin can read it back right away. There are other revisions, I'll notate the full details below.

Edit: 25 May 2012 - Uploaded v0.80 Beta 3. The plugin now fully reads back all keyboard and mouse data from the script, including user changes to the script. Plugin also now creates the module if it does not exist within the game.

Edit: 21 May 2012 - Uploaded v0.80 Beta 2. The plugin will now read back the mouse button data from the script. Known issue: GUI toggle, delay, and X/Y offset are not being properly read back.

Edit: 20 May 2012 - Added v0.80 Beta 1 of the plugin. Future releases will have both the module and plugin.

*NOTE: I forgot to comment the line for btnResume in InterfacerModes. The demo was built on top of the default template. You can either comment the line or add a btnResume to get it to compile.

Monsieur OUXX

Very interesting approach. It's a bit like a Listeners philosophy, isn't it?

Me gusta.
 

Ghost

That sounds extremely interesting and useful; I'll try it out for sure! Thanks for sharing this!

monkey0506

Expanding on this idea a bit further, here's a teaser of what I'm working on now:



(I've been toying with various layouts and such, and this seems the nicest so far...)

Spoiler
(Yes, that code was generated on the fly...)
[close]

Dualnames

I've made a similar unfinished and abandoned module called Control Modes. Glad to see someone bothered with this more. Well done, sir!
Worked on Strangeland, Primordia, Hob's Barrow, The Cat Lady, Mage's Initiation, Until I Have You, Downfall, Hunie Pop, and every game in the Wadjet Eye Games catalogue (porting)

monkey0506

I figured I may as well go ahead and upload a beta for the plugin as well. Mouse buttons and keyboard keys can have their interaction code for the module generated by the plugin. Please note that the plugin is not considered fully complete yet -- it cannot read any changes to the script that it generates, and does not save its data elsewhere. This means that if you close your project with the module, close it, and reopen it, any of the generated code will be lost (I'll be working on reading data back from the script).

Also, due to technical restrictions on the editor's project tree, the generated script will not show up in the script list right away. The script is generated when you save the game, but if it didn't exist you will have to manually add a new script, or change the order of the existing scripts.

In addition to reading changes from the script, I'll be working to add a way to set the interactions for GUI buttons, as well as establishing a status line label. For now, you can grab the plugin to try it out.

Secret Fawful


monkey0506

Uploaded a new beta with a partial implementation of reading data back from the InterfacerInteractions script. At the moment you should be able to save most of the mouse data...I say most, because the GUI properties for showing the GUI relative to the mouse, a GUI offset, and GUI delay aren't being read back properly. None of the keyboard data is being read back from the script yet.

After I get this sorted out, I will implement in the plugin a way to set the GUI Button data and other miscellany (like status lines and such). For now, grab the module+plugin v0.80 Beta 2.

Edit: I actually debugged the issue with the GUI properties not being read while laying in bed trying to fall asleep. It was a very, very simple mistake I had made in a couple of places. In my defense, I was at a party surrounded by people asking me what I was doing at the time I wrote the code. :P I'll try and make Beta 3 a bit more meaningful (i.e., keyboard data should be able to be read back as well), but just wanted to note that this is also fixed now.

Edit 2: 23 May 2012: In addition to fixing the above, I've also fixed a bug where the plugin was writing the GUI's ID instead of the name (for the keyboard keys). The keyboard data is now also being read back from the script. One other big change is that you can actually edit the script directly and the plugin will read the changes. There's a known issue when reading the data back from the script (if you're editing it manually) where the plugin is only being updated every other time you switch tabs. It does update if you save the game though. (Developer's note: I need to find a way to read from the Scintilla control directly instead of just reading the Script.Text which isn't kept up-to-date...)

There's a couple of other things I'd like to incorporate in Beta 3, and I'll be busy this weekend, but keep an eye out for that coming soon.

monkey0506

Beta 3 is up with the following changes:

- Fixed GUI toggle, delay, and X/Y offsets not being properly read from the script.
- Fixed plugin writing GUI ID where GUI name was expected for keyboard keys.
- Keyboard data is now read back from the script.
- Changes to InterfacerInteractions.asc are now read by the plugin when the game is saved (if you manually edit the script then later change something in the plugin pane itself, the changes in the script may overwrite your later changes).
- The plugin will now create the module if it does not exist within the game.
- The CursorModeKeycode property in the module has been renamed to KeycodeCursorModeToSet as it matches the other properties better.

I've started working on the interactions for the GUI buttons, as well as a way to set the text for the cursor modes and a label for the status line. That should be ready for beta 4.

Knox

Wow! Crap if only I could have used this before starting my project :P
--All that is necessary for evil to triumph is for good men to do nothing.

Sslaxx

Just trying Beta 3 with a new empty project and getting compilation errors.

Quote from: AGS 3.2.1Interfacer.asc(127): Error (line 127): undefined symbol 'gui'

This doesn't make much sense, obviously. Is this an error of omission on my part?

Trying to import the script module into a new AGS Empty Game template project seemed to be unreliable - sometimes it would duplicate the Interfacer.as* files (as Interfacer1.as*). One time it did not import the InterfacerInteractions.as* files. Trying to remove the files appeared to corrupt the Game.agf file.
Stuart "Sslaxx" Moore.

monkey0506

That's a known compiler issue...if you have no GUIs in your project then this won't build.

Sslaxx

Quote from: monkey_05_06 on Tue 29/05/2012 12:47:02
That's a known compiler issue...if you have no GUIs in your project then this won't build.
Ah. Need to modify some of my default project templates then. Thanks!

The plugin's adding the script files to the project directory but not importing them into AGS, though. AGS seems to add them anyway, but it causes duplication issues, and trying to remove the duplicate definitions removes the actual .as* files. Trying to reimport manually appears not to include the InterfacerInteractions.as* files. Closing and reopening the project appears to resolve this, though.
Stuart "Sslaxx" Moore.

monkey0506

Regarding the modules not showing up and/or duplicating...that's because the plugin has no way to refresh the project tree. It's been requested but the way the project tree is handled...is somewhat messy (IMO).

The scripts will build even if they don't show up in the tree...for now you can add a new script and then delete it, which will force it to update.

Edit: Just to be clear, the plugin will not duplicate the Interfacer module if it already exists. It will create it if it doesn't, but it won't show up in the project tree right away. If you then try to import the module (after the plugin has created it within the project), then you'll end up with a duplicate. The InterfacerInteractions script is always created, even if there are no interactions set.

Ghost

Damnit, monkey, this is getting better and better. Shame about the refreshing issue but I can't tell you how much fun I am having with this! Keep up the great work, this is one of the most useful things I've seen since the BakeCake plugin!

monkey0506

I'm glad to hear someone's actually getting some use out of it. :)

Regarding the refreshing issue, you've finally talked me into it, alright? It's hackish and really not the best practice, but I've reflected into the editor code, loaded up a bunch of data that isn't supposed to be publicly (in terms of script protection levels) exposed, and then dynamically invoked the function to repopulate the script tree view.

So now, any time you save your game with the plugin (in the up-coming Beta 4), the Interfacer and InterfacerInteraction scripts will show up in the scripts list (if they weren't already there, i.e. when they are being created).

Edit: I really shouldn't be doing this, but seeing as you already brow-beat me into reflecting into the editor assembly to repopulate the script tree view, I also went ahead and snagged the list (read as: Dictionary<Script, ContentDocument>) of open script editors, so now when you save the game, the auto-generated scripts can be updated as well. It's a silly thing to be doing the way that I am, but I'm at least wrapping it up in some error-proofing so worst-case scenario it won't explode, it will just fail silently.

Really, we need to expose this stuff to the editor plugin API... :)

6 June 2012: I know It's been almost two weeks since I first mentioned Beta 4, and given how rapidly the first 3 betas came out that might seem discouraging. The GUI button data is actually just a lot more work than I actually anticipated. Part of the problem is actually just a matter of keeping the plugin up-to-date if you add/remove/rename a GUI or GUIControl. I think I've finally figured out a reasonably good way of accomplishing that. Due to the other changes that Beta 4 has, I might hold off on some of the other features just to get this pushed out. Some of the things (like updating the project tree and open script editors) vastly improve the usability of the plugin. Anyway, just wanted to note that this isn't being abandoned! :)

9 June 2012: The only thing presently standing between me and releasing Beta 4 (that I'm presently aware of) is that changes to the plugin are now being automatically reflected in the script, but user changes to the script aren't being immediately updated in the plugin. It's bugging the crap out of me. :D If I can't figure it out soonishly, I may just go ahead and release Beta 4, you've waited long enough.

monkey0506

Alright, so I figured it out! :D

Beta 4 is up, and it's got some big changes:

- When the modules are created, the project tree is now updated right away (usually this will be the first time you save the game with the plugin).
- Despite the comment which I left in-tact (d'oh!), changes to the InterfacerInteractions.asc script are now not just read back, but are read back immediately!
- Changes to the plugin data are also reflected immediately in the script.*
- Key codes are now reliant on the KeyCodeAction property, as it fits better with the way that GUI Buttons and the mouse buttons are handled.
- Added list to set the GUI button actions. I want (still need) to implement a better way of handling the ListBox and TextBox controls for saving and loading games.
- Cursor modes and labels will populate for where I will be adding a way to set cursor mode text and the status label. This is not functional (yet).
- I almost forgot the totally awesome icon provided by Secret Fawful! Thank you sir!
- The entire thing is now OPEN SOURCE. I'll worry about proper licensing later, for now just don't go around trying to claim that you created the module or plugin and you're pretty free to do whatever the heck else you want with it (including derivative works of the source, so long as credit is given where due). :) The AGS project files are inside of the Visual C# 2008 Express project folder. This allows the VC# project to automatically update the plugin's internal copies of the module script files, and I can also automatically copy the SCM to the output directory, saving me a few seconds!

This means that most of the module data can be set up using the plugin. In upcoming versions of the module+plugin, I plan to include:

- Making sure that the script editor for InterfacerInteractions.asc will reflect changes to the plugin when it's opened. Currently it does this if the script is already open for editing, but I just need to make sure this is kept in-sync.
- Better dialog for setting the controls for saving and loading games. This will also make sure the controls are kept in-sync, whereas presently you can assign this multiple times, but only the last values are used. I will also need to make sure this has a way to set the list box format and max items.
- Add dialog for setting cursor mode text. I could actually default this to the cursor name as set in the editor, which would probably make sense for most use cases.
- Add dialog for setting the status line label, position, format, etc. As a matter of fact, that makes me realize that I don't have a separate format string available for using inventory items, which would be nice to have.
- A way to clear the plugin data entirely.
- A way to disable/enable the plugin and module. Just because you're using it in one project, doesn't mean you need or want it in every project.
- Various other miscellany, such as a key code action for quick save could be possible.
- A way to export the existing plugin data into a function in the InterfacerModes module. This would be a step in making it possible to design multiple interfaces for your game by using the plugin, whereas presently you have to either manually copy the scripts over, or write the script yourself.
- ??? You tell me! What do you want to see in the module or plugin? Feedback can only help make this thing better.

Also, I'd like to release an updated demo at some point to show off the features a bit more. Also, at some point I'll need to get around to actually testing that the inventory handling (which has been in-place since Beta 1) actually works. In fact, I remember mentioning to Fawful at one point that users might want/need the ability to set a different mouse button action for when the mouse is over an inventory item. For example, unless you actually specify in the "Interact with inventory" handler that the ActiveInventory should be set, the module doesn't have a way to do that at all. That's probably a reasonably good shortcoming.

In addition to the above, I'd also like to extend the InterfacerModes module as a sort of prepackaged set of example code, if only more for reference than actually being used (due to having to reference specific controls and such).

For now, enjoy Beta 4, and keep sending me feedback on this. I'm starting to worry that I'm putting all this time into something no one is going to find genuinely useful, but I'd like to think this is more than some gimmicky thing. 8-)

*Note: If the script isn't open for editing, this may actually cause things to get out-of-sync. To be honest, I'm not sure why I didn't test that more.

Ghost

That's a lot of new stuff- will test it for sure.

So far I didn't mind setting up inventory handling on my own; the automation you mention sounds useful though.

Quote from: monkey_05_06 on Sun 10/06/2012 00:49:40
I'm starting to worry that I'm putting all this time into something no one is going to find genuinely useful, but I'd like to think this is more than some gimmicky thing. 8-)
Don't worry!

Spoiler
Really, don't! I'll send a zombie with cheerful pom-poms over to your dig if you do!
[close]

monkey0506

As an example of extreme laziness the awesomitity of automation :P I have created a tool to automatically export the SCM file directly from the ASC and ASH files every time I build the plugin. I've done other stuff too, but this was a fun exercise. :D

Monsieur OUXX

Quote from: monkey_05_06 on Thu 14/06/2012 18:52:28
I have created a tool to automatically export the SCM file directly from the ASC and ASH files every time I build the plugin

Make it available, please! Preferably, create new forum thread. At this stage of AGS history, every automated build tool is welcome.
PS:  I seize this post to make sure you won't miss that post : here.
 

monkey0506

Presently it's linking against the Interfacer plugin itself:

Code: CSharp
using AGS.Plugin.Interfacer;
using System;
using System.IO;

namespace InterfacerModulator
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                string name = InterfacerModule.Name; // InterfacerModule is a new helper class in AGS.Plugin.Interfacer.dll
                string fileName = Path.GetFullPath(name + ".scm");
                string author = InterfacerModule.Author;
                string description = InterfacerModule.Description;
                string version = InterfacerModule.Version;
                string scriptText = InterfacerModule.ScriptText;
                string headerText = InterfacerModule.HeaderText;
                int uniqueKey = InterfacerModule.UniqueKey;
                Exporter.ExportScriptModule(fileName, author, description, name, version, scriptText, headerText, uniqueKey);
            }
            catch
            {
            }
        }
    }
}


The Exporter class is based directly on AGSEditor.AGS.Editor.ImportExport:

Code: CSharp
using System.IO;
using System.Text;

namespace InterfacerModulator
{
    internal class Exporter
    {
        private const string MODULE_FILE_SIGNATURE = "AGSScriptModule\0";
        private const uint MODULE_FILE_TRAILER = 0xb4f76a65;

        public static void ExportScriptModule(string fileName, string author, string description, string name,
            string version, string scriptText, string headerText, int uniqueKey)
        {
            BinaryWriter writer = new BinaryWriter(new FileStream(fileName, FileMode.Create, FileAccess.Write));
            writer.Write(Encoding.ASCII.GetBytes(MODULE_FILE_SIGNATURE));
            writer.Write((int)1); // version (???)
            WriteNullTerminatedString(author, writer);
            WriteNullTerminatedString(description, writer);
            WriteNullTerminatedString(name, writer);
            WriteNullTerminatedString(version, writer);
            writer.Write((int)scriptText.Length);
            WriteNullTerminatedString(scriptText, writer);
            writer.Write((int)headerText.Length);
            WriteNullTerminatedString(headerText, writer);
            writer.Write((int)uniqueKey);
            writer.Write((int)0); // Permissions
            writer.Write((int)0); // We are owner
            writer.Write((uint)MODULE_FILE_TRAILER);
            writer.Close();
        }

        private static void WriteNullTerminatedString(string text, BinaryWriter writer)
        {
            writer.Write(Encoding.Default.GetBytes(text));
            writer.Write((byte)0);
        }
    }
}


The InterfacerModulator builds directly to the AGS.Plugin.Interfacer project folder. Then when the AGS.Plugin.Interfacer is built, there is a post-build event that copies the AGS.Plugin.Interfacer.dll to the project directory, runs InterfacerModulator.exe, then deletes the DLL.

And...just in case you were wondering, InterfacerModule.Author, InterfacerModule.Description, etc. are defined as static readonly so that the exporter will read the values at run-time (as opposed to const where they would only be read at compile-time).

Edit: Just to make things a bit cleaner, I've now got the InterfacerModulator.exe set to build directly into the Release folder for the AGS.Interfacer.dll. When the DLL is built, it will run the EXE, and copy the DLL back into the EXE's project folder. Should help keep everything in sync if there's any changes.

Grim

Just wanted to say that this module seems really interesting to me and I've been keeping an eye on this thread for a while now... Making interface is like a whole other world compared to scripting your in-game events, and I confess I do struggle with it. I often wished there was something like Interfacer... and now there is! I'm just patiently waiting for a final version to give it a go. But yeah, monkey, great idea and I totally support what you're doing! :)

SMF spam blocked by CleanTalk