Preamble

This tutorial is mostly based off of Dark Souls 1 EMEVD, however it is extremely compatible with the other Soulsborne games. Later titles will have access to commands and systems that allow for more complex tasks or easier implementation, but the fundamentals will be mostly identical.

The first part of the tutorial will focus on the basic systems EMEVD uses to function, and the second part on commonly used commands and information.


What is EMEVD?

EMEVD is the event system used by most of the Soulsborne games. It consists of multiple EMEVD files, each containing numerous individual events. Events use EMEVD commands that check or modify various game systems to produce unique behavior; ranging from making an enemy never respawn if it has died before, to managing entire boss fights.

Each event runs its commands in order from top to bottom (unless a specific command demands otherwise), and ends once the last command is run (unless a specific command demands otherwise).

The entire event system is reset under various circumstances, such as quitting and reloading the game, warping, and dying. Individual map EMEVDs will also reset when loading and reloading the map. Specific events can be set to reset upon bonfire rests.

As a final note: EMEVD does not directly handle (but can interact with) some other scripting systems, notably: dialog (which is handled by talkESD), and AI (which is handled by lua files).


Getting Started

Download a tool for modifying EMEVD. I recommend DarkScript 3.

Downloading a tool for modifying map data isn't required for this tutorial, but it is recommended when modifying EMEVD normally as Entity IDs are defined there. I recommend DSMapStudio.

Unpack your game if you haven't already.


Accessing EMEVD Files

PRQ9ZPv.png


Within the unpacked game will be a folder named "Event". This is the location of all EMEVD files. Most EMEVD files are only active while a specific map is loaded; with the name of the EMEVD file corresponding with the Map ID. The Common EMEVD is always active within every map.

Games after Dark Souls 1 also include the Common Function EMEVD, which contains individual events with Event Parameters that can be used in every map.

Setting Up a Basic Event



Each individual event consists of declaring the event, declaring its parameters, and initializing it.


Declaring the event

Example of an basic event template:

Event(1231231, Default, function() { //Event ID, Restart Type, Event Parameters
 
});

When declaring an event, you are setting 3 things:

The ID of the event, the event restart type, and the event parameters (when desired).

The Event ID is used as an identifier when initializing events. It also corresponds with an Event Flag offset by the Event Slot, I.E. Event ID + Event Slot = Event Flag. This flag is set to ON when the event has fully ended.

Only certain ranges of IDs are valid and won't cause issues. You can avoid nearly all problems by deriving an ID off of an 8 digit vanilla event ID. For example, if a vanilla event is 10003010, then the 10000000-10009999 range is valid.

The Event Restart Type determines if the event should reset when a bonfire is used. It has two options: Default, which does not reset the event on bonfire usage; and Restart, which does reset the event on bonfire usage.

The Event Parameters: See Section: Event Parameters.


Initializing Events

1oNFEUS.png


At the top of each EMEVD file are Event 0 and Event 50, which are always run when the EMEVD first becomes active or is reset. Event 50 runs and finishes before Event 0.
These two events usually contain commands to accomplish some tasks like a normal event would, but more importantly they are responsible for initializing every other event.

When adding a new event, you must initialize it, otherwise it will never be run. In most cases you can initialize it at the very bottom of Event 0, but if desired you can initialize it under specific conditions or even from other events.
InitializeEvent(0, 1231231, 0); //Event Slot, Event ID, Event Parameters

When Initializing the event, you are setting 3 things:
The Event Slot, the Event ID, and the Event Parameters (when desired, use 0 otherwise).

The Event Slot is used to identify a singular event separately from other instances of the same event. Should be 0 for only one instance of an event. Used with commands that specifically interact with individual instances of an event. Also offsets the Event Flag that corresponds with the Event ID set when the event ends, I.E. Event ID + Event Slot = Event Ended Flag. Multiple instances of the same event must have different Event Slot values (0,1,2, etc), or alternatively -1 which also will not offset the Event Ended Flag (-1 needs more testing. May be ds1 only, maybe have unknown effects).


Event Parameters

Event Parameters are used to essentially set a (read only) variable during initialization for the event to use. Useful when the same event is being reused for multiple situations.
The first value (I.E. the 0 in X0_4) identifies the parameter. With vanilla events this value increments by 4 for each additional parameter (0, 4, 8, 12). The second value (I.E. the 4 in X0_4) determines how much data it can store (bit length), but in 99% of cases "4" is fine.

InitializeEvent(1, 1231231, 4040, 4444); //Event Parameters: X0_4 = 4040, X4_4 = 4444
InitializeEvent(2, 1231231, 5050, 5555); //Event Parameters: X0_4 = 5050, X4_4 = 5555
 
...
 
