Creating a Feature

Prepare classes

When creating a Mutator you must create a child Mutator class, but when creating a Feature you must create two classes: one for the Feature itself and one for its config. Name your config class something human-readable and your Feature class should have the same name, but with '_Feature' suffix. For example, if you want your configs to be stored inside 'MyFeatureConfig.ini', bare minimum class skeletons are:

//  MyFeature.uc
class MyFeature extends FeatureConfig
    perobjectconfig
    config(MyFeatureConfig);

defaultproperties
{
    configName = "MyFeatureConfig"
}
//  MyFeature_Feature.uc
class MyFeature_Feature extends Feature;

defaultproperties
{
    configClass = class'MyFeature'
}

Setup config variables in config file

Config variables are meant to be added into your FeatureConfig class. Assuming you want to add bool and int settings, simply add two public config variables:

//  MyFeature.uc
class MyFeature extends FeatureConfig
    perobjectconfig
    config(MyFeatureConfig);

var public config int   levelOfAwesome;
var public config bool  enableTrollEngine;

defaultproperties
{
    configName = "MyFeatureConfig"
}

To setup their default values, instead of defaultproperties define DefaultIt() method:

//  MyFeature.uc
protected function DefaultIt()
{
    levelOfAwesome      = 11;
    enableTrollEngine   = true;
}

Conversion to/from HashTable

To make full use out of your Feature, Acedia also requires that you provide methods to convert to and from its collection. For our example simply add two more methods:

//  MyFeature.uc
protected function HashTable ToData()
{
    local HashTable data;

    data = __().collections.EmptyHashTable();
    data.SetInt(P("levelOfAwesome"), levelOfAwesome);
    data.SetBool(P("enableTrollEngine"), enableTrollEngine);
    return data;
}

protected function FromData(HashTable source)
{
    if (source == none) {
        return;
    }
    //  Second parameters should are fallback - use values from `DefaultIt()`
    levelOfAwesome      = source.GetInt(P("levelOfAwesome"), 11);
    enableTrollEngine   = source.GetBool(P("enableTrollEngine"), true);
}

Setup your feature to use your config

Recommended way to do it is to duplicate your config variables in your Feature class:

//  MyFeature_Feature.uc
class MyFeature_Feature extends Feature;

//  Not actually config variables
var public /*config*/ int   levelOfAwesome;
var public /*config*/ bool  enableTrollEngine;

defaultproperties
{
    configClass = class'MyFeature'
}

Then add code that will handle setting config data for your Feature:

//  MyFeature_Feature.uc
protected function SwapConfig(FeatureConfig config)
{
    local MyFeature newConfig;
    newConfig = MyFeature(config);
    if (newConfig == none) {
        return;
    }
    levelOfAwesome      = newConfig.levelOfAwesome;
    enableTrollEngine   = enableTrollEngine.disableTick;
    //      Here you can also add any logic that needs to be performed when
    //  values of the config variables were swapped mid-game, if you care to
    //  support it.
    //      Just beware that this method can be called when you `Feature` is
    //  both enabled and not (can be checked with `IsEnabled()`).
}

Run initialization / shutdown logic

Instead of normal Acedia's Constructor() or Finalizer(), for Featuires one should use:

protected function OnEnabled()
{
}

protected function OnDisabled()
{
}

These methods are called when feature gets enabled or disabled, which can, in theory, happen several times per game. They aren't called when Feature's config is changed mid-game, so sometimes it is convenient to move part of the initialization logic into SwapConfig().