Examine.xml

Assuming I understand what is going on in Examine.xml, how do I determine what it is pulling back as the Description, format, values, line break, etc, when you rt-click and examine something.

The OnAdd script lists as OnAdd=UIScene_OnAdd_AddExamineScreen(“EXAMINE_NAME_TEXT”,“EXAMINE_DESCRIPTION_TEXT”,“IDENTIFY_BUTTON”)

Lower in the examine.xml, it shows the Name, a horizonal line, then that Description text as being displayed.

How do I control what is returned by the function that queries an item for description, plus seems to build a list of its properties.

Can I control what properties it returns, how it lists them and so on?

I should probably ask the real question I’m trying to get at; how do I modify the examine.xml, and replace whatever it’s doing to generate and pull a string with my own function that creates and returns a description string?

I can write the function that generates and returns a string with what information I want to be seen when you rt-click and Examine an item or placeable, but I’m having hard time grasping how the form displays the return string of a function.

1 Like

First off, set scriptloadable="true" in the UIScene of Examine.xml ( aka. "SCREEN_EXAMINE" )

also in the UIScene add

OnAdd0='UIObject_Misc_ExecuteServerScript("gui_effects")'

gui_effects is your script file – should have prefix gui_

 
Then here’s what my HEADER_LISTBOX looks like →

	<UIListBox name="HEADER_LISTBOX" x="16" y="197" width="375" height="405" yPadding="0" xPadding="3"
	showpartialchild="true" unequalcontrols="true" scrollsegmentsize="20" hidescrollbarwhennotneeded="true" >

		<UIText name="EXAMINE_DESCRIPTION_TEXT" width="PARENT_WIDTH" height="DYNAMIC"
		align="left" valign="top" color="303030" fontfamily="NWN1_Dialog" style="bold" />

		<UIText name="kL_EXAMINE_EFFECTS" width="PARENT_WIDTH" height="DYNAMIC"
		align="left" valign="top" color="303030" fontfamily="NWN1_Dialog" style="bold" />

		<UIScrollBar name="SB" style="STYLE_SB_THIN" />
	</UIListBox>

 
finally, in your script →

	string sEffects = ; // construct the string
	SetGUIObjectText(oPC, "SCREEN_EXAMINE", "kL_EXAMINE_EFFECTS", -1, sEffects);

 
Note that "kL_EXAMINE_EFFECTS" is the name of the UIText element that sEffects appears in ( but name your UIText-element whatever you want ofc )

 
you might have problems with string GetDescription(object oTarget) (if you use it) … iirc it doesn’t like to return the description on a blueprint, only text that has been set with void SetDescription(object oTarget, string sDescription)

ps. make sure that the width/height of the text element(s) are large enough, else the text in that element won’t show at all.

 
 
@Lance_Botelle might have somethin’ to add …

3 Likes

pps.

that’s a bunch of hardcoded stuff …

@THughes281

EDIT: See also my potentially easier post solution below this post.

Bottom line for me: I could not find it. If I could, then it would allow me to tidy my own code and have saved me around a year’s worth of coding around the problem (if you include the bug fixing time).

I needed to have the xml return the description string text to allow me to update my description pane in my inventory XML. It would have been great to find that and do one simple call, but to this day, it has evaded me, and as @kevL_s suggested, I believe it is most likely hard-coded. If you or another should discover it elsewhere, then I would also be pleased to know, as I could, perhaps, make my own code a lot more efficient.

If you want to see what I did to make use of it, check out my module …

WARNING: It does some crazy stuff, involving a lot of careful timing, and then has other points to consider throughout. Bottom line: It involves scripts executing and referencing scripts under careful timing.

Also, maybe you posting your scripts to date may give more of an idea of where you are heading and what it is you are actually trying to do? Or, can you explain what it is you are trying to do in a more gameplay way of describing?

P.S. It’s hard to understand exactly what it is you have currently done. I mean, if you have managed to get a description string returned, then that would suggest you have mastered something, but then it’s not clear as you then say you do not know how to use it. Some clarity required please. :smiley:

P.P.S. If, it is as I originally think you mean it, then my first answer stands.

My steps were along these lines … (very much simplified) …

  1. Force an examine, which generates the “description” text.
  2. Store this text on the item object.
  3. Use this string when required for my functions.
