Monday, March 5, 2007

UIObject_Input_ActionTargetScript

This callback will one of the most powerful new tools when it comes to creating custom actions in the game.

UIObject_Input_ActionTargetScript(), new in 1.06, will let you make 'GUI actions' similar to the 'actions' that are created when you click on a spell or feat button to cast it, or on a button in the DM Choser that then has you select a target to do the action to.

Once the user clicks on a valid target, a bunch of data will be sent to the server along with a request to execute a script associated with the click.

Anyway, enough summary, here's the details:

UIObject_Input_ActionTargetScript("sValidTargetTypes",nValidCursor,nInvalidCursor,nSpellTargetIndex,bUseHostile,"sScriptName", script parameter list...)

sValidTargetTypes
This parameter is a string that will list the types of objects that will be valid to click on. If you enter a "", that means all targets are valid for this action. Otherwise, you can enter a list, seperated by commas or any other deliminator you want to use. The valid target names are:
self
creature
ground
item
door
placeable
trigger

For example: "creature,placeable" would mean that valid targets would include clicking on placeables and clicking on creatures.

nValidCursor
This is an integer constant to point to one of the cursors that the engine understands. You can only really use the cursors compiled into the game's executable. I will make a list of those constants at the end of this post. This is the cursor that you want to use when the cursor is over a valid target.

nInvalidCursor
Just like the nValidCursor parameter, except this is the cursor you want displayed when the mouse is over an invalid target. For example, if my valid targets were creatures and placeables, but the mouse was over open ground, this is the cursor that would be shown.

nSpellTargetIndex
This is a reference into the spelltarget.2da to indicate that you want to use one of the spell targeting projected textures like the ones used when targeting Area of Effect spells. If you do not want an AoE texture around the cursor, just enter -1 for this parameter.

bIsHostile
This tells the engine if you want to use the Hostile Texture column from spelltarget.2da or not. If you entered -1 for nSpellTargetIndex, then this parameter doesn't mean anything. You must enter either "true" or "false", even if you entered -1 for nSpellTargetIndex.

sScriptName
The name of the script you want to follow. This follows the same restrictions that UIObject_Misc_ExecuteServerScript() usese, in that the script name must start with 'gui_', or else the server will reject the request.

Parameter List
This is an unlimited list of parameters just like ExecuteServerScript() supports. However, there are a few extra parameters that this function can support:
target.object - This will replace the parameter with the OBJECT_ID of the object that the user clicked on.
target.x - This will replace the parameter with the X value of the position that the user clicked on as a float. If the user clicked on a specific object, instead of open ground, then this X will be the position of that object as far as the client knows. Since the client could be a few milliseconds out of synch with the server, the server may consider this object at a slightly different position than the client does.
target.y - Same as X, except for the Y value of the position vector.
target.z - Same as X, except for the Z value of the position vector.

For example, if I wanted to create a DM Action button that deleted whatever placeable the DM targetted as well as all placeables within 15 yards of the target, I would create a UIButton with the following callback:
OnLeftClick=UIObject_Input_ActionTargetScript("placeable",51,53,17,"true","gui_eraseplaceables","target:object")

The number 51 would be for the CURSOR_KILL cursor, the number 53 would be for the CURSOR_NOKILL cursor, the number 17 would be for row 17 in spelltarget.2da whcih is a large circle with a 30 yard width (so 15 yard radius), and the "true" is because I want it to use the hostile texture out of that 2DA. I want to use the clicked on placeable as my starting point, so I go ahead and pass that along as a parameter as well. It will be passed into gui_eraseplaceables as either a string or an int, depending on what parameter I have that script expecting.

Anyway, the list of cursor constants is:

