In the Examples folder you may find the following
hammercan.jsondemonstrate an action that allows players to turn recycled cans into scrap metals with hammers. Just like the one provided by ThingsToDo.campingtools.jsonis a 1:1 replication of the EAAPI actions implemented in CampingTools mod by Jods. While this action is useless now because the mod itself is not working at the moment, this is a good example of how to create a Data Driven Generic Action.fat_to_torch.jsonis a 1:1 replication of the Make Torch action in Things To Do.empty.jsonis a raw and primitve action json that can be used as a starting point.
The examples.eaapi zip file contains the json files, try drop it into /Mods and see the example action start working right away.
--- ↓ LLM write English better than me ↓ ---
A DataDrivenGenericAction (DDGA) is like a LEGO set for creating new actions in the game. Instead of writing complex code, you build your action by snapping together different "blocks" called Providers.
Think of a DataDrivenGenericAction as a skeleton. On its own, it doesn't do much. To make it work, you attach Providers to handle specific logic.
| - | - |
|---|---|
| what audio to play during the action? | AudioNameProvider |
| custom code at specific events (Start, Success, Failure)? | CallbackProvider |
| if the player can perform the action (e.g. calories, condition)? | CanPerformProvider |
| how much liquid is consumed from the subject? | ConsumingLiquidLitersProvider |
| how much powder is consumed from the subject? | ConsumingPowderKgsProvider |
| how many units (stackable items) are consumed from the subject? | ConsumingUnitsProvider |
| how long does the action take in game minutes? | DurationMinuteProvider |
| what is the chance of failure? | FailureChanceProvider |
| custom info (like "Required: 500 cal") in the UI? | InfoConfigProvider |
| when should the action button appear in the menu? | IsActionAvailableProvider |
| if the player needs to look at something specific (e.g. fire)? | IsPointingToValidObjectProvider |
| if the action allowed at the current time of day? | IsValidTimeProvider |
| if the action allowed during the current weather? | IsValidWeatherProvider |
| if the action requires light? | LightRequirementTypeProvider |
| what items are consumed as materials? | MaterialItemProvider |
| what liquids are consumed as materials? | MaterialLiquidProvider |
| what powders are consumed as materials? | MaterialPowderProvider |
| what items are produced? | ProductItemProvider |
| what liquids are produced? | ProductLiquidProvider |
| what powders are produced? | ProductPowderProvider |
| how long does the action take in real-time seconds (progress bar)? | ProgressSecondProvider |
| when should the action be interrupted (e.g. fire goes out)? | ShouldInterruptProvider |
| if the action have multiple sub-options (like "Harvest 1", "Harvest 2")? | SubActionCountProvider |
| what tools can be used for this action? | ToolOptionsProvider |
You don't need to be a programmer to find what you need. Just follow these steps to browse the codebase:
-
Identify the Behavior: Ask yourself, "What part of the action do I want to define?"
- Example: "I want to make sure the player has a stack of 5 of the item"
-
Search by Name: Look for files that end with
Provider.csin theapi/DataDrivenGenericAction/ProviderImplmentations/folder. The names are usually very descriptive.- Search for:
*CanPerformProvider* - You might find:
SimpleCanPerformProvider.cs
- Search for:
-
Check the "Menu": Open the
DataDrivenGenericAction.csfile. This file lists all the "slots" where you can plug in providers.- Look for properties ending in
Provider. - Example:
public ICanPerformProvider? CanPerformProvider { get; set; }
- Look for properties ending in
-
Read the Instructions: Open the provider file you found (e.g.,
SimpleCanPerformProvider.cs). Look at the properties marked with[TinyJSON2.Include](or[Include]). These are the settings you can configure in your JSON file.- Example: In
SimpleCanPerformProvider, you might seepublic float MinNormalizedCondition { get; set; }. This tells you that you can set a minimum condition for the item in your JSON.
- Example: In
Once you know which provider you want to use, writing the JSON is just filling in the blanks.
- The Key is the property name from
DataDrivenGenericAction.cs(e.g.,"CanPerformProvider"). - The Value is an object containing the settings you found in the provider file (e.g.,
"MinGearNormalizedCondition": 0.5). - You also need to specify the Type of the provider so the game knows which "block" to load. This is done with the
$typefield.
- Goal: Limit action to items with > 50% condition.
- Find Slot: In
DataDrivenGenericAction.cs, foundCanPerformProvider. - Find Block: Searched for
*CanPerformProviderand foundSimpleCanPerformProvider. - Check Settings: Opened
SimpleCanPerformProvider.csand sawMinGearNormalizedCondition. - Write JSON:
"CanPerformProvider": {
"$type": "ExamineActionsAPI.DataDrivenGenericAction.SimpleCanPerformProvider, ExamineActionsAPI",
"MinGearNormalizedCondition": 0.5,
"MaxGearNormalizedCondition": 1.0
}- Start with a basic
DataDrivenGenericActionJSON structure. - Check
DataDrivenGenericAction.csto see what behaviors you can customize. - Browse
ProviderImplmentationsto find pre-made logic blocks. - Open the provider file to see what knobs and dials you can turn (properties with
[Include]).
Most, but not all, of the entries in a DDGA json file can be omitted for simplicity. You usually can remove lines with null value to keep your json file short. But in case like SimpleCanPerformProvider, if you only set MinStackSize but left MaxStackSize size, because a stack is never 0 sized, the action can never be performed.
If the built-in providers don't do what you need, EAPI can be intuitively extended with additional providers. For example, for the fat_to_torch action, IsPointingToBurningFireProvider, MinCaloriesCanPerformProvider, SpecificGearToolOptionsProvider are created.
-
Identify the Interface: Determine which interface corresponds to the behavior you want to customize.
- Example:
ICanPerformProviderfor check conditions,IToolOptionsProviderfor tool selection logic. - Check
api/DataDrivenGenericAction/ProviderInterfaces/for all available interfaces.
- Example:
-
Implement the Class: Create a new class that implements the chosen interface. This class can be part of your own mod assembly.
-
Expose Data to JSON: Use the
[TinyJSON2.Include]attribute on any public properties you want to be configurable via the JSON file. -
Use in JSON: Refer to your custom class using its fully qualified name (Namespace.ClassName, AssemblyName) in the
$typefield of your JSON.
using ExamineActionsAPI;
using ExamineActionsAPI.DataDrivenGenericAction;
namespace MyCustomMod
{
public class MinCaloriesCanPerformProvider : ICanPerformProvider
{
[TinyJSON2.Include]
public float MinCalories { get; set; }
public bool CanPerform(ExamineActionState state)
{
if (state.Subject?.m_FoodItem == null) return false;
return state.Subject.m_FoodItem.m_CaloriesRemaining >= MinCalories;
}
}
}Corresponding JSON Usage:
"CanPerformProvider": {
"$type": "MyCustomMod.MinCaloriesCanPerformProvider, MyCustomMod",
"MinCalories": 500
}- Single Responsibility: Keep providers focused on doing one thing well.
- No Side Effects:
CanPerformandIsActionAvailablechecks should generally be read-only and not modify game state. - Dependencies: If your provider relies on other mods or specific types, ensure your mod declares those dependencies so the assembly is loaded before the JSON is parsed.