2 Likes

@THughes281

Actually, having read this again, I’ll answer in another way … although see my post above too. :thinking:

As @kevL_s says, you can use SetDescription to add whatever text you require. BUT, this function does not work, unless you have removed/edited the original text from said object/item in the toolset at build time, or, iirc, you do an initial examine action. :thinking: Then GetDescription works to retrieve it again.

Therefore, subject to what you are aiming to do, the solution ranges between “easy” to “difficult”. Let us know.

1 Like

Thank you both for very quick replies and all the great information, I appreciate it!

Goal is to allow player to Examine an item or a placeable (right-click it, select Examine) and have the returned text be dynamic, and produced by a function I write that returns a string.

It does not necessarily need to use the Description, identified or not, that you set on the Item or Placeable from within the toolset. The returned text displayed on the Examine.xml might be based on player’s intelligence, or their appraise skill, or race, or quest states, etc. I have not completely developed those standards yet, or how complex I want to make this.
What I want, essentially, is to not have a simple boolean of “identified” determine what a player sees when they examine things.

That’s the short answer. I can go on for many more paragraphs of details if you want! :slight_smile:

and speaking of such, how hard is it to create a completely new right-click context option called “Appraise” that opens up its own xml form lol?

Thank you for the information.
I wanted to use this concept for many things, but I’ll describe what I wanted in terms of crafting

Some found items are purely reagents for crafting. Let’s say Venison, Turnip, Salt, Chicken Liver are all ingredients used in provisioning. In a very simple example, player examines the Venison and the “Examine” text will return what known provisioning recipes using Venison the player knows, as well as the approximate gold value if player doesn’t care about crafting and just wants to sell.

The reagent items all have a preset variable stored on them, nCraftType, whose value refers to what information is checked when Examined. Example, nCraftType == 2 denotes this is a Provisioning ingredient.

The called examine function will check the local int on the item being examined, pull out nCraftType and check case of nCraftType == 2 to put together the string to return.
Something along lines of:

Venison
Type: Provisioning
Value: 30 gp
Recipes: Venison Stew, Roast Venison, and so on

EDIT: I know what I’m describing is not complicated and sounds like could just enter that text in the Description of the reagent. But this is just a simple example, I have more complicated ideas for it that wouldn’t work with static text entered in the toolset.

1 Like

Looking forward to testing this all out.
I appreciate the detailed example.

1 Like

@THughes281

The examine GUI - where to begin… :thinking: I have had a love-hate relationship when altering code with this xml and its related contextmenu.xml. Bottom line, they can get quite involved, but what you are after may be reasonably straightforward. It’s hard to tell at this stage.

By the way, if you have not yet done much with xml coding, then I strongly recommend my XML tutorial. And, as I say in the first post, if you want to see some examples of homebrew coding around these two xml’s, then take a look at my module.

If I understand your example, here you would edit the basic item in the toolset to have whatever description you want to have on it as a general rule. Then, when you have done this, it effectively “unlocks” the ability for you to use the SetDescription and GetDescription functions.

Then, if I understand you correctly, the next stage is to update that text depending upon the PC that examines the item if/when they examine it. Unfortunately, I have changed where that info is displayed in my campaign, and so extra explanation will be out of context for you. However …

Basically, you could also add lines of text to the basic Examine GUI, which you then add or remove via the string you wish to add/alter. Let me give you an example by posting a direct copy of a script I use in my own setup, which may give you the idea of what I am trying to explain… but do not let it confuse you, I hope. It purely serves as an example, and would not be of any use to you in any other way.

The point is, you may be best just adding additional EXAMINE PANES that you can add extra texts to, which are updated according to when you choose to.

I hope this helps… ask if you have any questions. (I may have totally misunderstood what you are after.)

BTW, not sure why this does not post properly. :thinking:

///////////////////////////////////////////////////////////////////////////////////////////////////
// Adds extra text to creatures/items main description, By Lance Botelle. (Edited EXAMINE GUI also.)
// Added TEXT is assigned either PERMANENT (DEFAULT) or TEMPORARY status. 
// NB: ITEMS: SINGLE TARGET SPECIFIC : DO NOT USE WITH STACKED ITEMS. Tag and variables are preserved.
// PERMANENT texts can normally be appended. A SINGLE TEMPORARY text must be updated as required.
// TEXT IS INSERTED AFTER NORMAL DESCRIPTIVE TEXT: "PERMTEXT" + "TEMPTEXT". (THEN ANY ITEM PROPERTIES.)
///////////////////////////////////////////////////////////////////////////////////////////////////
// CREATURES USE OF XML EXTRA PANE CALLED "POST_DESCRIPTION_TEXT"
// A) A PERMANENT text is normally set only once. (Theorhetically could be changed as required.)
// B) TEMPORARY TEXTS USED FOR ANY CURRENT EFFECTS THE CREATURE HAS ON THEM AT EXAMINE TIME ONLY!
///////////////////////////////////////////////////////////////////////////////////////////////////
// ITEMS JUST APPEND THIER NORMAL DESCRIPTION UPDATED WHEN CLICKED ON. "ITEMDECS" UPDATES.
// A) PERMANENT texts are normally appended to any exisiting such texts as required.
// B) TEMPORARY text can only be ONE at a time, so apply sparingly. (NB; Properties handled elsehwere.) 
///////////////////////////////////////////////////////////////////////////////////////////////////
void LBAddToDescription(object oTarget, string sTextAdd = "", int iItemTemp = 0); 
void LBAddToDescription(object oTarget, string sTextAdd = "", int iItemTemp = 0)
{
	// CREATURE POST DESCRIPTION TEXT IS ALWAYS PERMANET THIS WAY. (CAN OVERWRITE)
	if(GetObjectType(oTarget) == OBJECT_TYPE_CREATURE)
	{		
		SetLocalString(oTarget, "POSTDESCTEXT", sTextAdd);
	}
	
	else
	{			
		string sPERMPLUS = GetLocalString(oTarget, "PERMTEXT");
		string sTEMP = GetLocalString(oTarget, "TEMPTEXT");				
		
		// UPDATE (OR ADD TO) A PERMANENT TEXT
		if(iItemTemp == 0)
		{				
			// DO NOT DUPLICATE ANY POTENTIAL EXISTING TEXT
			if(FindSubString(sPERMPLUS, sTextAdd) == -1)
			{
				sPERMPLUS = sPERMPLUS + "\n" + sTextAdd;	
				SetLocalString(oTarget, "PERMTEXT", sPERMPLUS);
			}
			
			else
			{
				return;						
			}
		}
		
		// UPDATE TEMP TEXT
		else if(sTextAdd != "")
		{	
			// CAN ONLY BE ONE INSTANCE FOR ITEMS (OVERWRITES)
			SetLocalString(oTarget, "TEMPTEXT", sTEMP);
			sTEMP = sTextAdd;			
		}
		
		// DELETE TEMP TEXT
		else
		{
			DeleteLocalString(oTarget, "TEMPTEXT");	
			sTEMP = "";		
		}
		
		// UPDATE STRING VARIABLE
		if(sPERMPLUS != "" && sTEMP != ""){sPERMPLUS = sPERMPLUS + "\n" + sTEMP;}
		else if(sPERMPLUS == ""){sPERMPLUS = sTEMP;}
								
		SetLocalString(oTarget, "POSTDESCTEXT", sPERMPLUS);
		
		DeleteLocalString(oTarget, "ITEMDECS");
	}
}

And here is a snippet of the examine.xml where it is used…

<UIText name="PRE_DESCRIPTION_TEXT" text="" width=PARENT_WIDTH height=DYNAMIC align=left valign=top color="3d3131" fontfamily="NWN1_Dialog" style="bold" />

	<UIText name="EXAMINE_DESCRIPTION_TEXT" text="" width=PARENT_WIDTH height=DYNAMIC align=left valign=top color="3d3131" 	fontfamily="NWN1_Dialog" style="bold"		
	
	OnUpdate0=UIObject_Misc_ExtractData("self:","objectid",0,local:0)
	OnUpdate1=UIObject_Misc_SetLocalVarString(local:0)
	OnUpdate2=UIObject_MISC_ExecuteServerScript("gui_inventory_info","",local:0,"UPDATE")/>

	<UIText name="POST_DESCRIPTION_TEXT" text="" width=PARENT_WIDTH height=DYNAMIC align=left valign=top 	color="3d3131" fontfamily="NWN1_Dialog" style="bold" />