#define MOUSECURSOR_DEFAULT 1
#define MOUSECURSOR_DEFAULT_DOWN 2
#define MOUSECURSOR_WALK 3
#define MOUSECURSOR_WALK_DOWN 4
#define MOUSECURSOR_NOWALK 5
#define MOUSECURSOR_NOWALK_DOWN 6
#define MOUSECURSOR_ATTACK 7
#define MOUSECURSOR_ATTACK_DOWN 8
#define MOUSECURSOR_NOATTACK 9
#define MOUSECURSOR_NOATTACK_DOWN 10
#define MOUSECURSOR_TALK 11
#define MOUSECURSOR_TALK_DOWN 12
#define MOUSECURSOR_NOTALK 13
#define MOUSECURSOR_NOTALK_DOWN 14
#define MOUSECURSOR_FOLLOW 15
#define MOUSECURSOR_FOLLOW_DOWN 16
#define MOUSECURSOR_EXAMINE 17
#define MOUSECURSOR_EXAMINE_DOWN 18
#define MOUSECURSOR_NOEXAMINE 19
#define MOUSECURSOR_NOEXAMINE_DOWN 20
#define MOUSECURSOR_TRANSITION 21
#define MOUSECURSOR_TRANSITION_DOWN 22
#define MOUSECURSOR_DOOR 23
#define MOUSECURSOR_DOOR_DOWN 24
#define MOUSECURSOR_USE 25
#define MOUSECURSOR_USE_DOWN 26
#define MOUSECURSOR_NOUSE 27
#define MOUSECURSOR_NOUSE_DOWN 28
#define MOUSECURSOR_MAGIC 29
#define MOUSECURSOR_MAGIC_DOWN 30
#define MOUSECURSOR_NOMAGIC 31
#define MOUSECURSOR_NOMAGIC_DOWN 32
#define MOUSECURSOR_DISARM 33
#define MOUSECURSOR_DISARM_DOWN 34
#define MOUSECURSOR_NODISARM 35
#define MOUSECURSOR_NODISARM_DOWN 36
#define MOUSECURSOR_ACTION 37
#define MOUSECURSOR_ACTION_DOWN 38
#define MOUSECURSOR_NOACTION 39
#define MOUSECURSOR_NOACTION_DOWN 40
#define MOUSECURSOR_LOCK 41
#define MOUSECURSOR_LOCK_DOWN 42
#define MOUSECURSOR_NOLOCK 43
#define MOUSECURSOR_NOLOCK_DOWN 44
#define MOUSECURSOR_PUSHPIN 45
#define MOUSECURSOR_PUSHPIN_DOWN 46
#define MOUSECURSOR_CREATE 47
#define MOUSECURSOR_CREATE_DOWN 48
#define MOUSECURSOR_NOCREATE 49
#define MOUSECURSOR_NOCREATE_DOWN 50
#define MOUSECURSOR_KILL 51
#define MOUSECURSOR_KILL_DOWN 52
#define MOUSECURSOR_NOKILL 53
#define MOUSECURSOR_NOKILL_DOWN 54
#define MOUSECURSOR_HEAL 55
#define MOUSECURSOR_HEAL_DOWN 56
#define MOUSECURSOR_NOHEAL 57
#define MOUSECURSOR_NOHEAL_DOWN 58
#define MOUSECURSOR_RUNARROW 59
#define MOUSECURSOR_WALKARROW 75
#define MOUSECURSOR_PICKUP 91
#define MOUSECURSOR_PICKUP_DOWN 92
#define MOUSECURSOR_CHATBOX_SIZING 99
#define MOUSECURSOR_NODEFAULT 125
#define MOUSECURSOR_NODEFAULT_DOWN 126
#define MOUSECURSOR_MAP 128
#define MOUSECURSOR_MAP_DOWN 127
#define MOUSECURSOR_NOMAP 130
#define MOUSECURSOR_NOMAP_DOWN 129
#define MOUSECURSOR_WAIT_DOWN 131
#define MOUSECURSOR_WAIT 132

18 comments:

Marc Paradise said...

Is a trap considered a trigger for purposes of target type?

Rich Taylor said...

It /should/. I need to do some testing with triggers before I can say for sure.

John Peterson said...
This comment has been removed by the author.
John Peterson said...

I can't wait to play around with this function. Wow! Very nice indeed.

John Peterson said...

Hmm, I was wondering something... Using this GUI function, would it be possible to "deactivate" the button for a period of time. Say, once the user clicks on it, it greys out for 5 minutes or something, and then becomes available again?

