Strange Eons
Customization Tutorial
Send Feedback Home Page > Strange Eons > Miriam's Basement > Customization > Tutorial
This tutorial demonstrates how to write an extension that adds a new kind of card ("Enemy" cards) based on the existing Ally card. You could make Enemy cards using the "Miscellaneous Small" card, but it wouldn't use the rounded portrait that appears on Ally cards.
This tutorial demonstrates one kind of customization: making small changes to a an existing component type. For more extensive changes, you can instead create an entirely new kind of component. If that is what you want to do, read the DIY Tutorial.
Before getting started, there is some background information which will make this article much easier to follow. First, you should make sure you understand how to extract the Strange Eons resources to a folder. Second, you may want to have a look at the basic extension tutorial, which adds a new expansion symbol to the list of expansions that can be selected for a component.
To make our custom card type, we will modify the text and images that are used for Ally cards. We will do this by changing the values of certain SE "settings" that can be found in the SE resources. However, we still want to be able to create "regular" Allies, so we want to add a new entry to the New Component dialog that starts by creating an Ally but then tweaks the settings just for that card to use the new settings that we want.
In order to find out which settings we need to change, we'll refer to a copy of the SE resources. If you don't already have a copy, it's easy to get one. Have a look here for instructions.
To make our new Enemy cards, we will change the ally cards so that the word "Ally" at the top of the card is replaced with "Enemy", and we will add new graphics to make the card red instead of orange. To start with, we will look through our extracted copy of the SE resources to figure out which settings to change.
First the text. The text printed on cards for a given game language is stored in that language's card-text.txt file. The files for a given language are in resources/text/XX, where XX represents your standard game language. This will be EN for English, FR for French, ES for Spanish, and so on. Normally it is best to use the English text for reference, because it is always complete. The English file can be found in the resources at resources/text/EN/card-text.txt.
Open the card-text.txt file in a plain text programmer's-style editor. (The best editor to use is the Strange Eons Text Resource Editor.) Now locate the key that defines the text used at the top of Ally cards. Here is the line:
ally-text = Ally
As you might expect, the word "Ally" would be different if the file was one for another game language. For example, in the French file, the line reads:
ally-text = AlliéThis may give you a hint as to what we want to achieve: by changing the value of this setting we can change the name printed at the top of the card to whatever we like. Cards normally use the setting values that they get from their various language-specific files, plus the master settings and card layout files. However, every component can also have "private" settings. A private setting overrides the default value of a setting, but only for that particular card. The scripting library provides a special function that can be used to add private settings to a card. We can try it out now. Create a new Ally, then open the Quickscript window (in the Toolbox menu). Paste in the following script, and then run it.
Patch.card( Component, "ally-text", "Enemy" ); Editor.forceRerender();The Patch.card function sets the private setting. (Component is whatever card was being edited when we ran the script). The next two parameters that we pass to the function are the name of the setting we want to change (its "key"), and the new value we want it to have. We could change more than one setting at a time by adding more pairs of keys and values to the function call. The second line forces the card to be redrawn even though it hasn't been modified in the editor window. We need to add this because the card previewer doesn't know we have changed some of the card settings, so it doesn't automatically redraw the card. After you run this script, the top of the card in the preview window will change to:

If your image editor cannot read .jp2 files, there is an example script in the Plug-in Authoring Kit that will convert all of the .jp2 images in the resource folder to .png image files.
The script is convert-jpeg2000.js in the JavaScript folder.
You can also right click on any image file in a Project to convert it between formats.
So far, so good. Exit Strange Eons. Next we need to figure out which settings
keys control the images used as the "templates" (background) for Ally cards. We
need to make our own (red) version of these images, and set the private settings
to point to our new version. We can find the settings keys we need by sifting
through card-layout.txt to find:
ally-front-sheet-template = templates/ally-front.jp2 ally-back-sheet-template = templates/ally-back.jp2
So now we know that the template images we need to edit are resources/templates/ally-front.jp2
and resources/templates/ally-back.jp2. Open these images in an image
editor and use a hue adjustment tool to shift them both by the same amount,
then save them. If you want, you can just save a copy of the images below:

Original game graphics copyright 2005
Fantasy Flight Publishing, Inc.
Make sure that you keep the front face template in .png format, because .jpg images do not support transparency. We need transparency for the portrait area in the center.
OK. So we have our modified art, and we know which settings keys we need to change in order to use it. We also know the key we need to change the title to "Enemy". Now we just need to put it all together.
Playing with private settings in the Quickscript window is fine (and useful when experimenting or debugging), but ultimately we would like to have "Enemy" component that we can create from the New Component Dialog, just like all of the standard component types. To do this, we will need to write some small programs, called scripts. If you are not a programmer, don't worry. Most customization projects don't require real programming skill; you can use one of the examples in the plug-in authoring kit as a starting point and change it to suit your needs.
Plug-ins are composed of a number of files, but to make things convenient for users of the application, we package the files for a plug-in up into a "plug-in bundle."
As with our introductory example of how to write an extension, we will create a project to manage various files needed by our plug-in. In the project we'll create a plug-in task with an eons-plugin root file and a resources folder. Inside the resources folder, we'll create a subfolder using our initials or some other unique identifier (for the purposes of the tutorial page, we'll use tut), and inside of that we'll put the materials for our plug-in.
Let's start by setting up the project and adding an empty plug-in task:
To finish the plug-in, we will need to create the following files:
Once we have all of the parts, we will pack them up in a bundle and install the plug-in!
Let's start by creating the script for the plug-in. Right click on the tut folder and choose New | Blank Script. Name the script enemy-extension.js (just type the name, SE will provide the .js), then open it for editing by double clicking. Delete the initial text (Edit | Clear). Carefully paste in the following code, and then save the file:
uselibrary( "extension" ); function getName() { return "Enemy Card Extension"; } function getDescription() { return "Adds the option to create Enemy cards."; } function getVersion() { return 1; } GameData.parseEditors( "tut/enemy.classmap" );
The first line of this script declares it to be an "extension" type of plug-in. This type of plug-in can modify the game database, and doesn't get listed as a selectable command in the Toolbox menu. Our plug-in must be an extension in order to add a new entry to the New Component dialog. The next few lines define functions that the plug-in system calls to learn basic information about the plug-in. This information is used to describe the plug-in in the Plug-in Manager dialog.
Most files in a plug-in should be located somewhere in the plug-in's resources folder. When we point to a resource from inside of a script, the path to its location is almost always relative to this folder. For example, note that resources/ was left off of the name of the class map file we are loading.
It is the last line of the script that does the real work. This line uses the GameData object to add a new entry to the New Component dialog.
The entries in this dialog are described using a file called a "class map."
Our class map file will only add one entry, so it will be pretty simple. To create it, right click on the tut folder and then choose New | Class Map. Open the file for editing. The comments at the top of the default file describe the syntax of class map files in detail. Read it over if you like. When you are done, delete the initial text from the file and paste in:
@cat-custom Enemy Card = script:tut/create-enemy-card.js
The @cat-custom line starts a category. Any definitions that follow will appear in that category in the new component dialog. The category name is a special predefined category that you can place custom material in. When the program runs in English, components in this category will be listed in a category named "Custom Components". You don't need to use this name; you could just as easily write Debbie's Special Cards there if you want. The next line defines a new kind of component called an "Enemy Card" that SE can create by running the script resources/tut/create-enemy-card.js. When the user picks the "Enemy Card" option, this script will be called to create the new component and open an editor window for it.
I'm sure you can guess that next we'll write the create-enemy-card.js script. That script will basically just do the same things that we did by hand before by changing the card settings, but it does them only for the particular card that it creates. Create a new blank script as before, calling it create-enemy-card.js. Open the file, delete the initial content, and paste in:
// create a standard Ally card and make it the active editor if( PluginContext.createEditor( "ally" ) ) { var editor = PluginContext.activeEditor; var component = editor.gameComponent; // customize the new component Patch.card( component, "ally-text", "Enemy", "ally-front-sheet-template", "tut/enemy-front.png", "ally-back-sheet-template", "tut/enemy-back.jpg" ); // clear the example text and/or install a new example if desired component.clearAll(); // once all modifications to the content of the component have been // made, call this method to copy the changes to the editor editor.replaceEditedComponent( component ); component.markSaved(); } else { error( "Failed to create base component" ); }
The PluginContext.createEditor method creates a new component and makes its editor the active editor. The type of component that is created is determined by the single parameter, which is the name of a key in one of the registered class maps. The standard components have keys that start with "app-new-" for historical reasons. As a shortcut, you can leave this prefix off (the actual name of the Ally component key is app-new-ally). The names for other component types can be found in the in the standard SE class map, resources/editors/standard.classmap.
The next two statements obtain the editor (the window used to edit the component) and the component itself. Once we have the component, we can use the Patch object to modify the component's settings just as we did above in the Quickscript window. You'll note that we've added two more pairs of patch parameters, to patch the template images to use our red image files.
The line component.clearAll(); will clear the card's settings as if we had used the Edit | Clear command. That gets rid of the example text you normally get when you create a new component—after all, that example is for an Ally, not an Enemy. If we wanted, we could modify the name, effects, and comments to create a different example at this point. (Use the Component Viewer plug-in to list the methods that can be used to modify a component. It is built-in to SE but may be inactive by default; open the Plug-in Manager and check its "Active" box in this case.)
Once we have made all of the changes to the component that we wish, we call editor.replaceEditedComponent() so that the editor will update to reflect our changes. Finally, we call component.markSaved(). This causes the component to "forget" that it was changed (when we cleared it). If we didn't do that, the new card would appear to have unsaved changes even though the user hasn't modified it yet.
That's it for scripting, but we still need to create the plug-in root file so SE knows which file contains the plug-in code. To do that, right click on the Extension Plug-in task folder and choose New | Plug-in Root. SE will create the file and open the root editor. Since our plug-in is in enemy-extension.js, select that entry and then press Update:

Now we just need to package the plug-in up in a bundle so we can install it. Right click on the plug-in task folder and choose Make Bundle. When the rename dialog opens, enter the name EnemyExtension, then click on the .seplugin to the right of the name field. That will let us change the extension, which we need to do because this is an extension. Change the extension so that the name field reads EnemyExtension.seext and rename the bundle.
To test the plug-in, right click on the bundle and choose Test Plug-in. This will allow you to test the plug-in in a special test version of Strange Eons. On some platforms you may need to adjust the "JVM" options in order to launch the test application. If you have problems using the test command and don't know how to adjust the JVM options for your system, you can simply install the bundle and restart Strange Eons to test it that way.
The plug-in authoring kit includes a more advanced version of this example. Among other enhancements, it installs a suitable example Enemy and it can be localized to multiple languages.
No matter how you end up testing the plug-in, you should end up with a "Custom Component" category with an "Enemy Card" option that you can use to create a new Enemy card.
And that's it. It may seem complicated, especially if you don't have any programming experience, but it is harder to describe than to do. Once you have knocked a few off yourself, you'll find that it is a fairly quick and painless process.
Return to Home Page Send Feedback
April 28, 2008 — Updated February 27, 2010
`