Vassal 4: Keys & Commands

The whole structure of Key Commands in Vassal 3 is cumbersome, confusing and unnecessarily complex.

Again, it had arisen organically over time as Vassal has developed.

The structure for how Commands are passed around inside Vassal is likely to change significantly, however, there are few things that really need to be done as part of this.

What we have now
Originally, all Command in Vassal where tied directly to keyboard key sequences (Control-A, Alt-Shift-D, etc.).

The addition of Named Key Strokes allow Commands to be tied to a name (string of characters) instead of a keyboard sequence.

Global Key Commands allowed a Key Stroke to be sent to all Game Pieces.

Global Hot Keys allow a Key Stroke to be sent to Game Components other than Game Pieces

Global Key Commands are arbitrarily divided into Global key Commands’ that

Some proposed changes.

1. Get rid of Key Strokes altogether as a basis for any sort of Commands.

All Commands should be based on a name, essentially what we have now with Named Strokes.

Key Strokes (Like Ctrl-A, Alt-Shift-D) should be able to be added to menu commands, toolbar buttons etc as Accelerators/Shortcuts, like every other piece of software ever written. An Accelerator key then initiates a Named Command.

2. Remove the distinction between Global Keys and Hot Keys

They are basically the same thing, except one gets directed to Game pieces and one gets directed to non Game Pieces.

This is just unnecessarily confusing.

Roll them into one type of Global Key Command. When you generate one, tick a couple of check boxes select whether to send it to just Game pieces, just non game pieces, or both.

3. Add a payload to Global Key Commands
You can send a Global Key Command from one Game Piece to another, but you cannot directly communicate any information between the two pieces.

This requires you to set up complex communication schemes using Global Variables, Trigger Actions and Dynamic Property commands to make useful things happen.

When you initiate a Global Key Command, it is not clear as to what part of the GKC is executed against the source piece, and what part is executed against the target piece, or how to compare values between the two.

I suggest we enhance Global Key Commands to be able to add a series of parameters that go out with the command.

Each parameter would have a checkbox to choose whether it is be evaluated against the source Game Piece before initiating the global command process, or against the target Game Piece.

The Property Match Expression (which is executed against the target Game Piece), would use a Lua function to access the Parameters.

The Command the gets executed on the target piece can also reference the parameters using the function.

NOTE: I am assuming the same {} functionality we use now for Beanshell to access Lua expressions.

Example mock up:

A Global Key Command that is sent to all Game Pieces to change the Dynamic Property named Division to the value of the NewDivision property on the source Game Piece. The Game Pieces to be selected must have a Formation property with the same value as the MyFormation property on the Source Piece.

Global Key Command setup:
Command Name: ChangeDivision
Parameter 1: {MyFormation} [Tick box to evaluate on source piece]
Parameter 2: {NewDivision} [Tick box to evaluate on source piece]
Property Match Expression: {Formation== GetParam(1)}

The Command that responds to this command will be a ‘Set Property’ Command. (The new equivalent to our current Dynamic Property Key Commands)

Receiving Command Setup:
CommandName: ChangeDivision
Property Name to Change: “Division”
Value to change to: {GetParam(2)}

This pretty much would have been my next question.

There is a specific issue I have in that when you type in a named command you cannot copy and paste because then it assumes you mean Ctrl-C or Ctrl-V to be the commands. This is very unorthodox behaviour for a modern program. But it seems to be a straightforward corollary from what you have said that this would go away.

I am excited by all the proposed changes.

In particular I would love if, in service of #2, toolbar items worked more like Game Pieces – when receiving GKC4.0’s they could have traits to process triggers and therefore do things like hide themselves, change their layer/art, etc, etc.

For #3, YES, the present situation obviously requires “bad coding practices” where I have to set some global properties for the target piece(s) to be able to evaluate against, which then makes the GKC’s “not-reentrant-safe”, etc.

Note that some of my GKC filters have a good number of properties being referenced, including property names being constructed from component parts and then checked. So the new solution would need a way to make quite a few “tick boxes” available:

(Rank>0) && (Side == ActiveSide) && (GetProperty("State_Names").contains(GetProperty("CurrentZone"))==true)

(Cav != "") && (GetProperty("LocationName").contains(GetProperty("ActiveArmy"))==true)

One alternative way to solve this, that might make both UI implementation easier and the “meaning of the code” clearer would be to provide SourceProperty() and TargetProperty() methods that operated analogously to GetProperty(). So, e.g.

TargetProperty("LocationName").contains(SourceProperty("ArmyName"))

Now it’s explicit in the syntax, and a maintainer coming later doesn’t have to remember to look at the checkboxes (and a programmer right now doesn’t have to go MAKE any checkboxes!)

And when I don’t need the full GetProperty() indirection functionality (which I for some reason need when using the java string/numeric functions even when not indirecting), then you could provide, e.g.:

SOURCE.LocationName == TARGET.LocationName

And if you decide to keep $$ available as a shorthand, then you could add %% or something as the target property indicator.

$Side$ == %Side%

Brian

Of course! Much cleaner than my clunky parameter idea. Since the expression is going to be evaluated by Lua, then assuming SOURCE and TARGET are correctly defined, the following automatically works as well:

SOURCE.GetProperty("Location" + "Name") == TARGET.GetProperty.("Location" + "Name")

Ah, yes – will be beautiful!

I just had a… Meaningful Thought.

What if, in Vassal 4, it was possible for pieces to “find each other” (and each other’s traits/properties) in other meaningful ways as well, often avoiding the need for (a) Global Properties and (b) Global Key Commands?

If, in whatever equivalent of BeanShell expressions will exist in Vassal 4, there were a function, perhaps “GetPiece()”, that I could use to find a specific piece, and then be able to either (a) send just that piece a keystroke, or (b) read/write its Dynamic Properties, read its Markers, etc?

Options for identifying the piece could include…

  • by BasicName (which for non-unique pieces would simply find the first match) - but there are so many Unique pieces, often exactly the ones you “want” to send information to (e.g. “The Red Player’s Scoring Marker”)
  • by gpid (and pieces could have access to their gpid’s as an exposed property, so that they could e.g. subscribe themselves to a function).
  • By GKC-style matching expression – still has to do THAT processing but means I wouldn’t need nearly as many Global Properties because I could store stuff for e.g. a particular Zone in a tracking piece for that zone. And then check it in expressions as e.g. GetPieceName(“RussiaZone”).Partisans >= 5

Could be cool?

Brian