Site Tools


tutorial:intro-to-elden-ring-emevd

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
tutorial:intro-to-elden-ring-emevd [2025/02/25 15:23] admintutorial:intro-to-elden-ring-emevd [2025/02/27 00:33] (current) – [Model names] admin
Line 26: Line 26:
   * Use DSMapStudio ([[https://github.com/soulsmods/DSMapStudio | Github link]]) to view MSB files, which come from ''map\mapstudio'' in the game hierarchy, and also to view/edit game params. This tool is super important.   * Use DSMapStudio ([[https://github.com/soulsmods/DSMapStudio | Github link]]) to view MSB files, which come from ''map\mapstudio'' in the game hierarchy, and also to view/edit game params. This tool is super important.
   * Use WitchyBND ([[https://github.com/ividyon/WitchyBND | Github link]]) or the older Yabber ([[https://github.com/JKAnderson/Yabber | Github link]]) for unpacking specific formats like archives (ending in bnd.dcx) and game messages (ending in fmg).   * Use WitchyBND ([[https://github.com/ividyon/WitchyBND | Github link]]) or the older Yabber ([[https://github.com/JKAnderson/Yabber | Github link]]) for unpacking specific formats like archives (ending in bnd.dcx) and game messages (ending in fmg).
-  * Use various [[er-reference:main | Elden Ring reference pages]], also listed below under "entry points"+  * Use various [[er-refmat:main| Elden Ring reference pages]], also listed below under "entry points"
  
 Of course, also use an event script editor. These are available for Elden Ring: Of course, also use an event script editor. These are available for Elden Ring:
Line 65: Line 65:
 Don't forget! If something isn't happening in the game when you expect it, make sure your changes are even recognized by the game in the first place. This is best accomplished by simple "is it on" checks which do something conspicuous. For instance, in DarkScript3: Don't forget! If something isn't happening in the game when you expect it, make sure your changes are even recognized by the game in the first place. This is best accomplished by simple "is it on" checks which do something conspicuous. For instance, in DarkScript3:
  
-<code>+<code JavaScript>
 DisplayBanner(TextBannerType.DutyFulfilled); DisplayBanner(TextBannerType.DutyFulfilled);
 </code> or </code> or
-<code>+<code JavaScript>
 AwardItemLot(997200); AwardItemLot(997200);
 </code> </code>
Line 90: Line 90:
 Let's start with some shorter events in the game, before getting into longer ones which pull together a ton of game systems. An event script defines many events which run while the script is loaded. You can think of these like functions in a programming language, each identified by a unique number. An event must be initialized to have any effect in the game at all. All events run in parallel, executing one instruction after the next from top to bottom, sometimes skipping optional instructions or stopping altogether until some condition is met. Let's start with some shorter events in the game, before getting into longer ones which pull together a ton of game systems. An event script defines many events which run while the script is loaded. You can think of these like functions in a programming language, each identified by a unique number. An event must be initialized to have any effect in the game at all. All events run in parallel, executing one instruction after the next from top to bottom, sometimes skipping optional instructions or stopping altogether until some condition is met.
  
-For how loading works, that's a bit complicated. See the [[[er-refmat:elden-ring-map-list|map list]]] for an introduction to maps in Elden Ring. When a map is loaded, its event script is also loaded. When you die, or save and quit, or warp to a grace, all the loaded event scripts are loaded in again from scratch. Everything gets completely reinitialized, and if it's not stored in the save file somewhere, it's like it never happened.+For how loading works, that's a bit complicated. See the [[er-refmat:map-name-list|map list]] for an introduction to maps in Elden Ring. When a map is loaded, its event script is also loaded. When you die, or save and quit, or warp to a grace, all the loaded event scripts are loaded in again from scratch. Everything gets completely reinitialized, and if it's not stored in the save file somewhere, it's like it never happened.
  
 How this works in the event script itself is that there are special events called constructors which get automatically initialized when an event script is loaded. These are events with low ids like 0 and 50. As you can see, these constructor events are responsible for initializing every other event in the file, and a few others too.  How this works in the event script itself is that there are special events called constructors which get automatically initialized when an event script is loaded. These are events with low ids like 0 and 50. As you can see, these constructor events are responsible for initializing every other event in the file, and a few others too. 
Line 96: Line 96:
 For instance, take a look at m14_00_00_00.emevd.dcx, which is the event script for Raya Lucaria. In DarkScript3, you can see its first constructor is a very long event which starts with these lines: For instance, take a look at m14_00_00_00.emevd.dcx, which is the event script for Raya Lucaria. In DarkScript3, you can see its first constructor is a very long event which starts with these lines:
  
-<code>+<code JavaScript>
 $Event(0, Default, function() { $Event(0, Default, function() {
     RegisterBonfire(14000002, 14001952, 0, 0, 0, 5);     RegisterBonfire(14000002, 14001952, 0, 0, 0, 5);
Line 113: Line 113:
 You can Ctrl+Click on 14002080 in the above event (within DarkScript3) to see the event's definition. You will find this event, which is responsible for the Iron Virgin warp from Raya Lucaria to Volcano Manor: You can Ctrl+Click on 14002080 in the above event (within DarkScript3) to see the event's definition. You will find this event, which is responsible for the Iron Virgin warp from Raya Lucaria to Volcano Manor:
  
-<code>+<code JavaScript>
 $Event(14002080, Default, function() { $Event(14002080, Default, function() {
     EndIf(!PlayerIsInOwnWorld());     EndIf(!PlayerIsInOwnWorld());
Line 138: Line 138:
 Here is another event in Stormveil Castle (m10_00_00_00) which has two waits in it. It includes a RestartEvent, which is the one exception to events only running from top-to-bottom. When a restart command is encountered, execution stops there and jumps back up to the top. This basically resets the event to like it was when it was first initialized. Here is another event in Stormveil Castle (m10_00_00_00) which has two waits in it. It includes a RestartEvent, which is the one exception to events only running from top-to-bottom. When a restart command is encountered, execution stops there and jumps back up to the top. This basically resets the event to like it was when it was first initialized.
  
-<code>+<code JavaScript>
 $Event(10002460, Restart, function() { $Event(10002460, Restart, function() {
     WaitFor(TimeOfDayInRange(19, 0, 0, 5, 59, 0));     WaitFor(TimeOfDayInRange(19, 0, 0, 5, 59, 0));
Line 158: Line 158:
 The key thing to note here is ''if (<condition>) {'' statements. Unlike WaitFor, these don't wait for a condition to become true, but instead immediately evaluate it. The key thing to note here is ''if (<condition>) {'' statements. Unlike WaitFor, these don't wait for a condition to become true, but instead immediately evaluate it.
  
-<code>+<code JavaScript>
 $Event(18002860, Restart, function() { $Event(18002860, Restart, function() {
     if (EventFlag(18000850)) {     if (EventFlag(18000850)) {
Line 197: Line 197:
 We'll get to the second case when we talk about parameters, but a quick example of the first use case is as follows. This event kills the player if they've been wearing a certain piece of armor for more than 30 seconds straight. It does this by waiting for one of two things: either 30 seconds have passed, or the armor is unequipped. It uses ''variable.Passed'' after the WaitFor to see which was the case, and reset the timer in the second case. We'll get to the second case when we talk about parameters, but a quick example of the first use case is as follows. This event kills the player if they've been wearing a certain piece of armor for more than 30 seconds straight. It does this by waiting for one of two things: either 30 seconds have passed, or the armor is unequipped. It uses ''variable.Passed'' after the WaitFor to see which was the case, and reset the timer in the second case.
  
-<code>+<code JavaScript>
 $Event(xxxxxxxx, Restart, function() { $Event(xxxxxxxx, Restart, function() {
     WaitFor(PlayerHasArmorEquipped(ArmorType.Legs, 1930300, -1));     WaitFor(PlayerHasArmorEquipped(ArmorType.Legs, 1930300, -1));
Line 233: Line 233:
  
 <hidden onHidden="Show event" onVisibile="Hide" > <hidden onHidden="Show event" onVisibile="Hide" >
-<code>+<code JavaScript>
 $Event(13002860, Restart, function() { $Event(13002860, Restart, function() {
     if (EventFlag(13000850)) {     if (EventFlag(13000850)) {
Line 290: Line 290:
  
 <hidden onHidden="Show event with comments" onVisibile="Hide" > <hidden onHidden="Show event with comments" onVisibile="Hide" >
-<code>+<code JavaScript>
 $Event(13002860, Restart, function() { $Event(13002860, Restart, function() {
     // This first block is executed only when the boss defeat flag is set. Otherwise, skip it.     // This first block is executed only when the boss defeat flag is set. Otherwise, skip it.
Line 427: Line 427:
  
 <hidden onHidden="Show event" onVisibile="Hide" > <hidden onHidden="Show event" onVisibile="Hide" >
-<code>+<code JavaScript>
 $Event(13002850, Restart, function() { $Event(13002850, Restart, function() {
     EndIf(EventFlag(13000850));     EndIf(EventFlag(13000850));
Line 448: Line 448:
  
 <hidden onHidden="Show event with comments" onVisibile="Hide" > <hidden onHidden="Show event with comments" onVisibile="Hide" >
-<code>+<code JavaScript>
 $Event(13002850, Restart, function() { $Event(13002850, Restart, function() {
     // If they're already defeated, there's nothing to do here, so stop the event.     // If they're already defeated, there's nothing to do here, so stop the event.
Line 501: Line 501:
  
 <hidden onHidden="Show event" onVisibile="Hide" > <hidden onHidden="Show event" onVisibile="Hide" >
-<code>+<code JavaScript>
 $Event(13002861, Restart, function() { $Event(13002861, Restart, function() {
     EndIf(EventFlag(13000850));     EndIf(EventFlag(13000850));
Line 511: Line 511:
  
 <hidden onHidden="Show event with comments" onVisibile="Hide" > <hidden onHidden="Show event with comments" onVisibile="Hide" >
-<code>+<code JavaScript>
 $Event(13002861, Restart, function() { $Event(13002861, Restart, function() {
     // As usual, ignore the rest if the boss is already defeated.     // As usual, ignore the rest if the boss is already defeated.
Line 536: Line 536:
 The next event starts out like this: The next event starts out like this:
  
-<code>+<code JavaScript>
 $Event(13002890, Restart, function(X0_4, X4_4, X8_4, X12_4) { $Event(13002890, Restart, function(X0_4, X4_4, X8_4, X12_4) {
     ...     ...
Line 544: Line 544:
 Note the extra X0_4, X4_4, and so on. Remember that all events must be initialized in order to be active. In this case, there is extra data passed in during initialization! If you Ctrl+Click on the event id here, you can hop to the initializations, and you'll see these two lines: Note the extra X0_4, X4_4, and so on. Remember that all events must be initialized in order to be active. In this case, there is extra data passed in during initialization! If you Ctrl+Click on the event id here, you can hop to the initializations, and you'll see these two lines:
  
-<code>+<code JavaScript>
 InitializeEvent(0, 13002890, 13002944, 13000851, 13000852, 15504); InitializeEvent(0, 13002890, 13002944, 13000851, 13000852, 15504);
 InitializeEvent(1, 13002890, 13002945, 13000852, 13000851, 15454); InitializeEvent(1, 13002890, 13002945, 13000852, 13000851, 15454);
Line 554: Line 554:
  
 <hidden onHidden="Show event" onVisibile="Hide" > <hidden onHidden="Show event" onVisibile="Hide" >
-<code>+<code JavaScript>
 $Event(13002890, Restart, function(X0_4, X4_4, X8_4, X12_4) { $Event(13002890, Restart, function(X0_4, X4_4, X8_4, X12_4) {
     EndIf(!PlayerIsInOwnWorld());     EndIf(!PlayerIsInOwnWorld());
Line 573: Line 573:
  
 <hidden onHidden="Show event with comments" onVisibile="Hide" > <hidden onHidden="Show event with comments" onVisibile="Hide" >
-<code>+<code JavaScript>
 // X0_4: The event flag to set indicating that X4_4 should be respawned // X0_4: The event flag to set indicating that X4_4 should be respawned
 // X4_4: Entity id of the godskin to respawn // X4_4: Entity id of the godskin to respawn
Line 621: Line 621:
 It respawns a godskin when it has been requested by the other alive godskin. It respawns a godskin when it has been requested by the other alive godskin.
  
-<code>+<code JavaScript>
 InitializeEvent(0, 13002891, 13002944, 13003851, 13000852, 15506, 13000851, 13002873); InitializeEvent(0, 13002891, 13002944, 13003851, 13000852, 15506, 13000851, 13002873);
 InitializeEvent(1, 13002891, 13002945, 13003852, 13000851, 15456, 13000852, 13002874); InitializeEvent(1, 13002891, 13002945, 13003852, 13000851, 15456, 13000852, 13002874);
Line 627: Line 627:
  
 <hidden onHidden="Show event" onVisibile="Hide" > <hidden onHidden="Show event" onVisibile="Hide" >
-<code>+<code JavaScript>
 $Event(13002891, Restart, function(X0_4, X4_4, X8_4, X12_4, X16_4, X20_4) { $Event(13002891, Restart, function(X0_4, X4_4, X8_4, X12_4, X16_4, X20_4) {
     EndIf(!PlayerIsInOwnWorld());     EndIf(!PlayerIsInOwnWorld());
Line 645: Line 645:
  
 <hidden onHidden="Show event with comments" onVisibile="Hide" > <hidden onHidden="Show event with comments" onVisibile="Hide" >
-<code>+<code JavaScript>
 // X0_4: The event flag to check indicating that X4_4 should be respawned // X0_4: The event flag to check indicating that X4_4 should be respawned
 // X4_4: Generator id (not entity id!) of the godskin to respawn // X4_4: Generator id (not entity id!) of the godskin to respawn
Line 702: Line 702:
  
 <hidden onHidden="Show event" onVisibile="Hide" > <hidden onHidden="Show event" onVisibile="Hide" >
-<code>+<code JavaScript>
 $Event(13002892, Restart, function() { $Event(13002892, Restart, function() {
     EndIf(!PlayerIsInOwnWorld());     EndIf(!PlayerIsInOwnWorld());
Line 734: Line 734:
  
 <hidden onHidden="Show event with comments" onVisibile="Hide" > <hidden onHidden="Show event with comments" onVisibile="Hide" >
-<code>+<code JavaScript>
 $Event(13002892, Restart, function() { $Event(13002892, Restart, function() {
     // Similar checks for host-only and for avoiding respawn after boss defeat.     // Similar checks for host-only and for avoiding respawn after boss defeat.
Line 801: Line 801:
 This event allows a host player to traverse a boss fog gate, and sets a flag once they do. Summoned players use a separate event 9005801 to traverse the fog gate after the host does, meaning when flag X12_4 here is set. They also run this event, but it has no effect for them. This event allows a host player to traverse a boss fog gate, and sets a flag once they do. Summoned players use a separate event 9005801 to traverse the fog gate after the host does, meaning when flag X12_4 here is set. They also run this event, but it has no effect for them.
  
-<code>+<code JavaScript>
 InitializeCommonEvent(0, 9005800, 13000850, 13001850, 13002850, 13002855, 13005850, 10000, 13000851, 13002851); InitializeCommonEvent(0, 9005800, 13000850, 13001850, 13002850, 13002855, 13005850, 10000, 13000851, 13002851);
 InitializeCommonEvent(0, 9005800, 13000850, 13001852, 13002852, 13002855, 13005850, 10000, 13000851, 13002851); InitializeCommonEvent(0, 9005800, 13000850, 13001852, 13002852, 13002855, 13005850, 10000, 13000851, 13002851);
Line 808: Line 808:
  
 <hidden onHidden="Show event" onVisibile="Hide" > <hidden onHidden="Show event" onVisibile="Hide" >
-<code>+<code JavaScript>
 $Event(9005800, Restart, function(X0_4, X4_4, X8_4, X12_4, X16_4, X20_4, X24_4, X28_4) { $Event(9005800, Restart, function(X0_4, X4_4, X8_4, X12_4, X16_4, X20_4, X24_4, X28_4) {
     if (!EventFlag(X0_4)) {     if (!EventFlag(X0_4)) {
Line 871: Line 871:
  
 <hidden onHidden="Show event with comments" onVisibile="Hide" > <hidden onHidden="Show event with comments" onVisibile="Hide" >
-<code>+<code JavaScript>
 // X0_4: Boss defeat event flag // X0_4: Boss defeat event flag
 // X4_4: Entity id for the fog gate asset // X4_4: Entity id for the fog gate asset
Line 1022: Line 1022:
 So the above event allows you pass through a solid wall using RotateCharacter, which forces an animation which ignores walls. It assumes the fog gate is blocking your way, but event 9005811 is the event that's responsible for actually making the fog gate appear and disappear. It has 5 initializations in m13_00_00_00, one for each fog gate, including those on the upper level of the Dragon Temple: So the above event allows you pass through a solid wall using RotateCharacter, which forces an animation which ignores walls. It assumes the fog gate is blocking your way, but event 9005811 is the event that's responsible for actually making the fog gate appear and disappear. It has 5 initializations in m13_00_00_00, one for each fog gate, including those on the upper level of the Dragon Temple:
  
-<code>+<code JavaScript>
 InitializeCommonEvent(0, 9005811, 13000850, 13001850, 5, 13000851); InitializeCommonEvent(0, 9005811, 13000850, 13001850, 5, 13000851);
 InitializeCommonEvent(0, 9005811, 13000850, 13001852, 5, 13000851); InitializeCommonEvent(0, 9005811, 13000850, 13001852, 5, 13000851);
Line 1032: Line 1032:
 This event controls when the fog gate shows up and disappears using the ChangeAssetEnableState and CreateObjectfollowingSFX commands. I won't explain the details of the event here, as it behaves in a bunch of different complicated ways depending on whether you're a host, a summon, or the invader. Still, the arguments are as follows: This event controls when the fog gate shows up and disappears using the ChangeAssetEnableState and CreateObjectfollowingSFX commands. I won't explain the details of the event here, as it behaves in a bunch of different complicated ways depending on whether you're a host, a summon, or the invader. Still, the arguments are as follows:
  
-<code>+<code JavaScript>
 // X0_4: The boss defeat flag. When this is on, the fog gate does not show up. // X0_4: The boss defeat flag. When this is on, the fog gate does not show up.
 // X4_4: Entity id for the fog gate asset // X4_4: Entity id for the fog gate asset
Line 1055: Line 1055:
 Here is an example initialization in m14_00_00_00 (Raya Lucaria), which applies to the scholar in the middle of the Cuckoo Church: Here is an example initialization in m14_00_00_00 (Raya Lucaria), which applies to the scholar in the middle of the Cuckoo Church:
  
-<code>+<code JavaScript>
 InitializeCommonEvent(0, 90005200, 14000210, 30000, 20000, 14002451, 1065353216, 0, 0, 0, 0); InitializeCommonEvent(0, 90005200, 14000210, 30000, 20000, 14002451, 1065353216, 0, 0, 0, 0);
 </code> </code>
  
 Here is another initialization in m30_04_00_00 (Murkwater Catacombs), which applies to an imp waiting to drop down from the ceiling when you approach: Here is another initialization in m30_04_00_00 (Murkwater Catacombs), which applies to an imp waiting to drop down from the ceiling when you approach:
-<code>+<code JavaScript>
 InitializeCommonEvent(0, 90005200, 30040200, 30001, 20001, 30042200, 0, 1, 0, 0, 0); InitializeCommonEvent(0, 90005200, 30040200, 30001, 20001, 30042200, 0, 1, 0, 0, 0);
 </code> </code>
Line 1067: Line 1067:
  
 <hidden onHidden="Show event" onVisibile="Hide" > <hidden onHidden="Show event" onVisibile="Hide" >
-<code>+<code JavaScript>
 $Event(90005200, Restart, function(X0_4, X4_4, X8_4, X12_4, X16_4, X20_4, X24_4, X28_4, X32_4) { $Event(90005200, Restart, function(X0_4, X4_4, X8_4, X12_4, X16_4, X20_4, X24_4, X28_4, X32_4) {
     EndIf(SpecialStandbyEndedFlag(X0_4));     EndIf(SpecialStandbyEndedFlag(X0_4));
Line 1155: Line 1155:
  
 <hidden onHidden="Show event with comments" onVisibile="Hide" > <hidden onHidden="Show event with comments" onVisibile="Hide" >
-<code>+<code JavaScript>
 // X0_4: Entity id of the enemy // X0_4: Entity id of the enemy
 // X4_4: Animation id which loops while it's waiting for the trigger // X4_4: Animation id which loops while it's waiting for the trigger
Line 1324: Line 1324:
 ==== Map ids ==== ==== Map ids ====
   * Where it's located: Maps are how most of the game world is split up, so finding a map id is critical to being able to search for something in a particular MSB (in ''map/mapstudio'') or event script (in ''event'').   * Where it's located: Maps are how most of the game world is split up, so finding a map id is critical to being able to search for something in a particular MSB (in ''map/mapstudio'') or event script (in ''event'').
-  * How to browse it: Use [[er-refmat:elden-ring-map-list | Elden Ring Map List]] to find commonly used maps, or use the lists provided in various editors. Other dumps like Item Analysis also contain map references to everything.+  * How to browse it: Use [[er-refmat:map-name-list | Elden Ring Map List]] to find commonly used maps, or use the lists provided in various editors. Other dumps like Item Analysis also contain map references to everything.
   * How to use it: Open the files in a map/event editor!   * How to use it: Open the files in a map/event editor!
   * Example: You want to view/edit something in Roundtable Hold. Find that it's m11_10_00_00 in the map list, and open those files in DarkScript3/DSMapStudio.   * Example: You want to view/edit something in Roundtable Hold. Find that it's m11_10_00_00 in the map list, and open those files in DarkScript3/DSMapStudio.
Line 1332: Line 1332:
   * How to browse it: Use [[https://docs.google.com/spreadsheets/d/1eZ9FWlu1MDLnR2J0z0ld6T9FFZssOCjM1tS-Vvx0Yr0/edit#gid=0 | Elden Ring CHR ID]] reference to identify the model you're looking for.   * How to browse it: Use [[https://docs.google.com/spreadsheets/d/1eZ9FWlu1MDLnR2J0z0ld6T9FFZssOCjM1tS-Vvx0Yr0/edit#gid=0 | Elden Ring CHR ID]] reference to identify the model you're looking for.
   * How to use it: In addition to or instead of the above reference, you can also use the [[https://soulsmods.github.io/data/er/enemies.html | Enemy Locations Dump]] to find specific maps to open, then open the relevant maps in DSMapStudio. To find out how the enemy is scripted, use its EntityID.   * How to use it: In addition to or instead of the above reference, you can also use the [[https://soulsmods.github.io/data/er/enemies.html | Enemy Locations Dump]] to find specific maps to open, then open the relevant maps in DSMapStudio. To find out how the enemy is scripted, use its EntityID.
-  * Example: You want to find Radahn. Use above references to see that he's c4730, and that his full name is m60_52_38_00-c4730_9002, but he's located in map m60_13_09_02. The different ids are because his physical location is in the 52_38 small tile, but he can travel far away from that, so his data is in the 13_09 big tile instead. See the [[er-refmat:elden-ring-map-list | map reference]] for details.+  * Example: You want to find Radahn. Use above references to see that he's c4730, and that his full name is m60_52_38_00-c4730_9002, but he's located in map m60_13_09_02. The different ids are because his physical location is in the 52_38 small tile, but he can travel far away from that, so his data is in the 13_09 big tile instead. See the [[er-refmat:map-name-list | map reference]] for details.
  
 ==== Entity ids ==== ==== Entity ids ====
tutorial/intro-to-elden-ring-emevd.1740497039.txt.gz · Last modified: by admin