2 Likes

In the script, I’m having trouble getting the object that I right-clicked to Examine.
OBJECT_SELF gives the PC, GetItemActivated did not work.
How do I know the inventory item I right-clicked in that gui_examine script?

on my Examine, some data gets printed custom. The basic answer is GetPlayerCurrentTarget() – but then you might want to distinguish the target’s, uh, circumstances … eg. is it a Creature, is it an item in a store, etc →

void main()
{
	object oPcc = GetControlledCharacter(OBJECT_SELF);

	string sReputation, sRange, sHitpoints;

	if (!GetIsOverlandMap(GetArea(oPcc)))
	{
		object oTarget = GetPlayerCurrentTarget(oPcc);
		if (GetIsObjectValid(oTarget))
		{
			string sColor;

			// determine reputation and hitpoints
			if (GetObjectType(oTarget) == OBJECT_TYPE_CREATURE)
			{
				int iRep;

				if (GetFactionEqual(oTarget, oPcc))
				{
					iRep = 100;
					sReputation = c_GREEN + c_FRIENDLY;
				}
				else
				{
					iRep = GetReputation(oPcc, oTarget);

					if      (GetIsFriend( oTarget, oPcc)) sReputation = c_GREEN + c_FRIENDLY;
					else if (GetIsNeutral(oTarget, oPcc)) sReputation = c_BROWN + c_NEUTRAL;
					else                                  sReputation = c_DRED  + c_HOSTILE; // if (GetIsEnemy(oTarget, oPcc))
				}
				sReputation += c_END + c_SPACE + c_DGREY + IntToString(iRep) + c_END;

				int   iHp = GetCurrentHitPoints(oTarget);
				float fHp = IntToFloat(iHp);
				if (fHp < 0.f) fHp = 0.f;

				float fRatio = fHp / IntToFloat(GetMaxHitPoints(oTarget));
				if      (fRatio <= 0.00f) sColor = c_PINK; // see DamageLevels.2da
				else if (fRatio <= 0.25f) sColor = c_RED;
				else if (fRatio <= 0.45f) sColor = c_SIENNA;
				else if (fRatio <= 0.76f) sColor = c_OLIVE;
				else if (fRatio <= 0.93f) sColor = c_YELLOW;
				else                      sColor = c_WHITE;

				sHitpoints = sColor + IntToString(iHp) + c_END + c_SPACE + c_DGREY + c_HP + c_END;
			}

			// determine range and location
			object oPossessor;

			if (GetObjectType(oTarget) == OBJECT_TYPE_ITEM
				&& GetIsObjectValid(oPossessor = GetItemPossessor(oTarget)))
			{
				string sContainer;

				if (oPossessor == oPcc)
				{
					sContainer = c_PERSONAL;
				}
				else
				{
					switch (GetObjectType(oPossessor))
					{
						case OBJECT_TYPE_CREATURE:
							if (GetFactionEqual(oPossessor, oPcc))
							{
								sContainer = c_PARTY;
							}
							else //if (GetIsDead(oPossessor))	// NOTE: Player can never access a non-party
							{									// creature's inventory unless it's dead.
								sContainer = c_CORPSE;
							}
							break;

						case OBJECT_TYPE_PLACEABLE:
							sContainer = c_INVENTORY;
							break;

						case OBJECT_TYPE_STORE:
							sContainer = c_STORE;
							break;

						default:
							sContainer = "</c><c=crimson>ERROR";
							break;
					}
				}

/*				int iType = GetObjectType(oPossessor);

				if      (oPcc == oPossessor)                sContainer = c_PERSONAL;
				else if (iType == OBJECT_TYPE_CREATURE
					  && GetIsDead(oPossessor))             sContainer = c_CORPSE;
				else if (iType == OBJECT_TYPE_PLACEABLE)    sContainer = c_INVENTORY;
				else if (GetFactionEqual(oPossessor, oPcc)) sContainer = c_PARTY;
				else                                        sContainer = c_STORE; */

				sRange = c_PERU + sContainer + c_END;
			}
			else
			{
				float fRange = GetDistanceBetween(oTarget, oPcc);

				if      (fRange <= 10.f) sColor = c_DRED;
				else if (fRange <= 20.f) sColor = c_BROWN;
				else if (fRange <= 40.f) sColor = c_GREEN;
				else if (fRange <= 50.f) sColor = c_DBLUE;
				else                     sColor = c_BISQUE;

				sRange = sColor + FloatToString(fRange, 3, 1) + c_SPACE + c_M + c_END;
			}
		}
	}

	SetGUIObjectText(oPcc, c_GUI_EXAMINE, c_kL_REP,      -1, sReputation);
	SetGUIObjectText(oPcc, c_GUI_EXAMINE, c_kL_RANGE,    -1, sRange);
	SetGUIObjectText(oPcc, c_GUI_EXAMINE, c_kL_HITPOINT, -1, sHitpoints);
}

 
such data needs to be updated constantly so it fires from an OnUpdate gui-event →

	<!-- kevL's Current Target Info, 11.03.04 -->
	<UIPane name="kL_TARGET" x="ALIGN_CENTER" y="145" width="325" height="40"
	OnUpdate='UIObject_Misc_ExecuteServerScript("gui_kl_updaterep")' update="true" updaterate="0.3" >
		<UIText name="kL_TARGET_TEXT" x="0" y="0" width="54" height="DYNAMIC"
		align="left" valign="middle" multiline="false" text="Target :"
		fontfamily="NWN1_Dialog" style="bold" color="303030" />
		<UIText name="kL_TARGET_INFO" x="55" y="0" width="270" height="DYNAMIC"
		align="left" valign="middle" color="maroon" fontfamily="NWN1_Dialog" style="bold" multiline="false"
		OnUpdate='UIText_OnUpdate_DisplayTargetObjectName()' update="true" updaterate="0.3f" />
		<UIText name="kL_RANGE_TEXT" x="0" y="19" width="54" height="DYNAMIC" multiline="false"
		align="left" valign="middle" text="Range :" fontfamily="NWN1_Dialog" style="bold" color="303030" />
		<UIText name="kL_RANGE_INFO" x="55" y="19" width="65" height="DYNAMIC"
		align="left" valign="middle" fontfamily="NWN1_Dialog" style="bold" multiline="false" />
		<UIText name="kL_HITPOINT_INFO" x="124" y="19" width="73" height="DYNAMIC"
		align="right" valign="middle" fontfamily="NWN1_Dialog" style="bold" multiline="false" />
