Jump to content

Photo

[CSE] Coda Scripting

Construction Set Extender Coda

  • Please log in to reply
20 replies to this topic

#1
AndalayBay

AndalayBay

    #ffc0db Wildebeest under #0000ff Flying Shoes

    • Unofficial Morrowind Patch
    • Dark Brotherhood Chronicles
    • Black Marsh
    • Wild Nuts
    • The Brotherhood of Old
    • TES3Gecko
    • Better Cities
    • Collective
  • 10,870 posts
  • Location: Ontario, Canada
Introduction

Coda is the scripting language for the Construction Set Extender (CSE). It is not Legacy, or the scripting language used for Oblivion. It is a scripting language that you can use to automate a variety of tasks in the editor, such as renaming objects, moving objects or batch editing object attributes. In this guide, or tutorial, I will show you how you can use Coda to accomplish a variety of tasks.

Documentation

The Coda Manual is distributed with the Construction Set Extender. You will find it in Data\Docs\Construction Set Extender. The manual explains the general syntax of Coda scripts and how to execute them. It also describes the general language structure, including a description of variables and data types, operators and flow control. In addition to the manual, there is the Coda Command document, which is an HTML file listing all the functions and commands. Since the command set is still being developed, the command document is not included in the distribution. Instead you can generate the most recent version by typing DumpCodaDocs from the console command prompt (see below).

Creating and Running Scripts

The structure of Coda scripts is described in the manual, so I won't repeat it here. I will be listing several examples that you can use to get started. You create your scripts using a text editor. If you use an editor like TextPad or NotePad++, you can use an Oblivion script syntax highlighter. Coda shares its basic syntax with Legacy. Save your scripts with .coda for the extension. Scripts must be in Data\BGSEE\Coda to be accessed and executed from the command line.

Once you've created a script, you run it from the command line in the CSE console window. Here's an example:

gallery_1_37_68573.jpg

Coda scripts are invoked with the RunCodaScript command. The command takes two parameters: the script name minus the extension and a boolean integer indicating whether it's a background script or not. You would use 0 for a regular script and 1 for a background script. Most scripts would be run as a foreground process. I will provide one example of a script that must be run as a background process.

Sample Scripts

Here are some sample scripts. They are all real scripts, so you can adapt them to your purposes.

Strip off Leading Digits from Editor IDs

This script will strip off the leading digits from editor IDs, which are known to cause problems with Oblivion scripts. It processes all AI packages. You can change the batch of editor IDs by changing the form type ID parameter for the GetDataHandlerFormList function. GetDataHandlerFormList can accept hexadecimal or decimal values. In this example we are using the hexidecimal value for AI packages. The decimal equivalent is 61. The full list of form type IDs is in the Coda Command Documentation or the OBSE documentation.

CODA(ChangeEditorID)

var Buffer
var EditorID
var firstChar

