Friday, March 30, 2007

Scripting Update

Here's a list many of the scripting changes made in 1.06 so far. Most of them come from the 1.67 and 1.68 updates of NWN1.

ActionRest() - The bIgnoreNoRestFlag wasn't actually doing anything. It will now force rest even if the area is flagged NoRest or there are monsters nearby. If the character is still in combat, it will not force rest.

CreateItemOnObject() - Added new string parameter to provide the newly created object a new tag.

EffectHeal() now works on doors and placeables.

GetAbilityScore() now takes a 'nBaseAttribute' parameter to allow querying just the base attribute.

OnClick event for placeables that fires whether or not the player can actually path to them.

SpeakConversationOneLiner() now takes a parameter to determine if the spoken line should have whisper, talk, or shout volume.

Added:
CreateTrapAtLocation()
CreateTrapOnObject()
GetAreaSize()
GetInfiniteFlag()
GetKeyRequiredFeedback()
GetPickpocketableFlag()
GetPlaceableLastClickedBy()
GetTrapActive() - (Now works on doors and placeables too)
GetTrapRecoverable()
SetFortitudeSavingThrow()
SetReflexSavingThrow()
SetWillSavingThrow()
SetHardness
SetInfiniteFlag()
SetKeyRequiredFeedback()
SetLockKeyRequired()
SetLockKeyTag()
SetLockLockable()
SetLockLockDC()
SetLockUnlockDC()
SetModuleXPScale()
SetPickpocketableFlag()
SetTrapDetectable()
SetTrapDetectDC()
SetTrapDisarmable()
SetTrapDisarmDC()
SetTrapKeyTag()
SetTrapOneShot()
SetTrapRecoverable()
SetUseableFlag()


I also added a couple console commands to help with GUI debugging
openuiscreen []
closeuiscreen
unloaduiscreen

Also added a console command:
guidebug [MOUSEINPUT] [MOUSEOVER] [CALLBACKS]

This brings up an extra 'chat' window and will provide engine-level debugging info about mouse-overs, mouseinput, or callback execution. This feature isn't really finished as of this post, but I hope to spend some more time polishing it up for 1.06.

There are some new toolset features and bug fixes. The bug with seams showing up between tiles has been fixed as well as issues with Specular lighting not looking right. There is also now 5-level terrain undo for grass, water, and terrain adjustment and some improvements to some of the brushes.

There's some other things as well, but this is a pretty good sampling of the new things that will be in 1.06.

Wednesday, March 14, 2007

1.06 Progress

The 1.05 beta was posted today. One tweak that I intend to have made before 1.05 final is to remove the 'tabs = 5 spaces' change in the script editor. While some people do want some option like that, I think it needs to be implemented as something that can be turned on/off/adjusted, so for now, it's best to leave it out and re-implement it correctly in 1.06.

As far as 1.06 goes, I've spent a lot of time this week integrating a lot of the NWN1 1.67 changes, especially the script functions. I won't go into details now, but I will once I'm done. But in summary, most of the script functions that have been missing from NWN1's later patches should be in 1.06.

Monday, March 5, 2007

GDC

Warning, incoming period of silence. :)
I'll be at GDC this week, so probably won't have any updates until next week.

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

Friday, March 2, 2007

IntToObject

This is just a quick note to mention that I've added:
object IntToObject( int nInt );

and

int ObjectToInt( object oObj );

scripting functions for 1.06.

Thursday, March 1, 2007

UIObject_Misc_ExtractData

Sorry for the delay in posting, but if you're into custom UI work, this information will have been worth the wait. :)

A new callback in 1.06 is:
UIObject_Misc_ExtractData( sourceobject, datatype, index, destinationvar )

This callback can be used to extract the data imbedded by the engine within UI Objects. First I'll go over the parameters, then I'll give some examples.

sourceobject
This parameter is used to define which UI Object we are extracting data from. It accepts the following options:

selected:listboxname - By replacing 'listboxname' with the name of the listbox you're interested in, you can tell this callback to use the selected row within the list box as the source of the data.

self: - By using 'self:', you are telling the callback to use the object that invoked the callback as the source from which to extract the data.

context:uiobject - By using context:uiobject:', you are telling the callback to use the UI Object that invoked the context menu. This obviously only means anything if the callback is being called from a context menu node. If called from anywhere else, the callback will do nothing.

objectname - If you enter any string besides the above 3 options, the callback will search for a UIObject within the same scene as the object executing this callback that has that name.

datatype
The datatype tells the callback which data type it should be extracting from the UIObject. Note that regardless of what the original data type is, the data type that gets stored in the GUI Variable at the end will always be a string.

The data type values are:
bool -'true' or 'false' are the strings that will get stored in the GUI variable
int
float
string
strref
objectid

Again, it's important to remember that no matter what the originating data type is, the result will be stored as a string variable within the GUI variable specified.

