Using ScrAddAction To Change Dockscreens Without Overwrting

This is a moderated forum that collects tutorials, guides, and references for creating Transcendence extensions and scripts.
Post Reply
TranscendentGeek
Commonwealth Pilot
Commonwealth Pilot
Posts: 88
Joined: Fri Mar 09, 2012 6:13 pm

Mon May 07, 2012 3:07 am

Using ScrAddAction To Change Dockscreens Without Overwrting

Once upon a time the only way to change a vanilla dockscreen was to copy the entire screen into a mod file and rewrite it. Then in version 1.04 of Transcendence a new function called “scrAddAction” was added. The following tutorial will walk you through using it by creating a fully functioning mod. For this tutorial I am going to use my "Identify Before Buying" mod, which adds a new option to the buy screen that will allow you to identify objects before buying them for a modest fee.

First let’s look at the function. It has the following syntax:

Code: Select all

(scrAddAction screen actionID pos label [key] [special] code)
  • screen: dockscreen to work on, usually gScreen
    actionID: string to identify action with later
    pos: number of action to insert this action before
    label: string display text of action
    [key]: string shortcut key
    [special]: list or token describing special functionality
    code: code to execute when player selects action
The above function needs to be called from an event. The sepecific event is <OnGlobalPaneInit> which is called whenever a pane on any screen is called ie used or initialized. Events typically need to be attached to a specific object such as a item, ship, or station. For this tutorial we will use a virtual item.

Step one for any mod is to establish the basic syntax for the xml file. Below is all that is needed to add a new mod. There is nothing in it at the moment so it does nothing, but it will be loaded if added the the "Extensions" folder without problems. Other tutorials explain what each of the items below mean.

Code: Select all

<?xml version="1.0" ?>
<!DOCTYPE TranscendenceExtension
[
	<!ENTITY	modIdentifyBeforeBuying	"0xDA1DA18E">

	
]>

<TranscendenceExtension
    UNID="&modIdentifyBeforeBuying;"
    name="Identify Before Buying or Selling"
    credits="TransGeek"
    version="1.0">

</TranscendenceExtension>
Ok so we created a mod file to use. Next we need to create the virtual item to attach the <OnGlobalPaneInit> event to.

Each item requires it's very own entity. Below is the virtual item for this mod.

First add the item name and unid number.

Code: Select all

<!ENTITY	vtIdentifyBeforeBuying	"0xDA1DA18F">
Next let's write the item in xml.

Code: Select all

<ItemType UNID="&vtIdentifyBeforeBuying;" 
		virtual="true"
			>

	</ItemType>
Next let's write the event syntax in xml

Code: Select all

		<Events>
			<OnGlobalPaneInit>
			
			</OnGlobalPaneInit>
		</Events>
Putting it all together looks like:

Code: Select all

<?xml version="1.0" ?>
<!DOCTYPE TranscendenceExtension
[
	<!ENTITY	modIdentifyBeforeBuying	"0xDA1DA18E">
	<!ENTITY	vtIdentifyBeforeBuying	"0xDA1DA18F">
]>

<TranscendenceExtension
    UNID="modIdentifyBeforeBuying"
    name="Identify Before Buying"
    credits="TransGeek"
    version="1.0">
	
	<ItemType UNID="&vtIdentifyBeforeBuying;" 
		virtual="true"
		>

		<Events>
			<OnGlobalPaneInit>
			
			</OnGlobalPaneInit>
		</Events>

	</ItemType>

</TranscendenceExtension>
Now we have everything we need to change a dockscreen now. It order to change a dockscreen you need to know what it looks like and how it functions in general. Most dockscreens can be found in the Transcendence.xml, which can be retrieved using TransData or downloaded from Xelerus. For this mod we need to look at the dockscreens "&dsExchangeBuy;" and "&dsExchangeSell;". Lets write the code now.

This is the basic look without code to make it function. Code will be inserted in the (block nil) area.

Code: Select all

		<Events>
			<OnGlobalPaneInit>
					(if 
						(or (eq aScreenUNID &dsExchangeBuy;)(eq aScreenUNID &dsExchangeSell;)) 
							(scrAddAction 
								gScreen 
								'identifyMe 
								1 
								"Identify this item" 
								"I" 
								(block nil
								)
							)
					)
			</OnGlobalPaneInit>			
		</Events>

This version is full of comments defined by <!-- -->

Code: Select all

		<Events>
			<OnGlobalPaneInit>