<!--	<UIText name="kL_HITPOINT_TEXT" x="178" y="19" width="17" height="DYNAMIC" multiline="false"
		align="left" valign="middle" text="hp" fontfamily="NWN1_Dialog" style="bold" color="303030" /> -->
		<UIText name="kL_REPUTATION_INFO" x="215" y="19" width="92" height="DYNAMIC"
		align="right" valign="middle" fontfamily="NWN1_Dialog" style="bold" multiline="false" />
	</UIPane>
3 Likes

its not hard its just tedious … x= / y= / width= / height= blah blah bla

A trick to use when laying out a gui-screen: set idleexpiretime="0.1" in the UIScene

that forces the gui to unload and reload when it’s closed and opened (make and save changes to the xml between close and reload)

2 Likes

did you use the ```nwscript identifier?

1 Like

@kevL_s

Yes. :+1:

Another potential issue you’ll encounter is that this stays active until the player selects a new target. Otherwise, as KevL says, use GetPlayerCurrentTarget.

2 Likes

Thank you, Lance. I appreciate all of your input.

2 Likes

I haven’t read all posts here that thoroughly, but I have a question that is kind of connected to this:

If the PC clicks and examines a placeable, could one place a script that activates when that happens? Could you perhaps use GetLastUsedBy(); in a script (on the OnUsed event) for that, or does right-clicking on an object not count as it being used?

@andgalf

Yes, I have added extra code at the end of my own gui_examine that can (and does) do this. If I recall correctly (as it has been some time since I last looked at this area of code closely), it was part of the general complication of getting everything to work together with these hooks.

Fired via Examine Open and then using GetPCCurrentlyControlled.

@Lance_Botelle - So there’s no real easy way to do this then? You can’t just put some code on the OnUsed? You have to do it with xml coding?

EDIT: I tried to do some simple changes to the examine.xml, partly by reading @kevL_s posts above:

<?xml version="1.0" encoding="NWN2UI">

<UIScene name="SCREEN_EXAMINE" x=ALIGN_CENTER y=ALIGN_CENTER modal=false width=309 height=400 fadeout="0.3" fadein="0.3" draggable=true backoutkey=true scriptloadable=true
	OnAdd=UIScene_OnAdd_AddExamineScreen("EXAMINE_NAME_TEXT","EXAMINE_DESCRIPTION_TEXT","IDENTIFY_BUTTON")
	OnAdd0=UIObject_Misc_ExecuteServerScript("gui_effects")
	OnRemove=UIScene_OnRemove_RemoveExamineScreen() priority="SCENE_INGAME"/>

	<UIText name="EXAMINE_SCREEN_NAME" strref="549" uppercase=true x=60 y=12 width=208 height=20 align=left valign=middle fontfamily="Title_Font" style="1" />
	<UIIcon name="EXAMINE_DETAIL" img="ia_examine.tga" x=15 y=12 width=40 height=40 />

	<!-- Close Button -->
	<UIButton name="CloseButton" x=274 y=8 style="STYLE_CLOSE_BUTTON" 
		OnLeftClick=UIButton_Input_ScreenClose()>
	</UIButton>

	<UIButton name="IDENTIFY_BUTTON" x=ALIGN_CENTER y=45 width=140 height=29
	OnLeftClick=UIButton_Input_HandleIdentify() disabledcolor="white" >
		<UIText name="IDENTIFY_TEXT" width=PARENT_WIDTH height=PARENT_HEIGHT align=center valign=middle uppercase=truefontfamily="Default" style="bold" />
		<UIFrame state=up		fill="b_subtab_normal.tga" />
		<UIFrame state=down		fill="b_subtab_pressed.tga" />
		<UIFrame state=focused	fill="b_subtab_normal.tga" />
		<UIFrame state=hilited	fill="b_subtab_normal.tga" />
		<UIFrame state=hifocus	fill="b_subtab_pressed.tga" />
		<UIFrame state=disabled	fill="b_subtab_disabled.tga" />
	</UIButton>

	<!-- Item Name -->	
	<UIText name="EXAMINE_NAME_TEXT" x=ALIGN_CENTER y=87 width=274 height=55 align=center valign=middle fontfamily="Body_Font" style="bold" multiline=true maxlines=3 />

	<UIIcon name="HZ_BAR" img="grid_pixel.tga" x=10 y=138 width=295 height=1 />
	<UIListBox name="HEADER_LISTBOX" x=26 y=139 width=272 height=251 yPadding=0 xPadding=0 showpartialchild=true
	unequalcontrols=true scrollsegmentsize=30 hidescrollbarwhennotneeded=true >

		<UIText name="EXAMINE_DESCRIPTION_TEXT" width=PARENT_WIDTH height=DYNAMIC align=left valign=top color="3d3131" fontfamily="NWN1_Dialog" style="bold" />
		
		<UIScrollBar name="SB" style="STYLE_SB_THIN"></UIScrollBar>	
	</UIListBox>	

	<!-- Background Image -->
	<UIIcon name="EXAMINE_BACKGROUND_TOP" img="idesc_top_bg.tga" x=10 y=8 width=290 height=80 />
	<UIIcon name="EXAMINE_BACKGROUND" img="idesc_paper_bg.tga" x=10 y=88 width=290 height=303 />
	<UIFrame x=0 y=0 width=PARENT_WIDTH height=PARENT_HEIGHT topleft="frame1_tl.tga" topright="frame1_tr.tga" bottomleft="frame1_bl.tga"
		bottomright="frame1_BR.tga" top="frame1_t.tga" bottom="frame1_b.tga"
		left="frame1_l.tga" right="frame1_r.tga" border=32 />	

I put this version of the xml in my campaign folder. Then I did a script called gui_effects in which I had this simple code:

void main()
{

	object oPC = GetFirstPC();
	
	SendMessageToPC(oPC,"My code is running");
}

When clicking on the placeable, no message was displayed though, so I must have missed something or done something wrong.

EDIT2: I tried changing more stuff, but nothing happens ingame.