Event(1231231, Default, function(X0_4, X4_4) { //Expected parameters declared. X0_4 is 1st, X4_4 is 2nd
 
    SetEventFlag(X0_4, ON); //set flag X0_4 (4040 or 5050) ON
    SetEventFlag(X4_4, ON); //set flag X4_4 (4444 or 5555) ON
});

Note: A few commands require float values (values that can use decimals) instead of integers. If you want to use parameters with float values, you will have to convert the float value into hex and then into decimal. You can then use that final decimal value for parameters.
Example: Float Value 1.5 = Hex 3fc00000 = Decimal 1069547520. Use 1069547520 for the parameter.


Commonly used commands and information


Event Flags

Event Flags are used to store information, typically ON or OFF. However, they can also be used used to store values when used with Event Value commands.

Only certain ranges of IDs are valid and won't cause issues. You can avoid nearly all problems by deriving an ID off of an 8 digit vanilla event ID. For example, if a vanilla event is 10003010, then the 10000000-10009999 range is valid.

Flags with a 4 digit ending value of 5000 or higher (such as 10005000) will be set to OFF whenever the event system is reset. This is not always the case in every Fromsoft game though, and different offsets are used for different behavior. Elden Ring, for example, uses 2000+ instead of 5000+.

All event flags are reset upon starting New Game Plus.


Entity IDs

Entity IDs are values used to identify specific characters, objects, and other things in map data.

Most Entity IDs are defined within map data, with at least one very important exception: The Player's Entity ID is always 10000.


End Commands

The Event End commands end or restart the event when used. Most of these commands require specific criteria to be met before running, however there is one notable exception:

The EndUnconditionally() command immediately ends or restarts the event when run. Unconditionally restarting events is very useful in a lot of situations, as it allows events to run repeatedly without restarting the event system.

Event(1231231, Default, function() {
 
    WaitFixedTimeSeconds(10); //Wait 10 seconds
 
    ClearSpeffect(10000, 125000); //Remove special effect 125000 from player (entity ID 10000)
 
    EndUnconditionally(EventEndType.Restart); //Restart the event
});

Skip Commands

The Skip commands skip over other commands, allowing for some more complex behavior.

Event(1231231, Default, function() {
 
    SkipIfEventFlag(2, ON, TargetEventFlagType.EventFlag, 555444); //If Flag 55544 is ON, skip next 2 commands.
    WaitFixedTimeSeconds(10); //Wait 10 seconds
    SetEventFlag(555444, ON); //set flag 555444 ON
 
    ClearSpeffect(10000, 125000); //remove special effect 125000 from player
});

Condition Groups

Condition Groups refer to an EMEVD system that has functionality somewhat similar to a normal programming language's conditional statements.

There are three condition group types: AND, OR, and MAIN.
AND requires every check to be true.
OR requires any check to be true.
MAIN is special, and will halt the event and repeatedly check all conditions until they are true.

Event(1231231, Default, function() {
 
    IfEventFlag(AND_01, ON, TargetEventFlagType.EventFlag, 555444); //Check if Event Flag 555444 is ON
    IfEventFlag(AND_01, ON, TargetEventFlagType.EventFlag, 888999); //Check if Event Flag 888999 is ON
 
    IfConditionGroup(MAIN, PASS, AND_01); //If AND_01 is TRUE, continue. If not, keep checking all conditions.
    ClearSpeffect(10000, 125000); //remove special effect 125000 from player
});


AND and OR have multiple slots, and each slot can be used independently from the other.

In the next example, the MAIN check will only pass if:
Flag 555444 OR flag 888999 is true
AND
Flag 222333 OR flag 666777 is true

Event(1231231, Default, function() {
 
    IfEventFlag(OR_01, ON, TargetEventFlagType.EventFlag, 555444); //Check if Event Flag 555444 is ON
    IfEventFlag(OR_01, ON, TargetEventFlagType.EventFlag, 888999); //Check if Event Flag 555444 is ON
 
    IfEventFlag(OR_02, ON, TargetEventFlagType.EventFlag, 222333); //Check if Event Flag 222333 is ON
    IfEventFlag(OR_02, ON, TargetEventFlagType.EventFlag, 666777); //Check if Event Flag 666777 is ON
 
    IfConditionGroup(AND_01, PASS, OR_01); //Check if OR_01 is TRUE
    IfConditionGroup(AND_01, PASS, OR_02); //Check if OR_02 is TRUE
 
    IfConditionGroup(MAIN, PASS, AND_01); //If AND_01 is TRUE, continue. If not, keep checking all conditions.
    ClearSpeffect(10000, 125000); //remove special effect 125000 from player
});


