EZState (also referred to as ESD since that is the file extension where the data is located on disk), is a graph based scripting system. It is used in all games from DS1 onward for various pieces of game logic.

Format

The best way to understand how EZState works is to think of it as a state diagram, where each node is a point or discrete step in a sequence of actions that make up any event. These nodes are referred to as states. Each state in the graph has 4 data pieces attached to it:

  • A list of actions that run on entry to the node: entry commands.
  • A list of action that run at 30HZ while in the node: while commands. These are unused in DS1, but do function.
  • A list of conditions that are run in order after the entry commands and can jump to a new state. These conditions are written using a EZState specific bytecode that is interpreted at runtime.
  • A list of actions that are run on exit from the state: exit commands. These are always run on exit from this state.
For example, the fire surge pyromancy has 3 (major) states.
  1. #138: entry The entry state has it's entry commands set the "ItemInUse" flag, and sets the upper body animation to 6325 for 0.1 seconds.

    The condition (only one present), jumps to the next state once the attack animation (6325) has ended.

    Then the exit commands set the upper body animation back to -1 and unsets the "ItemInUse" flag.

  2. #139: loop The loop state, on entry, sets ItemInUse, sets upper body animation to 6525, and sets SyncAtInit_Active(1).

    For the two conditions present: #1 jumps to state 132 if magic is no longer usable. #2 jumps to state 140 if GetAtkDuration(2) < 0 (think this is if the attack animation in attack slot/type 2 is ended) or IsAtkReleaseRequest(2) (the user has stopped holding the attack button).

    Then the exit commands set set the upper body animation back to -1, unsets the "ItemInUse" flag, and sets SyncAtInit_Active(0).

  3. #140: exit The exit state, on entry, sets ItemInUse and sets upper body animation to 6725.

    There are numerous conditions present, which mostly seem to deal with chaining into a new attack or something??

    Then the exit commands set the upper body animation back to -1 and unsets the "ItemInUse" flag.


To better understand the state structs check out Meow's SoulsFormat Library for parsing ESD files , now merged into SoulsFormats proper.

To understand the bytecode itself (unnecessary with current tools) check out https://gist.github.com/JKAnderson/e3fd38d4efd5f3350e13a613319b8bed

Usage

ESD is used in all games from Dark Souls onwards for various pieces of game logic.

  • In Dark Souls, action ESD is used for enemy animation states (chr\enemyCommon.esd.dcx) and player animation states (chr\c0000.esd.dcx). Talk ESD is used for dialogue trees (script\talk\), including bonfires.
  • In Dark Souls II, event ESD is used for event scripts and dialogue trees (ezstate\), and AI ESD is used for enemy AI (ai\).
  • In Bloodborne, Dark Souls III, and Sekiro, talk ESD is used for dialogue trees (script\talk\). Commands are added over time from game to game, with DS1 talk ESD as a basis, and occasionally reused for repurposed mechanics.

Action ESD

Action ESD, in enemyCommon and c0000, is used for performing many in game actions such as: attack chains, climbing a ladder, sitting at a bonfire, and basically any in game event or action done by the player.

The current list of labeled states is in ESD State List

Editors

DS1 EZState can be edited with AinTunez's Zeditor , and c0000 editing usually uses it.

SoulStruct supports talk and chr ESD editing, with DS1 being best-supported.

All known ESD can be edited with the control flow-based decompiler ESDLang esdtool.exe, but it is mainly recommended for DS2 ESD and talk ESD in Bloodborne onwards, in which multiple machines in an ESD calling each other is extensively used. Each machine is turned into its own function in a Python-like language.