<!--Since the event is called on all screens when they are loaded we need to test for the screen we want. We do this using a conditional statement ie the switch statement-->
					(if 
<!--This statement uses "aScreenUNID" a type of global identifier.-->
						(or (eq aScreenUNID &dsExchangeBuy;)(eq aScreenUNID &dsExchangeSell;)) 
<!--If aScreenUNID is equal to &dsExchangeBuy; (The Buy Screen) or &dsExchangeSell; (The Sell Screen) then this statement returns true and calls the scrAddaction function. Otherwise it does nothing-->
							(scrAddAction 
<!--This is another global identifier that points to the current screen that is open-->
								gScreen 
<!--String used for identification-->
								'identifyMe 
<!--Each action in a dockscreen pane has a positional number starting with 0-->
								1 
<!--This is the text the user will see as the action it will appear below "Buy this item"-->
								"Identify this item" 
<!--The keyboard key assigned to the action-->
								"I" 
<!--This is the point in the code that things getting interesting. If the following (block nil ) is left empty the action will still be added to the dockscreen, but it wont do anything. We have to add code to make the action do something.-->
								(block nil

<!--Determine the currently selected item-->
									(setq gItem (scrGetItem gScreen))

<!--Determine the cost to identify-->	
									(switch
<!--Sets the cost at 10% of the items real value-->
										(geq (itmGetPrice gItem) 100)
											(setq identifyCost (divide (itmGetPrice gItem) 10))
<!--Sets the cost at 5% of the items real value-->
										(leq (itmGetPrice gItem) 99)
											(setq identifyCost (divide (itmGetPrice gItem) 5))
										
										)
<!--Make sure the item isn't already identified-->	
									(if (not (itmIsKnown gItem))
<!--Make sure you have enough money to identify-->		
										(if (geq (objGetBalance gPlayerShip (objGetDefaultCurrency gSource)) identifyCost)
										
											(block nil
<!--Identify the item-->			
												(itmSetKnown gItem)
<!--Check the stations currency and charge the player in it to identify the item-->			
												(if (eq (objGetDefaultCurrency gsource) &ecRinEconomy;)
<!--If the station uses rins-->
													(block nil
														(plyCharge gplayer &ecRinEconomy;(divide identifyCost 5))
														(scrShowPane gScreen "Default")
													)
<!--If the station uses credits-->					
													(block nil
														(plyCharge gplayer identifyCost)
														(scrShowPane gScreen "Default")
													)
												)
											)
<!--Let the player know they don't have enough credits to identify the item-->			
											(scrSetDesc gScreen  "You don't have enough credits to identify that item.")
										)
<!--Let the player know if the item has already been identified-->
										(scrSetDesc gScreen  (cat "That item has already been identified." ))
									)
								)

							)
					
					)
			</OnGlobalPaneInit>			
		</Events>

This is how it looks without comments.

Code: Select all

		<Events>
			<OnGlobalPaneInit>
					(if 
						(or (eq aScreenUNID &dsExchangeBuy;)(eq aScreenUNID &dsExchangeSell;)) 
							(scrAddAction 
								gScreen 
								'identifyMe 
								1 
								"Identify this item" 
								"I" 
								(block nil
									(setq gItem (scrGetItem gScreen))
									(switch
										(geq (itmGetPrice gItem) 100)
											(setq identifyCost (divide (itmGetPrice gItem) 10))
										(leq (itmGetPrice gItem) 99)
											(setq identifyCost (divide (itmGetPrice gItem) 5))
										)
									(if (not (itmIsKnown gItem))
										(if (geq (objGetBalance gPlayerShip (objGetDefaultCurrency gSource)) identifyCost)
										
											(block nil
												(itmSetKnown gItem)
	
												(if (eq (objGetDefaultCurrency gsource) &ecRinEconomy;)
													(block nil
														(plyCharge gplayer &ecRinEconomy;(divide identifyCost 5))
														(scrShowPane gScreen "Default")
													)
													(block nil
														(plyCharge gplayer identifyCost)
														(scrShowPane gScreen "Default")
													)
												)
											)
											(scrSetDesc gScreen  "You don't have enough credits to identify that item.")
										)
										(scrSetDesc gScreen  (cat "That item has already been identified." ))
									)
								)

							)
					
					)
			</OnGlobalPaneInit>			
		</Events>
That is all there is to it. Things can be as complex or as simple as you like. Remember however there is the possibility of screen clutter when too many mods add things to the same screens. This can make screens unusable at times.

The working xml file for this mod is attached. It can also be downloaded at Xelerus Identify Before Buying or Selling
Attachments
IdentifyScreen.zip
(1.41 KiB) Downloaded 152 times

TranscendentGeek
Commonwealth Pilot
Commonwealth Pilot
Posts: 88
Joined: Fri Mar 09, 2012 6:13 pm

Mon May 07, 2012 8:32 am

I added some new features to the code. The version on Xelerus will now gray out the action if the item you have selected is already identified or if you can't afford to identify it. However, if it is unknown and you can afford it then the action text will change from "Identify this item" to "Identify this item for X credits/rins". This code can be inserted into the Block nil surrounding the scrAddAction code after the scrAddAction code, but before the block nil closing parenthesis.

Code: Select all

							
(if (and (not (itmIsKnown (scrGetItem gScreen)))(geq (objGetBalance gPlayerShip (objGetDefaultCurrency gSource)) (divide (itmGetPrice (scrGetItem gScreen)) 10)) )
	(block nil
		(if (eq (objGetDefaultCurrency gsource) &ecRinEconomy;)
			(setq cost1 (divide (divide (itmGetPrice (scrGetItem gScreen)) 10) 5))
			(setq cost1 (divide (itmGetPrice (scrGetItem gScreen)) 10))
		)
									
		(scrEnableAction gScreen 'identifyMe  true)
		(scrSetActionLabel gscreen 'identifyMe (cat "Identify this item for "(fmtCurrency (objGetDefaultCurrency gsource) cost1)""))
	)
	(block nil
		(scrEnableAction gScreen 'identifyMe  Nil)
	)
								
)

Post Reply