Discuss this page on this page's Talk Page.
This exercise has been brought to you by a total n00b to Transcendence modding. Helpful comments welcomed.
The first thing you need to do is get a hold of a human-readable version of the Transcendence content - in other words you want the source code. And that means you want TransData. Then you need to decide what you are going to mess with. I'm going to work with the Tinker Stations. So you look through the content of the TranscendenceSource directory for a likely looking filename - and there it is, Tinkers.xml.
Now the 'Tinker Gathering' source contains all the details on the station and how it works in the original (vanilla) Transcendence. You don't want to just mess with it there, though. Tinkers.xml contains a <TranscendenceModule> and what you want to produce is a file containing a working <TranscendenceExtension>.
So, for now, I'm just going to copy Tinkers.xml into another directory and rename it SmartTinkers.xml (please ignore any delusions of grandeur you may notice here )
I'm also going to follow the traditional method of seeing how other people do it by looking at the Commonwealth Fortress Overwrite by sdw195. (Cheers, mate )
Starting at the very top of our 'new' file, SmartTinkers.xml you might be able to spot the first problem.
<?xml version="1.0" encoding="utf-8"?>
UTF-8? What's that? Well the good news is that 90+% of the users can forget about it. Look at the first line of CommonwealthFortressoverwrite.xml - it doesn't include the encoding attribute and unless you want to support a foreign language you can forget about it as well.
<?xml version="1.0" ?>
You do want to support foreign languages? Well here's How I Learned to Stop Worrying and Love the BOM. UTF-8 denotes a type of Unicode encoding and the Byte Order Mark is a three byte sequence that may be present at the very start of every UTF-8 text file. TranscendenceSource files don't need a BOM, and if you try to run Transcendence with an extension using a BOM it will fail to run. Windows (especially Microsoft) programs like BOMs and saving in UTF-8 in, for example, Notepad will set us up the BOM whether we want it or not. Short version? If you use Unicode you probably need a BOM Remover.
Next up - look down a few lines for the <TranscendenceExtension> tag.
<TranscendenceExtension UNID="&UNIDCommonwealthFortress;" version="1.0" name="Commonwealth Fortress Overwrite" credits="sdw195">
The <TranscendenceModule> doesn't have all those attributes, so that's another thing to change. The UNID and version attribute are mandatory, the others are optional (are there any other optional attributes?). The version number shows what version of Transcendence this extension works with. If you miss it out, or have an invalid number there, Transcendence won't start. Stick with version=“1.0” here. You should get your own UNID with help from the UNID database, but for now we can use the UNID range reserved for code in the Wiki - 0xD000. You need a new UNID for the extension (anything in the range will do). You can also put the _old_ station UNID (as defined in Transcendence.xml) in there.
So the first few lines, and last line, of SmartTinkers.xml should be changed to look like this…
<?xml version="1.0" ?> <!DOCTYPE TranscendenceExtension [ <!ENTITY UNIDTinkerGathering "0xD0000010"> <!ENTITY stTinkerGathering "0x08040080"> ]> <TranscendenceExtension UNID="&UNIDTinkerGathering;" version="1.0" name="Smart Tinker Gathering" credits="Total n00b">
Put the SmartTinkers.xml file into your extensions directory and start Transcendence - if it doesn't crash you've passed the first step. Congratulations! If it does crash maybe you've made a typo. Sucks to be you!
Now you've got an extension that exactly replicates what Transcendence already did, but did it work? If you're going to debug this it helps if you can actually find the station when you need it. So here is a little bit of code (adapted from the Item Repair Framework by Prophet.
<Globals> (setq smartTinkerME (lambda Nil (sysCreateStation &stTinkerGathering; (objGetPos gPlayerShip)) )) </Globals>
I put that right at the start of the <TranscendenceExtension> element. Hopefully what it will do is create a tinker (ahem 'Smart' Tinker) station right where your ship is if you type (smartTinkerME) on the debug console. (To get the debug console, start Transcendence like
then press F9 while the game is running).
Now we're getting to the tricky stuff. The bits that might make someone somewhere say “Hey, I might actually download this.” You need an idea of something that will make the game easier, harder, more fun, more interesting, or just show off your fancy code techniques*
* Author does not possess fancy code techniques.
In this case I want to fix two annoyances with the current tinker stations. 1) Being told that your pile of 20 Mining Lasers are crap they can't do anything with. 2) Using 19 lots of Ortho Steel Ore to make Ortho Steel when you only needed 10. Credit goes to the hapless citizens of the Transcendence Shipyards for pointing me in the right direction, giving me code examples, and generally doing my work for me. Thanks folks!
So, how to do this? Better still where to do this? The answer to the second question (as I was informed) is inside the <ListOptions> element. Which looks like
<ListOptions dataFrom= "player" list= "*U" />
in the first place. There is no 'inside' as it's an empty element. That's easily fixed though, you just need to add an explicit closing tag (note that the /> empty tag closer is changed to the standard > ).
<ListOptions dataFrom= "player" list= "*U" > </ListOptions>
For the 'how to do this' question I think we are looking at a job for scrSetListFilter. So a closer look at that is warranted.
I'm not to proud to admit that I failed to get this to work - the following code is from Alterecco who has been invaluable during this whole process.
(scrSetListFilter gScreen (lambda (itm) (block (found) (enumWhile (objGetStaticData gSource 'CustomWork) (not found) work (if (eq (item work 0) (itmGetUNID itm)) (setq found True)) ) found ) ))
You now only see the items that (potentially) can be converted at the Tinkers.
This shouldn't be as tough as the last one. (fingers crossed)
First off all - where is it that the items actually disappear? Well that would be …
(scrRemoveItem gScreen gMaxCount)
gMaxCount? Sounds a bit of an ominous naming choice to me. Let's look a little further back to when we decide to go ahead with the deal …
(scrSetDesc gScreen (cat "We can convert your " (itmGetName (itmCreate (itmGetUNID gItem) gMaxCount) 8) " into " (itmGetName (itmCreate (item gMatch 2) gCount) 8) " for " gCost " credits. Do you wish to proceed?"))
There's that gMaxCount again. Here's where we are told that they can convert 19 Ortho steel ore into 1 Ortho steel - when they only actually need 10.
We'll have to go back even further to see the heart of the matter.
(if (geq gMaxCount (item testWork 1)) (block Nil (setq gMatch testWork) (setq gCount (divide gMaxCount (item testWork 1))) (setq gCost (multiply gCount 50)) ) )
The problem is that it works out how many we can make (gCount), but not how many are needed to make that amount. We need another variable, say, gMaxUsable to be defined there and used in place of gMaxCount later on.
(if (geq gMaxCount (item testWork 1)) (block Nil (setq gMatch testWork) (setq gCount (divide gMaxCount (item testWork 1))) (setq gMaxUsable (multiply (item testWork 1) gCount)) (setq gCost (multiply gCount 50)) ) )
Now replace gMaxCount with gMaxUsable in the two places mentioned earlier and we're done.
To make it easier to test whether it works or not I've added another bit of code to the <Globals> element (needs to be inside <TranscendenceExtension> and outside (before, or after) the <StationType> element.).
<Globals> (block Nil (setq smartTinkerME (lambda Nil (sysCreateStation &stTinkerGathering; (objGetPos gPlayerShip)) )) (setq oreME (lambda Nil (objAddItem gPlayerShip (itmCreate &itOrthoSteelOre; 19)) )) ) </Globals>
Who-hoo! It works.
If you don't believe me you can download SmartTinkers from Xelerus (with my UNID range).
Return to Tutorials.