begin
	; retrieve AI package forms
	forEach Buffer <- GetDataHandlerFormList(0x3D)
		EditorID = GetEditorID(Buffer)
		firstChar = StringSubStr(EditorID, 0, 1)
		; we only want to change the editor IDs that start with a number
		if StringIsNumber(firstChar)
			printC("editorID (old) ="//$EditorID)
			; skip the first character. If there is more than one digit,
			; change the starting position so that it skips all the digits.
			EditorID = StringSubStr(EditorID, 1, StringLength(EditorID))
			printC("editorID (new) ="//$EditorID)
			setEditorID(Buffer, $EditorID)
		endif
	loop
	; It's pretty quick, but nice to know exactly when it's done.
	printc("All done.")
end
Save this script as ChangeEditorID.coda and execute it by typing

RunCodaScript ChangeEditorID 0
at the command prompt. Note that the command and parameters are not case sensitive.

Add Mod Prefix to Editor IDs for Cells

This script will add the specified mod prefix to the cell editor IDs. This is handy if you would like to have a naming convention for the objects in your mod and some team members neglected to prefix their editor IDs accordingly. This example renames the editor IDs for cells, but once again, you can change the scope by changing the form type ID. This time we're using the decimal value for cell, which is 48. Semi-colons ( ;) are used for comments in Coda, just as they are in Oblivion script. The printC commands have been commented out in this sample.

CODA(AddBMStartCell)

var Buffer
var EditorID
var firstChars
var length
var formIDString
var formStart

begin
	forEach Buffer <- GetDataHandlerFormList(48)
		EditorID = GetEditorID(Buffer)
		formIDString = FormatNumber("%08X", (int)Buffer, 1)
		;printc("form string = "//formIDString)
		formstart = StringSubStr(formIDString, 0, 2)
		if StringCompare(formStart, "01", 0) == 0
			firstChars = StringSubStr(EditorID, 0, 2)
			length = StringLength(EditorID)
			if length == 0
				continue()
			endif
			if (StringCompare(firstChars, "BM", 0) != 0)
				;printC("editorID (old) ="//EditorID)
				EditorID = "BM" // EditorID
				;printC("editorID (new) ="//EditorID)
				setEditorID(Buffer, EditorID)
			endif
		endif
	loop
	printc("All done.")
end
Print Random Numbers

Here's a little test script to show how the random number function works. I use this function later. :D In this case, I'm generating random numbers between 0 and 359, which correspond to angles. The generated numbers are floats with six decimal places.

CODA(Random)

var randomNum
var i

begin
	while i < 10
		randomNum = RandomNumber(0, 359)
		printC("Random number: "//$randomNum)
		i += 1
	loop
end
Change Imperials and Nords to Orcs and Dark Elves

Here's a script that shows an example of the Base Form Component (BFC) functions. It switches the race of all male Imperial and Nord NPCs to Orcs and Dark Elves.

CODA(SwapRace)

var npc
var race

begin
	forEach npc <- getDataHandlerFormList(35)
		race = getBFCRace(npc)
		
		if (race && (race == getFormByEditorID("Imperial") || race == getFormByEditorID("Nord")) && getBFCActorBaseDataSex(npc) == 0)
			printC("NPC "//getEditorID(npc)//" switched to new race!")
			
			if (race == getFormByEditorID("Imperial"))
				setBFCRace(npc, getFormByEditorID("Orc"))
			else ; nord
				setBFCRace(npc, getFormByEditorID("DarkElf"))
			endif

			markAsModified(npc, 1)
		endif
	loop
	printC("All done!")
end
All the scripts so far are executed as regular foreground scripts. Before I detail the monster background script, I'll direct you to the Disable Trees example on the CS Wiki. Disable Trees demonstrates how to scan exterior cells for specific objects and disable them. It also mentions a key CSE function: you can get a list of all cell form IDs by using the Export tool from the main menu.

File -> Export -> Interior and Exterior Cell Data
Madam, you have between your legs an instrument capable of giving pleasure to thousands, and all you can do is scratch it!
-- Attributed to Thomas Beecham in reference to the performance of a female cello soloist

#2
AndalayBay

AndalayBay

    #ffc0db Wildebeest under #0000ff Flying Shoes

    • Unofficial Morrowind Patch
    • Dark Brotherhood Chronicles
    • Black Marsh
    • Wild Nuts
    • The Brotherhood of Old
    • TES3Gecko
    • Better Cities
    • Collective
  • 10,870 posts
  • Location: Ontario, Canada

Move Trees

shadeMe helped me develop this script to fix the floating/sunk trees in the Black Marsh eastern regions that were artefacts of the region generation process. shadeMe added some new functions to Coda to enable me to accomplish this task and also added a new command line parameter to designate a specific script as a background process.

First you need to generate the list of cell form IDs by using the Export function mentioned at the end of the last post.

File -> Export -> Interior and Exterior Cell Data

This will limit the cells that need to be scanned. Even at that, the Black Marsh eastern regions comprise 1500 cells. In order to properly position the trees, I needed to simulate the Floor command so shadeMe added a new function called FloorRef. We discovered that the Floor command only worked on objects loaded into the render window, so shadeMe added another function called LoadRefIntoRenderWindow. Since it takes a moment to load a cell, this script must be run as a background process and requires some timing features. We also needed to specify that this script run as a background script on demand, so shadeMe added a second parameter to the RunCodaScript command. Yes, I have kept him busy. Idle hands and all that...

Like the Disable Trees example from the previous post, this script uses an array to specify the cells that need to be scanned. You specify the object that should be moved via the getFormByEditorID function. A simple comparison checks each object in the cell for the one you wish to move. Once a target object is found, I use the random number generator to set a random rotation angle. Then I raise the tree and use the FloorRef function to drop it to the ground. Since the ground isn't flat, I lower the tree by a bit more to sink it into the ground.

Since this script needs to load every cell into the Render Window, it takes a LONG time to run. I suggest starting it before you go to bed and it should be done by morning. :D Needless to say, it will take less time if you have fewer cells to scan.

Spoiler


I warned you this was a beast! We had to increase the post size limit so I could post this. In order to run this script, you need to enable background execution by selecting Execute Background Scripts from the main Coda menu:
 
Coda -> Execute Background Scripts
 
You also need to use 1 for the background execution parameter when you invoke the RunCodaScript command, as follows:
 
RunCodaScript MoveTrees 1
 
Now you can go to bed or leave for the day to do some chores. :P
 
If you have any questions about this or any of the previous scripts, please feel free to ask in this thread.
Madam, you have between your legs an instrument capable of giving pleasure to thousands, and all you can do is scratch it!
-- Attributed to Thomas Beecham in reference to the performance of a female cello soloist

#3
AndalayBay

AndalayBay

    #ffc0db Wildebeest under #0000ff Flying Shoes

    • Unofficial Morrowind Patch
    • Dark Brotherhood Chronicles
    • Black Marsh
    • Wild Nuts
    • The Brotherhood of Old
    • TES3Gecko
    • Better Cities
    • Collective
  • 10,870 posts
  • Location: Ontario, Canada

shadeMe has added a new function to Coda that will retrieve the usage of a specific object. In the above MoveTrees script, you'll see that it requires a list of Form ID's for the cells to scan. Previously you had to get that list by exporting the full list of exterior cell Form ID's and then manually comb through the usage info to figure out which cells actually needed to be scanned. No longer! Here is a script demonstrating the usage of the new GetCellUseList function:
 

CODA(GetUsage)

var cellUsage
var baseObject
var instance

begin
    baseObject = getFormByEditorID("BMFloraKelp03")
    cellUsage = GetCellUseList(baseObject)
    foreach instance <- cellUsage
        printC("Cell "//$instance)
    loop
end
 
This script assumes that the specified object has been instantiated at least once. :) This script produces a list like this:
 
 
[CSE]            Cell 01011EE7
[CSE]            Cell 01011EEF
[CSE]            Cell 01011EF1
[CSE]            Cell 0100237F
[CSE]            Cell 01002395
[CSE]            Cell 010037CB
[CSE]            Cell 010037CE
[CSE]            Cell 01011F0B
[CSE]            Cell 010024B3
[CSE]            Cell 0103658A
[CSE]            Cell 010036DC
[CSE]            Cell 010369FB
...
 
If you have an editor like TextPad which has a block select mode, you can easily copy the Form ID's and paste them into your script. Failing that, you can create a regular expression to strip off the extra text. :P
Madam, you have between your legs an instrument capable of giving pleasure to thousands, and all you can do is scratch it!
-- Attributed to Thomas Beecham in reference to the performance of a female cello soloist

#4
tegeusCromis

tegeusCromis

    Krill

  • 39 posts
  • Location: UK

I'm so glad I wandered over here. Been wondering about using Coda to do some tree stuff, and this addresses a few issues in advance that I know would trip me up. May not get round to it anyway, but thanks for making it easier to think about...



#5
AndalayBay

AndalayBay

    #ffc0db Wildebeest under #0000ff Flying Shoes

    • Unofficial Morrowind Patch
    • Dark Brotherhood Chronicles
    • Black Marsh
    • Wild Nuts
    • The Brotherhood of Old
    • TES3Gecko
    • Better Cities
    • Collective
  • 10,870 posts
  • Location: Ontario, Canada
I suspect shadeMe has added more stuff since I last looked at it. I've been away from modding for over a year now. I might add more stuff to this thread when I get back into the swing of things.
Madam, you have between your legs an instrument capable of giving pleasure to thousands, and all you can do is scratch it!
-- Attributed to Thomas Beecham in reference to the performance of a female cello soloist

#6
tegeusCromis

tegeusCromis

    Krill

  • 39 posts
  • Location: UK

I suspect shadeMe has added more stuff since I last looked at it.

 

That's kind of the shadeMe mantra... :)


Edited by tegeusCromis, 15 October 2016 - 09:42 PM.


#7
tegeusCromis

tegeusCromis

    Krill

  • 39 posts
  • Location: UK

Here's a thing, and it doesn't show in your example, because you are using SetRefRotation. But, ah, CreateRef, I completely forgot that it takes radians, not degrees. I made the mistake of blithely feeding the results of GetRefRotation into CreateRef, you see (while cloning an existing static as an activator). Of course, it's easily fixed by *.0174532925, but why? Why use different units at all? It's like the metric-imperial clash, sitting in the road just waiting to trip someone up.



#8
AndalayBay

AndalayBay

    #ffc0db Wildebeest under #0000ff Flying Shoes

    • Unofficial Morrowind Patch
    • Dark Brotherhood Chronicles
    • Black Marsh
    • Wild Nuts
    • The Brotherhood of Old
    • TES3Gecko
    • Better Cities
    • Collective
  • 10,870 posts
  • Location: Ontario, Canada
You might want to mention that to shadeMe. I was just doing random rotations, so I didn't worry about the units.
Madam, you have between your legs an instrument capable of giving pleasure to thousands, and all you can do is scratch it!
-- Attributed to Thomas Beecham in reference to the performance of a female cello soloist

#9
tegeusCromis

tegeusCromis

    Krill

  • 39 posts
  • Location: UK

Well, I think it's OK as long as people are alerted to it. I must have figured it out (can't see where it mentions radians in the CSE docs, frankly) a while back, looking at my old code. But I was only using CreateRef by itself then; it's when it meets other commands you have to watch out, especially when there's lots of cut 'n' paste coding going on...



#10
tegeusCromis

tegeusCromis

    Krill

  • 39 posts
  • Location: UK

Right, since your Move Trees script was such an inspiration (would have taken me a little time getting backgrounding right, I think), in return here's my adaptation, which substitutes forms using CreateRef. Maybe you can find a use for it - seems to work pretty well, I've done a fair few runs now:
 

Spoiler

 

Notes:

 

1) I've combined the getusage run with the main processing script, it now populates the array directly - saves a bit of time doing all that cut and paste, but needs carefully checking usage info going in and coming out.

 

2) The (objectRef > 0x1000000) check is to ensure that vanilla objects are not replaced in this case (I'm running it on another mod). A crude check, delete/reverse it for vanilla, or it might be a bit more nuanced if there is a chain of dependencies.

 

3) Performance isn't too bad IMO, processed about 150 forms in around 10mins (that's just the birch logs, though).

 

4) Just FYI, the background here is that I am substituting activators (with the same meshes) for static logs, as in Qarl's Harvest, where I have already published a fruit tree add-on (I seem to keep coming back to this, dunno why). That's why I'm avoiding vanilla meshes, he already did those. I've been test running on Valenwood Improved (terrific landscape, but very very few ingredients in it), quite demanding, hundreds of logs. Unique Landscapes is an obvious other target - after all, it doesn't change the look of the landscape in any way, you just get a bunch of extra ingredients.


Edited by tegeusCromis, 24 October 2016 - 07:45 PM.

  • AndalayBay and Schtearn like this

#11
AndalayBay

AndalayBay

    #ffc0db Wildebeest under #0000ff Flying Shoes

    • Unofficial Morrowind Patch
    • Dark Brotherhood Chronicles
    • Black Marsh
    • Wild Nuts
    • The Brotherhood of Old
    • TES3Gecko
    • Better Cities
    • Collective
  • 10,870 posts
  • Location: Ontario, Canada
Cool. Thanks for posting. :beerchug:
Madam, you have between your legs an instrument capable of giving pleasure to thousands, and all you can do is scratch it!
-- Attributed to Thomas Beecham in reference to the performance of a female cello soloist

#12
tegeusCromis

tegeusCromis

    Krill

  • 39 posts
  • Location: UK

A question. I'm exploring the potential of the FloorRef command, which could have all sorts of possibilities (good on you for getting shadeMe to implement it!). But I've come up against an issue, I'm simply trying to figure out which cells a mod has altered. Now this is not a problem when it comes to a mod adding refs, you just scan through every cell looking for >0x1000000s or whatever. I've done this and it's shockingly fast even for the whole of Tamriel. But I've only got one form of alteration - if the mod has added something to the cell. But CODA cannot tell if it has modified vanilla objects (or objects from a prior mod - as in patches for example). There is a MarkAsModified command, but no GetRefModified function (argh!). [EDIT: I've asked shadeMe for one.]

 

So - am I missing something here, perhaps a workaround?

 

(Also, of course, I don't think there is any way in CODA to detect landscape edits, but I don't suppose it was intended to go there in the first place...)


Edited by tegeusCromis, 24 October 2016 - 02:35 PM.


#13
AndalayBay

AndalayBay

    #ffc0db Wildebeest under #0000ff Flying Shoes

    • Unofficial Morrowind Patch
    • Dark Brotherhood Chronicles
    • Black Marsh
    • Wild Nuts
    • The Brotherhood of Old
    • TES3Gecko
    • Better Cities
    • Collective
  • 10,870 posts
  • Location: Ontario, Canada
Yeah I think shadeMe would have to implement something. The CS knows when something has changed, so hopefully it would be fairly simple to implement.
Madam, you have between your legs an instrument capable of giving pleasure to thousands, and all you can do is scratch it!
-- Attributed to Thomas Beecham in reference to the performance of a female cello soloist

#14
tegeusCromis

tegeusCromis

    Krill

  • 39 posts
  • Location: UK

Well, blow me down...

 

http://forums.bethso...extender/page-6

 

Wow, what an amazing person. But now, of course, the script I posted here has to be changed. I assume that shadeMe has standardised on degrees (in which case I simply remove the conversion lines), but I'll have to test it. I'll just edit the previous post. The conversion lines will be commented out wih a note, because it'll need v8+.

 

[Well, when 8.0 is uploaded. Fine by me, I need a break from the current app...]


Edited by tegeusCromis, 24 October 2016 - 06:50 PM.


#15
AndalayBay

AndalayBay

    #ffc0db Wildebeest under #0000ff Flying Shoes

    • Unofficial Morrowind Patch
    • Dark Brotherhood Chronicles
    • Black Marsh
    • Wild Nuts
    • The Brotherhood of Old
    • TES3Gecko
    • Better Cities
    • Collective
  • 10,870 posts
  • Location: Ontario, Canada
I didn't think it would take much work. :D I've got some catching up to do though!
Madam, you have between your legs an instrument capable of giving pleasure to thousands, and all you can do is scratch it!
-- Attributed to Thomas Beecham in reference to the performance of a female cello soloist

#16
tegeusCromis

tegeusCromis

    Krill

  • 39 posts
  • Location: UK

Got the 8.0 dev version (don't know when he's putting it on the Nexus, lotsa testing going on). Yep, the rotations now harmonise, so I've added a note to the above script pending a general release.
 
Now for GetRefModified...
 
Oh, and I'm sure it won't take you long, AB. It took me a couple of months to remember how things worked earlier this year, I admit, but I have a teeny tiny amount of experience compared with you.
 
EDIT: Oh well, I'm short of that for now.
 
OK, what I'm trying to do is create a simple-minded floater detector, which would be of use for checking by both landscape modders and patch authors. I don't think it's possible to construct one that doesn't throw many false positives, especially for rocks (trees not too bad), but I still think it is good to have a list of suspects as a final check. It's essential, though, that it can detect if vanilla/prior mod objects have been moved, hence the GetRefModified. Of course you can FloorRef every damn object of a given type in the game database, but that's mad. I do see, however how to construct one that operates only on a pre-specified matrix of cells (this actually takes a little finagling in fact) which would cut down on run time, but it could be that much easier...
 
UPDATE: We do have now a new function, downloaded from Dropbox. And it seems to do what I hoped. Going to plug it into the latest script version now.:
 

IsModified - Returns true if the form has been modified, i.e., saved to the active file.
(numeric) IsModified form:ref


Edited by tegeusCromis, 24 October 2016 - 09:38 PM.


#17
AndalayBay

AndalayBay

    #ffc0db Wildebeest under #0000ff Flying Shoes

    • Unofficial Morrowind Patch
    • Dark Brotherhood Chronicles
    • Black Marsh
    • Wild Nuts
    • The Brotherhood of Old
    • TES3Gecko
    • Better Cities
    • Collective
  • 10,870 posts
  • Location: Ontario, Canada
I was using CODA to fix the region generation in Black Marsh, so everything was modified. :P We had both floating objects and objects that were embedded in the ground, so my approach was to raise everything of a particular base object 30,000 units up and then floor them. I also added a random rotation since they were all oriented in the same direction originally. That's what my moveTrees script did, above.

I limited the number of objects by doing this for blocks of cells. A similar approach might work for you, combined with checking for modified objects.
Madam, you have between your legs an instrument capable of giving pleasure to thousands, and all you can do is scratch it!
-- Attributed to Thomas Beecham in reference to the performance of a female cello soloist

#18
tegeusCromis

tegeusCromis

    Krill

  • 39 posts
  • Location: UK

Yeah, sorry, should have pointed out that yours also was a (very large scale) floater fixer. Mine is something more modest that simply reports suspects (apart from false positives, some may even be intentional) that can then be fixed manually; and we're checking against vanilla/previous mods so it's also a compatibility check for mods. (Hope the Black Marsh is going well, love to see it. Didn't sign on to beta testing because I had too much else on at the time, maybe when that comes up again.)
 
I started with the cell block thing, but what I'm actually doing now is filtering through every single damn thing in the foreground initialise using two passes. Saves specifying, plus might catch the odd out-of-area mess-up (of course TES4Edit is the usual go-to for that, but every check helps).
 
Oh, and I just realised that background mode probably isn't needed for the CreateRef example I posted here, I just thought it looked cool. I'll post a foreground version later, which should run much faster.
 
In any case background mode broken right now, probably because of something I did installing shadeMe's dev v8...
 
EDIT: (Groan but, OK, developments keep insisting on being ongoing entities.) An important note to anyone using CODA is that - apart from the angular units, which is partly my fault and could affect existing code - shadeMe is now redirecting background text output, which is why I thought my CSE was broken. To quote:
 

I've added a new console context for Coda output (Right click on the console > Contexts > Coda Script). All background scripts print their output exclusively to this context at the moment. I'll add an INI toggle to allow them to additionally write the default context.

 

Of course, this is v8, which is in dev, but I thought an early alert might be in order.


Edited by tegeusCromis, 25 October 2016 - 06:59 PM.


#19
tegeusCromis

tegeusCromis

    Krill

  • 39 posts
  • Location: UK

OK, here's another one for you (taking a break from the floater thing, will come back to it). Valenwood Improved is proving a useful test case here. Almost none of the NPCs have any dialogue at all, so I thought about creating a Valenwood faction that gave them special provincial dialogue, at least. Goes without saying that we do not want to directly edit NPC base records - but fine, we can add them into the faction from game script with the slightly eccentric SetFactionRank command. But this needs an EditorID to operate on an instance of the Base Form (not the base itself, thankfully), however, none of the NPCs in this mod have EdIDs when instantiated.
 
So here's a script that attends to these neglected people:

 

Spoiler

 

It also has a little extra, because some of the NPCs have Base EdIDs that start with numerics, which is a bad idea in game scripting, so it lops these out.

 

I'm also not sure that we aren't pushing GetCellUseList  a bit far. It would be helpful to be able to access more Use Lists. And more, but not nagging poor shadeMe any more right now.

 

Oh, and the other handy thing is that this can now be used to generate a series of SetFactionRank command lines that can pasted into a game startup script. Though in most cases it would be better to use a separate script for this to be sure and catch all refs (you can make one from this one easily enough).

 

ACTUALLY - here it is now, just a quick bodge from the old script:

 

Spoiler

 

Whatever I do with the result, it's proving to be fun finding out how far one can get with this stuff...

 

EDIT: As noted, further down, I wittily forgot to finalise the entry with MarkAsModified(). Fixed now. Also, it is possible that the program may generate a ref that already exists (after all, it's mostly just adding '01REF' to the Base EDid, though it does check for multiple instances). I could check for this, but right now it isn't catastrophic, I checked and the CS catches it in its usual way and appends 'DUPLICATE0000' or whatever to the new ref. It will show when this happens in the console context and the log.

 

Oh, and to anyone running any of this stuff. I know it shouldn't have to be said but please, please be sure to back your work up properly before doing so!


Edited by tegeusCromis, 26 October 2016 - 08:37 PM.


#20
AndalayBay

AndalayBay

    #ffc0db Wildebeest under #0000ff Flying Shoes

    • Unofficial Morrowind Patch
    • Dark Brotherhood Chronicles
    • Black Marsh
    • Wild Nuts
    • The Brotherhood of Old
    • TES3Gecko
    • Better Cities
    • Collective
  • 10,870 posts
  • Location: Ontario, Canada
You need to use code tags in the spoiler tags to keep your formatting or it will get stripped out when you edit your post. The code tags actually support different languages, as listed in this post: http://www.theassimi...o-using-bbcode/
Madam, you have between your legs an instrument capable of giving pleasure to thousands, and all you can do is scratch it!
-- Attributed to Thomas Beecham in reference to the performance of a female cello soloist





Also tagged with one or more of these keywords: Construction Set Extender, Coda

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users