index
This is the index of the data to extract. If the index is invalid for the UIObject, a default value will get stored in the GUI variable (Either "" or "0" or "false" depending on what the originating data type was).

destinationvar
This can be a local:# or global:#. The type of the stored data will always be a string.

Example 1
Now for some real examples that will work.

The grid icons in the inventory grid have the following data imbedded in them:
OBJECT_ID(0) = The object currently displayed in that icon
OBJECT_ID(1) = The object that was dispalyed in that icon on the previous frame
int(0) = The stacksize for that object on the previous update frame
int(1) = Indicates if the item is identified or not
int(2) = Indicates if the item is usable or not
int(3) = Indicates if the item is ghosted or not

It would be possible for me to change the inventory screen to drop items if I double right click on them.

I would edit the 'InvPrototypeButton' inside of inventory.xml and add:

OnRightDoubleClick0=UIObject_Misc_ExtractData("self:","objectid",0,local:3)
OnRightDoubleClick1=UIObject_Misc_ExecuteServerScript("gui_dropitem",local:3)

When I right double click that item, the first callback will extract the Object ID of the item and place it in local:3 as a string that looks like: "32144" or whatever the object ID is.

Then the 2nd callback would execute, asking the server to fire off the "gui_dropitem" script, and it would pass the 32144 value in as a parameter to the script (Either as a string "32144" or an integer 32144, depending on what the parameter type the script is expecting).

Now I will be adding an IntToObject() script funciton that will make it possible to take that int parameter and turn it into an object parameter. At that point, you could use the DropItem action and have the player drop that object.

Note that in this example, I would've had to remove the OnRightClick call to the context menu for the item button or it would end up blocking the double right click (Due to the context menu popping up and blocking the second click).

Example 2
This example is a lot more complicated, I'll try to explain it clearly. :)

The spell buttons in the game usually have the following pieces of data:
int(0) = The spell ID that the button represents
int(1) = The multi-class index that can cast that spell
int(2) = Not used anymore, so nothing.
int(3) = The meta magic feat, if any
int(4) = Whether or not this is a spontanously cast spell
int(5) = Whether or not this is a domain spell
int(6) = Whether or not this is a cheat-command spell cast

The values for int(3), the meta magic feat, are:
1 = Empower
2 = Extend
4 = Maximize
8 = Quicken
16 = Silent
32 = Still
64 = Persistent
128 = Permanent

The warlock invocations are also meta-magic feats, as follows (Note, I'm not sure how many of these are actually in the game, I'm just going down the list of constants in the code):
256 = Draining Blast
512 = Eldritch Spear
1024 = Frightful Blast
2048 = Hideous Blow
4096 = Beshadowed Blast
8192 = Brimstone Blast
16384 = Eldritch Chain
32768 = Hellrime Blast
65536 = Bewitching Blast
13102 = Eldritch Cone
262144 = Noxious Blast
524288 = Vitrioloic Blast
1048576 = Eldritch Doom
2097152 = Utterdark Blast

Let's say I want to make my own custom Examine Spell command for the spells_known.xml listing of spells.

First off, let's add our own custom radial menu (Another new feature in 1.06).

In spells_known.xml, I would go to the SPELLPANE_PROTO pane and add a
OnRightClick=UIObject_OnRadial_DisplayCustomRadial("root-customspell")

root-customspell will be the name of a new root node that I would then add to the contextmenu.xml

For now, that root node will only have 1 sub node called: node-spellexamine

node-spellexamine will be the name of a new radial node that I would make with the following callbacks:
OnLeftClickExpanded0=UIObject_Misc_ExtractData("context:uiobject","int",0,local:5)
OnLeftClickExpanded1=UIObject_Misc_ExecuteServerScript("gui_spellexamine",local:5)
OnLeftClickCollapsed0=UIObject_Misc_ExtractData("context:uiobject","int",0,local:5)
OnLeftClickCollapsed1=UIObject_Misc_ExecuteServerScript("gui_spellexamine",local:5)

Now when I go into the game and bring up the Spells Known screen and right click on the spell icon for a spell in the list, I will get a context menu with 1 item called 'Examine'. Clicking on that will first call:
UIObject_Misc_ExtractData()
which will extract the spell ID from int(0) on the button I brought the context menu up on and place that ID in local:5

Then it will fire
UIObject_Misc_ExecuteServerScript()
and pass along the spell ID (As stored in local:5) as the parameter to the script: gui_spellexamine

From there I can do whatever I want with that data. :)

To recap, the new features mentioned in this article are:
UIObject_Misc_ExtractData()

and

UIObject_OnRadial_DisplayCustomRadial()

Feel free to ask any time you would like me to provide a detailed list of what data is imbedded in a given UI Object. Some you'll probably be able to figure out on your own via trial and error, but I'll be happy to look into any specific UI Object if you need.