Downpour Reference/ExtEnum

From Rain World Modding
Jump to navigation Jump to search

ExtEnum is a new data structure in Downpour that is used in place of most enums in the source code, and is designed for better mod compatibility and easy run-time modification of its values.

Extending Existing ExtEnums

The recommended procedure for having your mod extend the entries for existing enum values is to create a new static class within your mod's code that includes the new entries for the enum. This new static class has three goals:

  • Add the new entries to the enum when your mod is enabled.
  • Remove the new entries from the enum when your mod is disabled.
  • Provide a way to reference the new entries you added.

Below is an example of how this might look. The below static class is built to add two new entries to the original game's CreatureTemplate.Type enum:

public class MyModdedEnums
{
    public class CreatureTemplateType
    {
        public static CreatureTemplate.Type NewCreature;
        public static CreatureTemplate.Type AnotherNewCreature;

        public static void RegisterValues()
        {
            NewCreature = new CreatureTemplate.Type("NewCreature", true);
            AnotherNewCreature = new CreatureTemplate.Type("AnotherNewCreature", true);
        }

        public static void UnregisterValues()
        {
            if (NewCreature != null) { NewCreature.Unregister(); NewCreature = null; }
            if (AnotherNewCreature != null) { AnotherNewCreature.Unregister(); AnotherNewCreature = null; }
        }
    }
}

MyModdedEnums.CreatureTemplateType.RegisterValues() would be called on the mod being enabled.

MyModdedEnums.CreatureTemplateType.UnregisterValues() would be called on the mod being disabled.

To reference one of the new entries anywhere in the code where a CreatureTemplate.Type value is accepted, you can reference it with the static fields defined in your class, like with MyModdedEnums.CreatureTemplateType.NewCreature.

Creating New ExtEnums

Whereas a normal enum definition would look like:

public enum Test { Hello, World, Slug, Cat };

The equivalent construction for an ExtEnum would look like:

public class Test : ExtEnum<Test> { 
    public static readonly Test Hello = new Test("Hello", true);
    public static readonly Test World = new Test("World", true);
    public static readonly Test Slug = new Test("Slug", true);
    public static readonly Test Cat = new Test("Cat", true);

    public Test(string value, bool register=false) : base(value, register) {}
}

Additionally, add a reference to your new enum into the ExtEnumInitializer.InitTypes() function to ensure the static entries are populated into memory before they are accessed.

Rewriting Existing Enum Code

For Enum.GetNames(), the code:

for (int i = 0; i<Enum.GetNames(typeof(EnumType)).Length; i++) {
    string name = Enum.GetNames(typeof(EnumType))[i];
}

Becomes:

for (int i = 0; i < EnumType.values.Count; i++) {
    string name = EnumType.values.entries[i];
}

For Custom.ParseEnum() or Enum.Parse(), the code:

EnumType parsedEnum = Custom.ParseEnum<EnumType>(stringValue);

Becomes:

EnumType parsedEnum = new EnumType(stringValue);

Although note for the above that if stringValue is not a registered type for the enum, instead of throwing an exception, this will create an ExtEnum object whose Index value is -1.

Enum variables that were defined as nullable before shouldn't specify the ? operator anymore, as ExtEnums can be null by default.

Collision Detection

As long as you give your enum entries sufficiently unique names, the chance of another mod adding a new entry to the same enum with the same name is low. But if you want to play it safe and write your code to be completely robust to name conflicts, you can check if the name exists before registering the enum value and adapt accordingly. For example:

public static void RegisterValues()
{
    string entryName = "NewCreature";
    int conflictIndex = 1;
    while (CreatureTemplate.Type.values.entries.Contains(entryName)) {
        conflictIndex += 1;
        entryName = "NewCreature" + conflictIndex.ToString();
    }
    NewCreature = new CreatureTemplate.Type(entryName, true);  
}