Strange Eons
Creating a Basic Extension
Send Feedback Home Page > Strange Eons > Miriam's Basement > Creating a Basic Extension
This tutorial demonstrates how to create a simple extension that adds a custom expansion called "The Shining Trapezohedron". It demonstrates the basic steps needed to create either a plug-in or an extension: plug-ins and extensions are basically the same thing (an extension is technically a special type of plug-in). The difference between them has to do with when SE starts them and what they are allowed to do. More about that in a moment. But first...
This tutorial can be used in two ways. First, it provides a solid grounding in the fundamentals of writing SE plug-ins. Those who want to write their own, more complicated plug-ins can revel in the details and absorb as much as possible. Second, those who just want to have their own expansion symbol can choose to skim over the details and follow the tutorial as a "cookbook" example, editing the details to use their own expansion name and graphics. However, if all you want is a custom icon, there is an easier way to get one. The Introducing the Project System tutorial shows you how to create a custom expansion in a couple of minutes by letting Strange Eons do all the coding for you.
A typical plug-in is a small tool that is listed in the Toolbox menu in Strange Eons. Plug-ins are started just before SE starts, and can be stopped and reloaded at any time. This means that you can add and remove plug-ins without restarting SE. This type of plug-in is technically called the "activated" type.
An "extension" type plug-in, on the other hand, is started much earlier on as SE is starting up, when SE is building its internal model of all of the game content and rules. As a result, an extension can modify SE's database of game material: it can add new kinds of cards, new locations, new items, new expansions, and so on. The trade-off for this flexibility is that an extension can't be changed or unloaded while SE is running, because open files may be relying on the new extension material. After installing an extension, you have to restart SE before it can take effect.
In essence, you write an extension if you want to be able to write new information into SE's game model. You write an activated plug-in if you want to create a tool that can read that game information, or affect the currently edited card.
There is a third, rarely used type of plug-in called the injected type. This type of plug-in works like an activated plug-in, but it isn't listed in the Toolbox menu. (The plug-ins that add new case book export options are of this type.)
A plug-in file is a ZIP archive with a special file name extension, either .seplugin (for activated or injected plug-ins) or .seext (for an extension). The file name extension tells SE when it should load the plug-in during its start-up. The root directory of the plug-in file must contain a file named eons-plugin. This file, called the plug-in's root file, is a plain text file that SE reads to discover which file in the ZIP archive has the actual plug-in code in it. Here is the file we'll use for our extension:
# # Custom Expansion example extension # # This extension is an example of how to add a custom expansion. # script:resources/tut/trapezohedron.js
If you look at the root files in other plug-ins, you will see that they often start with a comment like this: # CATALOGUEID{...} (the dots will be a bunch of letters and numbers). This is a catalogue ID, and it is used to indicate in an unambiguous way what plug-in this is, and which version. The plug-in catalogue system uses this information to compare the plug-ins you have installed to the ones listed in a catalogue, so it can figure out whether any of your plug-ins have updates available.
The first few lines (those marked with a # are comments. They are ignored by SE. (The same rule applies to all of the settings files in SE, although script files mark comments differently.)
The final line tells SE to look in the resources/tut folder (inside the plug-in ZIP archive) for a JavaScript file called trapezohedron.js. One should normally place the materials for your plug-in inside of a subfolder of resources/ with a unique name. Here we have used tut. I recommend using your initials or your forum name. This naming convention keeps the files that belong to different plug-ins from conflicting with each other.
SE knows that the file must be a script because the line starts with script:. If it hadn't, it would have been treated as the name of a compiled Java class. (See the plug-in kit documentation for more information about the eons-plugin file and compiled versus script plug-ins).
Let's create a basic extension that we can get running and test. To start with, create a new project using File | New Project. Call your project something like The Shining Trapezohedron. If you need help creating the project, the Introducing the Project System has a video tutorial you can follow.
Now we need a place inside of our project to put the plug-in code. For this, we will use Add New Task to add a new Empty Plug-in task. Usually if we were writing a plug-in from scratch we'd use the New Plug-in task, but in this case we want to learn how plug-ins are put together. We don't want Strange Eons doing the work for us. Call your new task something like "Expansion Icon".
Now we'll create the root file for the plug-in. Since the plug-in doesn't current have a root, right clicking on the plug-in task will reveal a New | Plug-in Root command. When you use it, an eons-plugin file will be created and opened in the Root Editor.
Normally, the Root Editor has two views: you can either edit the code directly, or you can select a script or class from a simple list. In this case, there are no files to choose, so you can only use the code editor. Copy and paste the text in the box above into the script editor, replacing the current code. Then press Update.
Now we need to create the script file that we told SE to run in our eons-plugin file. Create a folder by right clicking on the task folder and choosing New | Folder. Call it resources. Now right click on the new folder and create another new folder inside of it called tut. Finally, right click on that folder and choose New | Blank Script to create an empty script file. Name it trapezohedron. Don't worry about the extension: SE knows to make it a .js file. Double click the file to edit it, then paste in the following:
uselibrary( "extension" ); println( "The extension was loaded." );
The first line tells SE that this plug-in will be an extension. The other line prints a message to the script console. Save the file.
This is enough for a working plug-in. We can package it up and try it out. Right click on the plug-in task folder and choose Make Bundle. SE will pack it up, then offer to rename it. In the name field, type in TrapezohedronExpansion. There is a small problem. Because we started from an empty plug-in, SE does not know what kind of plug-in we are making. Therefore, it used the default file name extension of .seplugin. But this is an extension plug-in, so the file name needs to end in .seext. To fix this, move the pointer over the .seplugin that appears after the name field in the rename dialog box (the pointer will change to a hand) and click on it. The file extension will become editable and we can now rename the file to TrapezohedronExpansion.seext.

Now we can test the plug-in. Right click on the bundle and choose Test Plug-in. A dialog will appear with some options we can use when testing. For example, we can change the languages used for the test. Don't worry about any of these, just press Test. In all likelihood, a second copy of Strange Eons will start in a moment, running in test mode. You can tell when a copy of SE is in test mode because it will be golden in colour. If it doesn't start, then you may need to adjust the JVM Command on the test dialog. If for some reason you can't get test mode to work, just double-click on it and choose Install to install it immediately. Then restart SE so the new plug-in will load.
In either case, partway through start-up, the script console window will pop open and display the message we asked it to print in the last line of the script. (If it prints an error message instead, or nothing, you've made a mistake somewhere. Go back and try again.)
TrapezohedronExpansionV1.seext
So far, so good. Before you carry on with the extension, try double clicking on the root file again now that you have a script in your file. This time, you'll get the handy dandy list version, and the current plug-in script will be selected. (It is also the only script, but still….) You can switch between the "easy" view and the real code using the button in the lower-left corner. The panel labelled "Catalogue Information" lets you create a catalogue ID in case you want to add this plug-in to a catalogue at some point. (You can read about catalogue IDs in an orange margin note a little way back on this page.) When you are done looking around, click on Cancel.
Getting back to the task at hand: an extension that just prints a message isn't much use. There are a few other issues as well: if you open the Toolbox menu and select Manage Plug-ins..., you'll see your new extension listed with a big long ugly name and no description. We should really remedy that. If you are using the test version of SE, quit from it now.
How can we get the extension to print a proper name? If we have a look at the "Scripted Plug-ins" section of the readme.html in the Plug-in Kit, we will see that it mentions the functions getName(), getDescription(), and getVersion(), among others. It also has a link to a "plug-in template" script. If we look at the template, it includes the functions we mentioned, with descriptive comments. It has some other functions, too, but they are used by regular ("activated") plug-ins, and are not needed by extensions.
Based on the example functions in the template script, let's add a name, description and version number to our extension. Change our script to read:
uselibrary( "extension" ); // return the version number for this extension function getVersion() { return 2; } // return the name that appears in the Plug-in manager function getName() { return "Shining Trapezohedron Extension"; } // return the description that appears in the Plug-in manager function getDescription() { return "Adds the Shining Trapezohedron expansion."; } println( "The extension was loaded." );
Save this, then use Make Bundle again to update the plug-in bundle. Test it just like you did the previous version.
TrapezohedronExpansionV2.seext
This is an improvement, but the extension doesn't actually do what it says on the tin. All it does is print a message that gloats about the fact that the script has been run. We want to add our expansion and its graphics to SE so that our symbol can appear on new cards.
Since we want to add something to SE's database of game material, that is a good place to start looking. As it turns out, there is a class called GameData that exposes this database. The plug-in kit contains documentation for this class. You can reach it from the main readme file by following the link Plug-in API Documentation, which is listed just before the section on Scripted Plug-ins starts. Then in the "All Classes" list, select "GameData". There is a lot of stuff in there, and some of the descriptions may be confusing if you don't have a programming background. Don't be intimidated. Just scan around and look for anything that looks like it has something to do with expansions. What you will find is a method (function) called registerExpansion. Bingo. There are a couple of different versions of this function. The ones that take more parameters give you the most control, but require more work. Let's have a look at the description of the "big" one:
cardImage,
displayed on light backgrounds, and inverseCardImage, displayed
on dark backgrounds. You may pass null for
inverseCardImage, in which case an inverse image is generated
automatically.
Expansion icons are typically 24 by 18 pixels with a transparent background and may be in colour. Card symbols can be any size, but should be equal in width and height. Symbols are normally black with a transparent background. If you use colour, you will probably want to supply your own inverse image instead of relying on an automatically generated one.
code - a unique identifying string, typically 2
letters, which will be used to identify the expansion that items and
locations belong toname - the human-readable name of this expansion,
preferably in the user interface languageiconImage - an image that is used as the expansion's
icon in item and location drop-down listscardImage - an image that is used to draw the
expansion's symbol on cardsinverseCardImage - an alternative to cardImage
that is used on cards with a dark background java.lang.IllegalArgumentException - if an expansion
with this code has already been registeredOK. There is a lot of information in there. Let's summarize it. To register our expansion, we need an "expansion code", the name of the expansion, a small image to use as the expansion's icon when it appears in menus and drop-down lists, and either one or two images to use when the expansion symbol is drawn on cards. That's not so bad. Let's tackle them one at a time.
Let's start with the easiest first: an expansion name. For this example, we're making a hypothetical expansion called "The Shining Trapezohedron". So that will be the name we'll use when registering the expansion. Done.
Each expansion in SE has a unique code that provides a way to identify it regardless of the language that is in effect. For example, the code for Dunwich Horror is DH, while the code for Kingsport Horror is KH. We'll need to provide our own code for this expansion, one that hasn't been taken yet. If we try to register an expansion using a code that's already been used, the extension will fail with an error (an IllegalArgumentException, as documented above).
We could just use whatever code we want, and then change it if the extension fails. But let's have a look at the SE resources and see if we can figure out what codes are already used. (Have a look here if you don't know how to extract the Strange Eons resources.) There are a few places that we can look in order to deduce the codes (for example, the file names of the symbols in the expansiontokens folder include the codes). We can also find this in the comments for text/EN/items.txt:
# After the cost is listed, an optional annotation in parentheses # indicates the expansion from which the card comes: # (NX) = No Expansion (available in base game) # (DP) = Dark Pharaoh # (DH) = Dunwich Horror # (KY) = The King in Yellow # (KH) = Kingsport Horror # (BG) = The Black Goat of the Woods # (IH) = Innsmouth Horror # # - If no annotation is listed, the (NX) annotation is assumed. # - If the program doesn't recognize the annotation, it will # assume this is a new expansion and provide a generic # custom expansion icon (see unknown-icon in settings.txt). # If you wish to describe your own custom expansions, it is suggested # that you use the annotations (X1), (X2), etc., to avoid conflicts # with any future official expansions. # - The new item creation dialog in the Investigator editor uses the # expansion code (XX) for custom expansions.
So the codes that are already used are: NX DP DH KY KH BG IH XX. (If a new expansion has come out since this was published, then your list may be longer.) Since our expansion is The Shining Trapezohedron, and since the code is free, let's use the code ST.
As it turns out, this is all the information that we really need to register our expansion. If you read the documentation for the GameData class, you'll see that there is a version of registerExpansion that takes just a code and name. Let's try using that to apply what we have so far.
The GameData class is in the resources package, so we could register our expansion using "resources.GameData.registerExpansion( "ST", "The Shining Trapezohedron" );". However, if we read the documentation for the "extension" library, (from the main readme in the plug-in kit, follow the link "The Strange Eons Standard Scripting Library Documentation", then click "extension") we will learn that the library provides a little short-cut because it defines a global GameData variable for us that is the same as the longer resources.GameData.
This gives us the following script:
uselibrary( "extension" ); // return the version number for this extension function getVersion() { return 3; } // return the name that appears in the Plug-in manager function getName() { return "Shining Trapezohedron Extension"; } // return the description that appears in the Plug-in manager function getDescription() { return "Adds the Shining Trapezohedron expansion."; } GameData.registerExpansion( "ST", "The Shining Trapezohedron" );
You should know what's coming next: edit your script to register the expansion, make another bundle, and test it out. Now check the expansion symbol menu, and sure enough: there is our new expansion. But it's using a default image, and we want to provide our own custom graphics, so let's keep going.
TrapezohedronExpansionV3.seext
Checking the documentation for registerExpansion again, it suggests using a transparent 24 pixel wide by 18 pixel high image. As it turns out, if we are using an image editor than can open Photoshop (PSD) files, there is a template that we can use in the graphics templates folder of the plug-in kit, called expansion-icon.psd. The template is already the right size and includes a layer style that we can copy to make our icon look more like the standard ones. Here is the icon we'll use:
![]()
Save your icon image in the resources/tut folder of your task using the name st-icon.png. You can either save it there directly from your image editor, or save it somewhere handy, right click on it in your file system viewer (e.g., Explorer for Windows or Finder for OS X) and choose copy; then go to the project pane in SE and paste it into the right folder.
The last element we need to complete our project is the symbol for the expansion that will be printed on cards when the expansion is selected. The documentation tells us there are two different versions of this graphic: one that is used on light-coloured cards (investigator, items, etc.), and one that is used on dark-coloured cards (such as ancient ones). We only need to provide the regular version: if we don't provide a version for dark cards it will be created automatically. However, if we want to use a full-colour symbol instead of a black-and-white one, we should provide both images. (You can use the same image for both.) The documentation also tells us that we can make the image any size we want, but that it should be square. If your picture is not as tall as it is wide, I recommend that you position the graphic along the bottom of the image, leaving any extra blank space at the top. The plug-in kit also provides a sample expansion symbol in the graphics templates folder. Here is the card symbol we'll use:
![]()
Similarly to the icon, put a copy of this file in the task folder as resources/tut/st-symbol.png.
Now we have all the graphics and a script that registers the expansion. We just need to make the script use our images. Checking the documentation for registerExpansion yet again, we see that the "big" version wants the images to be supplied as "BufferedImage" objects. There is a scripting library (imageutils) that would make it easy for us to load those from our extension, but we don't need to use it, because the GameData class provides a different version of registerExpansion that will do the work for us. We just need to tell it where to look in the resources folder:
uselibrary( "extension" ); // return the version number for this extension function getVersion() { return 4; } // return the name that appears in the Plug-in manager function getName() { return "Shining Trapezohedron Extension"; } // return the description that appears in the Plug-in manager function getDescription() { return "Adds the Shining Trapezohedron expansion."; } GameData.registerExpansion( "ST", "The Shining Trapezohedron", "tut/st-icon.png", "tut/st-symbol.png" );
If we wanted to provide both the regular version of the symbol and the inverse symbol to use on dark cards, we could have added the name of the inverse version as a fifth parameter to the method call.
TrapezohedronExpansionV4.seext
Let's test our final version to check that everything works as expected. Save the updated script, make and test the plug-in bundle. When the test version of SE starts, create a new investigator and then select the new expansion in the menu. And the result:

If at some point in this tutorial you get stuck or you mess up your project, you can use one of the versions of the extension on this page to help. Download a version of the extension from this page, then choose Add New Task in your project (make a new project if you have to) and this time pick the Import Plug-in task type. In the file chooser that appears, select the extension that you downloaded. SE will then unpack the plug-in bundle into a plug-in task folder for you, and you can have a look at it to see where you went wrong.
Now you've got an extension that registers your custom expansion. If you share .eon files with your friends, they'll be able to see your custom symbol if they have the extension installed.
As a first project, try modifying the script and images from this example to create an expansion of your own. Once you've mastered that, you might feel ready to take on a more complex project. There are lots of examples in the plug-in kit you could start with. Are you developing an entire expansion, complete with custom items and investigators, that will use your new expansion symbol? Have a look at the more advanced version of the extension in this tutorial in the plug-in kit. It shows you how to add your own custom items to the list of selectable items in the investigator editor (complete with your new expansion icon, of course).
Return to Home Page Send Feedback
October 25, 2008 — Updated January 01, 2011