Getting Started
This guide walks you through creating your first mod for Fall of an Empire, from setting up the folder structure to seeing your content in-game.
Prerequisites#
- Fall of an Empire installed
- A text editor (VS Code, Notepad++, or any editor with syntax highlighting)
No special tools or SDKs are required. Mods are written in AngelScript, a scripting language that compiles at game startup.
Creating Your Mod#
1. Create the Folder
Create a new folder inside the Mods/ directory at the root of the game installation:
The folder name becomes your mod's internal ID.
2. Add mod.json
Create Mods/MyFirstMod/mod.json with your mod's metadata:
{
"id": "my_first_mod",
"name": "My First Mod",
"version": "1.0.0",
"author": "Your Name",
"description": "A simple mod to learn the basics.",
"load_order": 100,
"dependencies": [],
"incompatible": []
}Field reference:
idnameversion"1.0.0").authordescriptionload_order100.dependenciesincompatibleLoad order ranges:
3. Create the Directory Structure
Organise your script files by content type:
You only need to create directories for the content types you are adding. The Script/ directory is discovered automatically by the mod loader.
AngelScript Basics for Modders#
AngelScript is the scripting language used by Fall of an Empire. It resembles C++ but with several simplifications. Here are the essentials you need to know.
Class Declaration
Every moddable content type is a class that extends a base type:
UCLASS()
class UMyTrait : UTrait
{
// Properties and methods go here
}- The
UCLASS()decorator registers the class with the engine. - Class names must start with
U(for UObject-derived types). - No semicolons after the closing brace of a class.
Setting Default Properties
Use the default keyword to set class default object (CDO) properties:
default TraitName = Localization::GetText("MyMod", "MyTrait_Name");
default Authority = 10.0f;
default bCanBeGained = true;
default IconKey = n"MyTrait";These are evaluated once when the class is loaded, not per-instance.
Constructors
Constructors are used for operations that cannot be expressed as simple default assignments, such as adding to arrays or maps:
UMyTrait()
{
ConflictingTraits.Add(USomeTrait::StaticClass());
ResourceCost.Add(n"Iron", 15.0f);
}Overriding Methods
Override base class methods using UFUNCTION(BlueprintOverride):
UFUNCTION(BlueprintOverride)
bool CanBeGained(UPerson Character) const
{
return Character.Stats.GetAuthority() > 60.0f;
}FName Literals
Many identifiers in the engine use FName, a lightweight hashed string. In AngelScript, create them with the n"..." syntax:
default IconKey = n"MyTrait";
RequiredConditions.Add(EContextSlotType::AtPeace);
ResourceCost.Add(n"Iron", 15.0f);Localised Text
All user-facing text must use the localisation system. Never use hardcoded strings.
// Get localised text (returns FText)
Localization::GetText("Namespace", "Key")
// Convert to FString for string operations
Localization::GetText("MyMod", "Description").ToString()
// Dynamic text with placeholders
Localization::GetText("MyMod", "Effect").ToString().Replace("{Bonus}", "" + Bonus)The first argument is the namespace (use your mod ID), and the second is a unique key. You will add the actual text to a PO file later (see Assets & Packaging).
Arrays and Maps
AngelScript arrays and maps use methods rather than initialiser syntax:
// Arrays
TArray<FName> MyArray;
MyArray.Add(n"Value");
// Maps
TMap<FName, float> MyMap;
MyMap.Add(n"Key", 1.0f);
// Checking map contents
if (MyMap.Contains(n"Key"))
{
float Value = MyMap[n"Key"];
}Common Types
FTextFStringFNameint32floatf to literals: 1.0f.booltrue or false.TArray<T>TMap<K, V>TSubclassOf<T>UMyClass::StaticClass().UObject References
In AngelScript, UObject types are automatically references (no pointers):
// These are references, not pointers
UPerson Character; // Not UPerson* Character
AFaction PlayerFaction; // Not AFaction* PlayerFaction
// Null check with nullptr
if (Character != nullptr)
{
// Use dot syntax, not arrow syntax
Character.Stats.GetAuthority();
}Accessing the Player Faction
AFaction PlayerFaction = StrategyGameplay::PlayerFaction;Tutorial: Creating a Trait#
Let's create a complete trait from scratch. This trait, "Stoic", will grant governance and loyalty bonuses to characters with high constitution.
Step 1: Create the Script File
Create Mods/MyFirstMod/Script/Traits/Stoic.as:
UCLASS()
class UStoic : UTrait
{
UStoic()
{
ConflictingTraits.Add(UTimid::StaticClass());
}
// Display properties
default TraitName = Localization::GetText("MyFirstMod", "Stoic_Name");
default Description = Localization::GetText("MyFirstMod", "Stoic_Description");
default SortOrder = 10;
default IconKey = n"Stoic";
// Generation properties
default Heritability = 0.15f; // 15% chance to inherit from parent
default Probability = 0.08f; // 8% base generation chance
// Stat modifiers (added to character's stats)
default Tactics = 0.0f;
default Authority = 0.0f;
default Cunning = -5.0f;
default Loyalty = 10.0f;
default Governance = 10.0f;
default Constitution = 5.0f;
// Gain/loss behaviour
default bCanBeGained = true;
default bIsTemporary = false;
// Event shown when the trait is gained
default GainedEvent.Title = Localization::GetText("MyFirstMod", "Stoic_GainedTitle");
default GainedEvent.Message = Localization::GetText("MyFirstMod", "Stoic_GainedMessage");
default GainedEvent.Option = Localization::GetText("MyFirstMod", "Stoic_GainedOption");
UFUNCTION(BlueprintOverride)
bool CanBeGained(UPerson Character) const
{
// Only characters with high constitution can gain this trait
return Character.Stats.GetConstitution() > 60.0f;
}
UFUNCTION(BlueprintOverride)
bool CanBeLost(UPerson Character) const
{
return false;
}
}Step 2: Add Localisation Strings
Create or edit a PO file for your mod. For now, you can add entries directly to Content/Localization/Game/en/Game.po (see Assets & Packaging for details on the PO format):
#: MyFirstMod,Stoic_Name
msgctxt "MyFirstMod,Stoic_Name"
msgid ""
msgstr "Stoic"
#: MyFirstMod,Stoic_Description
msgctxt "MyFirstMod,Stoic_Description"
msgid ""
msgstr "This character maintains composure under pressure, earning respect through steady governance."
#: MyFirstMod,Stoic_GainedTitle
msgctxt "MyFirstMod,Stoic_GainedTitle"
msgid ""
msgstr "A Quiet Strength"
#: MyFirstMod,Stoic_GainedMessage
msgctxt "MyFirstMod,Stoic_GainedMessage"
msgid ""
msgstr "Through years of measured decisions and calm resolve, this character has earned a reputation for unshakeable composure."
#: MyFirstMod,Stoic_GainedOption
msgctxt "MyFirstMod,Stoic_GainedOption"
msgid ""
msgstr "A valuable quality."Step 3: Test Your Mod
- Launch the game.
- Start a new campaign.
- Your trait will appear in the trait pool and may be assigned to newly generated characters whose constitution exceeds 60.
To verify the mod loaded correctly, check the log file at Saved/Logs/FallOfAnEmpire.log for:
LogModLoading: Discovered mod: MyFirstMod-- confirms the mod was foundLogAngelscript: Compiled modulemessages -- confirms scripts compiledLogGameData: Discovered X Trait classes-- confirms your trait was registered
How Auto-Discovery Works#
When the game starts, UGameDataSubsystem calls GetDerivedClasses() for each base type (UTrait, UBuilding, etc.). This finds every class in the entire class hierarchy -- including those defined in mod scripts. Your class is registered alongside the base game classes with no additional setup.
This means:
- You do not need to register your class anywhere.
- You do not need to modify any configuration files.
- The class name itself is the unique identifier.
Common Mistakes#
UCLASS() decoratorUCLASS() above the class declarationf suffix on floats, wrong string types, or pointer syntaxGame.po and run the import/compile commandsUMyMod_StoicCanBeGained() returns false for all charactersIconKey value.Add() instead: MyArray.Add(Value)Super:: only works when the parent is also AngelScript.Next Steps#
- Content Types -- Full property and method reference for all content types
- Interactions -- Create diplomacy, espionage, and character interactions
- Events -- Build scripted events with branching options
- Assets & Packaging -- Add icons, package your mod, and distribute it