Additionally, condition groups can be used outside of MAIN if you need the event to continue even if a check isn't true. This is referred to as an Uncompiled Condition Group.
Event(1231231, Default, function() {
 
    IfCharacterDeadalive(OR_07, 44444, DeathState.Dead) //Check if character with entity ID 44444 is dead
    EndIfConditionGroupStateUncompiled(EventEndType.End, PASS, OR_07) //End the event if OR_07 is TRUE
 
    ClearSpeffect(10000, 125000); //remove special effect 125000 from player
});

Mattscript

Recent versions of Darkscript 3 also include Mattscript, which converts the EMEVD script into a more readable Javascript-esque format. I would highly recommend familiarizing yourself Mattscript usage. See for more information: http://soulsmodding.wikidot.com/tutorial:mattscript-documentation


Random examples



#1: (DS3, original event by NamelessHoodie)
When player has special effect 1122 AND 2233, give them special effect 4455.
When player no longer has special effect 1122 OR 2233, remove special effect 4455
Restart once the end has been reached.

Event(123123, Default, function() { //Event ID, Restart Type, Event Parameters
 
    IfCharacterHasSpeffect(AND_01, 10000, 1122, true, ComparisonType.Equal, 1); //has special effect 1122
    IfCharacterHasSpeffect(AND_01, 10000, 2233, true, ComparisonType.Equal, 1); //has special effect 2233
    IfConditionGroup(MAIN, PASS, AND_01); //wait here until every AND_01 is TRUE
 
    SetSpEffect(10000, 4455); //Give player special effect 4455
 
    IfCharacterHasSpeffect(OR_01, 10000, 1122, false, ComparisonType.Equal, 1); //does NOT have spEffect 1122
    IfCharacterHasSpeffect(OR_01, 10000, 2233, false, ComparisonType.Equal, 1); //does NOT have spEffect 2233
    IfConditionGroup(MAIN, PASS, OR_01); //wait here until any OR_01 is FALSE
 
    ClearSpeffect(10000, 4455); //Remove special effect 4455 from player
 
    EndUnconditionally(EventEndType.Restart) //restart the event immediately
 
});

#2: (DS3, original event by PapaAppa)
Disable AI for 3 enemies with specific Entity IDs immediately.
If player is within 30 units of the character with Entity ID 3500312, wait 3 seconds and enable AI for the 3 enemies.
If player rests at a bonfire, restart the event (immediately disabling AI until the player is close enough again).

Event(13506000, Restart, function() { //restart event on bonfire rest
 
    SetCharacterAIState(1281100, Disabled); //Disable AI for character with entityID 1281100
    SetCharacterAIState(3500312, Disabled); //Disable AI for character with entityID 3500312
    SetCharacterAIState(3500311, Disabled); //Disable AI for character with entityID 3500311
 
    IfEntityInoutsideRadiusOfEntity(MAIN, InsideOutsideState.Inside, 10000, 3500312, 30, 1); //Wait here until the player (entityID 10000) is within 30 units of the character with enetityID 3500312
 
    WaitFixedTimeSeconds(3); //wait 3 seconds
    SetCharacterAIState(1281100, Enabled); //Enable AI for character with entityID 1281100
    SetCharacterAIState(3500312, Enabled); //Enable AI for character with entityID 3500312
    SetCharacterAIState(3500311, Enabled); //Enable AI for character with entityID 3500311
 
});

#3 (DS1)
If player has special effect 1 (X0_4) continuously for 10 seconds, grant special effect 2 (X4_4).
If special effect 1 is lost at any point, special effect 2 is removed.

Event(51321060, Default, function(X0_4,X4_4) {
 
    IfCharacterHasSpeffect(MAIN, 10000, X0_4, true); //wait until player has spEf 1
 
    IfCharacterHasSpeffect(OR_02, 10000, X0_4, false); //has player lost spEf 1?
    IfElapsedSeconds(OR_01, 10); ///duration spEf 1 must be present
    IfConditionGroup(OR_01, PASS, OR_02); //if OR_02 passes, also pass OR_01 to get past MAIN
    IfConditionGroup(MAIN, PASS, OR_01); //wait for 10 seconds OR if spEf 1 has been lost
 
    EndIfConditionGroupStateCompiled(EventEndType.Restart, PASS, OR_02); // If OR_02 has passed, player has lost spEf 1. Restart event.
 
    //player has had spEf 1 present for entire duration, grant spEf 2
    SetSpeffect(10000, X4_4); //grant spEf 2
 
    IfCharacterHasSpeffect(MAIN, 10000, X0_4, false); //wait until player has lost spEf 1
    ClearSpeffect(10000, X4_4); //remove spEf 2
 
    EndUnconditionally(EventEndType.Restart); //restart event
});