CREATING MOUSE ZONES
Mouse zones are similar to emissive and visibility in that they are attached to a <Component> element. Therefore, your mouse zone does not need to be the part it is animating so you can use a larger mouse square if you have a tiny switch.
Asobo has provided a number of templates in Generic/Complex that covers things such as knobs, levers, pushbuttons and switches. These templates also include code to control animations. But this post is about writing your own code and not re-using Asobo templates.
All Asobo templates lead to ASOBO_GT_MouseRect and that template leads to ASOBO_GT_MouseRect_Impl which is where the actual mouse rectangle code is written. ASOBO_GT_MouseRect_Impl is a complex template with nearly 200 lines of code most of which I don't understand. There are a lot of new parameters such as <IMCursorsInstances>. I don't know what the IM stands for but there are a lot of parameters that are called IM.
I highly recommend that you take a look at ASOBO_GT_MouseRect_Impl in OneStore/fs-base-aircraft-common/ModelBehaviourDefs/Asobo/Generic/Interactions.xml. This is the base code for mouse rectangles and contains everything, far more than I will explain in this brief introduction. We will use just a subset of the possible code.
One point I will make is that if you have used the right mouse button for interactions in the past then you probably shouldn't use it in MSFS. By default holding the right mouse button down allows the user to move the view with the mouse. The middle click does the same but it toggles mouse view on/off. I'd therefore recommend against using middle or right mouse actions and just limit yourself to left mouse click.
So, I think I will start with some simple code as an example. The examples I have created are based on the Asobo interactions templates. I have stripped them down to the core elements that do what I need and passed those elements to ASOBO_GT_MouseRect (which passes them on to ASOBO_GT_MouseRect_Impl). This example provides a mouse zone for a switch. I wanted to use the mouse wheel to toggle the switch as well as the standard left click to toggle on/off.
Code:
<Component ID="MouseZone_FuelPump" Node="MouseZone_FuelPump">
<UseTemplate Name="ASOBO_GT_MouseRect">
<TOOLTIPID>Fuel Pump:%((A:GENERAL ENG FUEL PUMP SWITCH:1, Bool))%{if}ON%{else}OFF%{end}</TOOLTIPID>
<CURSOR>Hand</CURSOR>
<MOUSEFLAGS_DEFAULT_IM>LeftSingle+WheelUp+WheelDown</MOUSEFLAGS_DEFAULT_IM>
<CALLBACKCODE_DEFAULT_IM>
(M:Event) 'LeftSingle' scmi 0 == if{ (>K:FUEL_PUMP) }
(M:Event) 'WheelUp' scmi 0 == if{ (A:GENERAL ENG FUEL PUMP SWITCH:1,bool) 0 == if{ (>K:FUEL_PUMP) } }
(M:Event) 'WheelDown' scmi 0 == if{ (A:GENERAL ENG FUEL PUMP SWITCH:1,bool) 0 != if{ (>K:FUEL_PUMP) } }
</CALLBACKCODE_DEFAULT_IM>
</UseTemplate>
</Component>
TOOLTIPID - This is equivalent to ToolTipText in the old FSX code. You can enter simple text here or complex text scripts. Preset tooltips can be called using the TT: code. The majority of ASOBO templates use the preset tooltips (which I presume are translated automatically) to display information. For example:
<TOOLTIPID>TT:COCKPIT.TOOLTIPS.FUEL_PUMP_SWITCH_ON</TOOLTIPID>
Asobo has not listed the tooltip codes (still TO DO I guess) but I have attached a list of the codes that I have pulled from the XML code in the Asobo model behaviors files. The first part is a list of used mouse Cursors. The second part is mostly text scripts that give you an idea how to do the complex scripts. Below that is an alphabetical listing of the tooltips. NOTE: These were pulled from the XML code so you may get some odd ordering and it is also no guarantee that these are all the tooltips.
Personally, I don't like the way Asobo has created tooltips. For example, if you are going through a checklist and it says FUEL PUMP ON then that is what I want the tooltip to say. It shouldn't say "Turn off fuel pump". That is too confusing in the cockpit, you end up having to figure out whether it means the fuel pump is already on or off.
CURSOR - These are the cursors I have been able to find in the templates and that work: TurnRight, TurnLeft, TurnRightSmall, TurnLeftSmall, TurnRightUpSideDown, TurnLeftUpSideDown, Hand, UpArrow, DownArrow, LeftArrow, RightArrow, Grab. The following names appear in the code but they do not appear to work with normal cursors: Animtip, Center_Cursor, Cursor, Center_Radius.
MOUSEFLAGS_DEFAULT_IM - LeftSingle, LeftDrag, LeftRelease, WheelUp, WheelDown seem to be the most common. As I said before Right and Middle mouse buttons are used to control the mouse view so unless you have a good reason I would advise against using them.
CALLBACKCODE_DEFAULT_IM - Some nice XML code. The mouse code supports CallbackJumpDragging and CallbackDragging although I refer you back to ASOBO_GT_MouseRect_Impl to see exactly which parameters are required as there are some new ones in all the callback elements.
Let's look at a more complex example. In this case the altimeter knob is partially obscured in my aircraft so I wanted click spots above and below the knob. Holding down the mouse button will repeatedly adjust the altimeter. Using the mouse wheel will also adjust the altimeter.
Code:
<Component ID="MouseZone_Altimeter_Inc" Node="MouseZone_Altimeter_Inc">
<Update Frequency = "10" InteractionModel="Default">
(O:XMLVAR_FirstUpdateTime) 0 !=
if{
(O:XMLVAR_FirstUpdateTime) (E:SIMULATION TIME, seconds) < if{ (>K:KOHLSMAN_INC) }
}
</Update>
<UseTemplate Name="ASOBO_GT_MouseRect">
<TOOLTIPID>%((P:Units of measure, enum) 2 == )%{if}%((A:KOHLSMAN SETTING HG, mbar))%!d!TT:COCKPIT.TOOLTIPS.MBAR%{else}TT:COCKPIT.TOOLTIPS.ALTIMETER_CALIBRATION_KNOB_INHG</TOOLTIPID>
<CURSOR>TurnLeft</CURSOR>
<MOUSEFLAGS_DEFAULT_IM>LeftSingle+LeftRelease+WheelUp+WheelDown</MOUSEFLAGS_DEFAULT_IM>
<CALLBACKCODE_DEFAULT_IM>
(M:Event) 'LeftSingle' scmi 0 == if{
(>K:KOHLSMAN_INC)
(E:SIMULATION TIME, seconds) 0.4 (>O:XMLVAR_FirstUpdateTime)
}
(M:Event) 'WheelUp' scmi 0 == if{ (>K:KOHLSMAN_INC) }
(M:Event) 'WheelDown' scmi 0 == if{ (>K:KOHLSMAN_DEC) }
(M:Event) 'LeftRelease' scmi 0 == if{ 0 (>O:XMLVAR_FirstUpdateTime) }
</CALLBACKCODE_DEFAULT_IM>
</UseTemplate>
</Component>
<Component ID="MouseZone_Altimeter_Dec" Node="MouseZone_Altimeter_Dec">
<Update Frequency = "10" InteractionModel="Default">
(O:XMLVAR_FirstUpdateTime) 0 !=
if{
(O:XMLVAR_FirstUpdateTime) (E:SIMULATION TIME, seconds) < if{ (>K:KOHLSMAN_DEC) }
}
</Update>
<UseTemplate Name="ASOBO_GT_MouseRect">
<TOOLTIPID>%((P:Units of measure, enum) 2 == )%{if}%((A:KOHLSMAN SETTING HG, mbar))%!d!TT:COCKPIT.TOOLTIPS.MBAR%{else}TT:COCKPIT.TOOLTIPS.ALTIMETER_CALIBRATION_KNOB_INHG</TOOLTIPID>
<CURSOR>TurnRight</CURSOR>
<MOUSEFLAGS_DEFAULT_IM>LeftSingle+LeftRelease+WheelUp+WheelDown</MOUSEFLAGS_DEFAULT_IM>
<CALLBACKCODE_DEFAULT_IM>
(M:Event) 'LeftSingle' scmi 0 == if{
(>K:KOHLSMAN_DEC)
(E:SIMULATION TIME, seconds) 0.4 (>O:XMLVAR_FirstUpdateTime)
}
(M:Event) 'WheelUp' scmi 0 == if{ (>K:KOHLSMAN_INC) }
(M:Event) 'WheelDown' scmi 0 == if{ (>K:KOHLSMAN_DEC) }
(M:Event) 'LeftRelease' scmi 0 == if{ 0 (>O:XMLVAR_FirstUpdateTime) }
</CALLBACKCODE_DEFAULT_IM>
</UseTemplate>
</Component>
The interesting thing about this code is the <Update> element. It is not related at all to the mouse zone but it is used here to repeat an action while the mouse button is held down. The mouse code is used to turn on and off the update by writing into (O:XMLVAR_FirstUpdateTime). I assume Frequency is times per second, no idea what InteractionModel does. I don't see any reason why you couldn't use the update element to do just about any sort of XML code that you like.
When we call the ASOBO_GT_MouseRect you can see we use a script for the Tooltip to display either mbar or inHG depending on the user settings. This code is just copied from the Asobo altimeter template. Different cursors for the two zones so the user knows what to expect. Mouseflags includes LeftRelease so we can stop the Update code.
In the CallbackCode itself the LeftSingle will only occur once so we add 0.4 seconds to the sim time and write that into (O:XMLVAR_FirstUpdateTime). Now the update code will become active after a small pause (0.4 seconds) and then continue at the Frequency rate thus providing the continuous adjustment while the mouse button is held down. The LeftRelease will reset the (O:XMLVAR_FirstUpdateTime) to zero to stop the update otherwise it would go on forever. O: variables (as used in (O:XMLVAR_FirstUpdateTime)) seem to be Component dependent. The (O:XMLVAR_FirstUpdateTime) in "MouseZone_Altimeter_Inc" is not affected by the one in "MouseZone_Altimeter_Dec". I found this out in testing when I didn't have a LeftRelease for one of the zones. If the O: variable was global then clicking on the other zone should have stopped the update but it didn't.
Another example:
This is the code for a radio unit with a 12 key pad for user input. Hold the button down and then lift it when the mouse is released. No coding beyond the animation is done in this mouse interaction. I will process the inputs within the radio gauge itself. This is the perfect opportunity to create my own template and then I can simply call it 12 times for each key.
Here is the template where I combine our previous examples, animation, emissive and mouse rectangle, all into one template. This template takes in one parameter #KEY# which is used in the anim name, tooltip and L:var
Code:
<Template Name = "ANTS_a582_radio_button">
<UseTemplate Name="ASOBO_GT_Anim_Code">
<ANIM_NAME>a582_radio_button_#KEY#</ANIM_NAME>
<ANIM_CODE>(L:radio_button_#KEY#,number)</ANIM_CODE>
<ANIM_LAG>200</ANIM_LAG>
<ANIM_LENGTH>20</ANIM_LENGTH>
</UseTemplate>
<Material>
<EmissiveFactor>
<Parameter>
<Code>put some XML code in here to control the self illumination of the buttons</Code>
</Parameter>
<OverrideBaseEmissive>TRUE</OverrideBaseEmissive>
</EmissiveFactor>
</Material>
<UseTemplate Name="ASOBO_GT_MouseRect">
<TOOLTIPID>#KEY#</TOOLTIPID>
<CURSOR>Hand</CURSOR>
<MOUSEFLAGS_DEFAULT_IM>LeftSingle+LeftRelease</MOUSEFLAGS_DEFAULT_IM>
<CALLBACKCODE_DEFAULT_IM>
(M:Event) 'LeftSingle' scmi 0 == if{ 20 (>L:radio_button_#KEY#,number) }
(M:Event) 'LeftRelease' scmi 0 == if{ 0 (>L:radio_button_#KEY#,number) }
</CALLBACKCODE_DEFAULT_IM>
</UseTemplate>
</Template>
To call this template I simply use this code where KEY is the name/number of the key on the key pad. Repeat 12 times changing the component ID and Node and <KEY> to build the keypad.
Code:
<Component ID="Radio_Button_000" Node="Radio_Button_000">
<UseTemplate Name="ANTS_a582_radio_button">
<KEY>0</KEY>
</UseTemplate>
</Component>
And so ends the lesson. This is just a brief introduction and I hope it gives you an idea of the underlying core at work here. The examples are very simple but they all use the core templates without the confusing plethora of templates and subtemplates that Asobo uses. Hopefully this will allow you to better understand how the Asobo templates work and if there isn't a template that works for you then you should be able to create your own for your needs.