I suppose you could use this function in conjunction with the ability to hide UI elements to create a similar effect.

Rich Taylor said...

Well, there is a script function for enabling and disabling buttons. Or at least, I think there is.

The script that your ActionTargetScript() callback executes could then in turn disable the button that was used to fire itself. Then it could do a DelayCommand() for 5 seconds to re-enable the disabled button.

John Peterson said...

Going to have to look into this a bit then. :D Sounds like it could do what I want. Yay!

John Peterson said...

SetGUIObjectDisabled would be the function I believe. Just have the script execute that function with a TRUE to disable it and then a FALSE x number of seconds later to re-enable it.

Rich Taylor said...

Just an update on this:
Traps AND area transitions are 'trigger'.
Also, if you can see the triggers (I.e., you're a DM with 'show triggers' turned on, or you have DebugMode on and can see triggers), then the triggers you can see are valid targets for the 'trigger' type.

I can see refining the 'valid target' type options further down the line, like 'hostile creatures only', or 'traps, but not other kinds of triggers', etc. But the current functionality should be enough to get started with.

Heed said...

Hey Rich.

I think there's a bug in Beta 1.06 in passing the object ID as a string using this function -- I always get an empty string as the passed value. I switched to using an int with no other code changes and I get various object ID's no problem.

From what you've said, I should be able to pass the object ID as a string as long as that's the parameter the executed script expects. I'm using the parameter "target:object" in a string parameter slot, but I get an empty string. I moved that parameter to an int slot and get various ints.

Here's the function I'm using:
(parameter list: string, int, float, string [same parameter list as the contextmenu.xml example -- just recently realized I could have unlimited parameters, but these are enough for my current code])

String:

UIObject_Input_ActionTargetScript("ground,creature",37,39,-1,"false","gui_hss_pc_tool","target:object","1","7.0")

Int:

UIObject_Input_ActionTargetScript("ground,creature",37,39,-1,"false","gui_hss_pc_tool","","target:object","7.0")

Thanks.

Anonymous said...

Is this going to work?

UIObject_Input_ActionTargetScript("ground,creature,placeable",29,53,15,false,"gui_destroytarget","target:object")

//gui_destroytarget
void main(int nTarget)
{
object oTarget = IntToObject(nTarget);
DestroyObject(oTarget);
}

Heed said...

Hey Rich,

I can't seem to get any cursor other than the spell cast and no spell cast ones to appear. Whatever I enter for the cursor ints I get the spell cursors.

Not a huge deal, but it doesn't seem to work properly as described.

Thanks.

Heed said...

The parameter syntax listed for the position values is incorrect in the article. The article lists "target.x" as the syntax, but it is actually "target:x".

Passing floats as strings also seems to have the same issue as passing ints as strings -- empty string.

Just a heads up.

Rich Taylor said...

I'm looking into the bug with not being able to get any other type of mouse cursor icon right now.

As far as the passing parameters as strings behaving wrong, I think that's tied into the over all major problem with parameter parsing on the engine side... Mn. I'll have to investigate that some more. Thanks..

-Rich

Rich Taylor said...

Okay, the cursor bug should be fixed in 1.11.

-Rich

Heed said...

Thanks Rich -- much appreciated. I would hazard a guess you end up doing a fair bit of this stuff "above and beyond" your regular duties. So, just a note to say that it doesn't go unnoticed and unappreciated. :)

Oh, and did you happen to notice my comment on the hideoverride entry? It would be a boon to have that functioning for the context menu.

Anonymous said...

What scripting function shall I use to determine the targeted object? I've restricted to self and creature but OBJECT_SELF is the UI function activator.
Any idea?

Master Changer said...

Hi Rich,

I imagine you're probably not checking this anymore, but in case you are, I wanted to ask a question about this function.

I've tested it with many different spelltarget.2da values. It seems to work beautifully with all circles, points, and rectangles. However, with cones (3,4,5) or bolts (12), NWN2 immediately crashes.

I wonder if this has to with those target UI types being concerned with the point of origin, unlike the other shapes?

Any assistance would be much appreciated.
-MasterChanger