Difference between revisions of "Loading Level Resources With Lua"

From Journey Modding Wiki
Jump to navigation Jump to search
(Initial info on loading alternate resource files)
Line 2: Line 2:
[[Category:Level Resources]]
[[Category:Level Resources]]
This is what has been found so far to add and/or modify level resource objects using a Lua script, along with brief descriptions of what these objects do.
This is what has been found so far to add and/or modify level resource objects using a Lua script, along with brief descriptions of what these objects do.
==Loading Resource Files==
==Loading Resource Files==
 
To make different sets of resources load at the start of a level, one way is to run code that alters the resTbl table used within the QueueResourcesForLoad() function. This function is initially defined in plain text in Journey.exe . (Research on this and the other functions in that ".lua" will probably reveal how to load completely customized resources from outside the .exe)
To make different sets of resources load at the start of a level, one way is to run code that alters the resTbl table used within the QueueResourcesForLoad() function. This function is initially defined in plain text in Journey.exe .


For example, if you inject this script, then whenever you define swapLevel as an internal level name ("Graveyard","Barrens",etc) the next level's resources will all be replaced with the ones from Level_''swapLevel''.
For example, if you inject this script, then whenever you define swapLevel as an internal level name ("Graveyard","Barrens",etc) the next level's resources will all be replaced with the ones from Level_''swapLevel''.
  swapLevel = ""
  swapLevel = ""
   
   
Line 51: Line 48:
  end
  end
  end
  end
"Script" resources are .lua's embedded in Journey.exe, but all other resources are loaded from the game folders.
"Script" resources are .lua's embedded in Journey.exe, but all other resources are loaded from the game folders.


Line 59: Line 55:


Here is the format of resTbl as it gets passed to QueueResourcesForLoad:
Here is the format of resTbl as it gets passed to QueueResourcesForLoad:
  {  
  {  
  {
  {
Line 149: Line 144:
  }  
  }  
  }
  }
==Adding/Modifying Individual Resources==
==Adding/Modifying Individual Resources==
'''Important things to remember'''
'''Important things to remember'''


Line 178: Line 171:
#Environment Nodes (apparently gets loaded twice)
#Environment Nodes (apparently gets loaded twice)
*Load order not yet known for Particle Emitters, Sound Emitters, Terrain Data, the SFX/Music Banks, or the DuneColorShadow texture.
*Load order not yet known for Particle Emitters, Sound Emitters, Terrain Data, the SFX/Music Banks, or the DuneColorShadow texture.
===Triggers===
===Triggers===
Triggers are various types of events, including events that activate other events. They control basically everything that ever "happens" in a level, apart from basic player actions.
Triggers are various types of events, including events that activate other events. They control basically everything that ever "happens" in a level, apart from basic player actions.
The functions in this category are mainly from EventSetter.lua .
The functions in this category are mainly from EventSetter.lua .
====Modifying Triggers====
====Modifying Triggers====
To modify existing Triggers, find them in TriggerInstances.lua, define them in the TriggerInstances table with the same format used in that lua, then run the ResolveTriggerNames function.
To modify existing Triggers, find them in TriggerInstances.lua, define them in the TriggerInstances table with the same format used in that lua, then run the ResolveTriggerNames function.
Code "template":
Code "template":
Line 204: Line 193:


With this method by itself, if you change the trigger type to a different one than the original, it seems to (sometimes?) not work due to it expecting different data types; it's not yet known how to completely change the trigger type. You might be able to at least change trigger types if the Vars would be the same data types/names - for example if two trigger types both use { var1 = float, var2 = string, var3 = a Clump of Hulls, var4 = boolean } you could possibly switch from one type to the other and change the Var values and it would work; not tested yet.
With this method by itself, if you change the trigger type to a different one than the original, it seems to (sometimes?) not work due to it expecting different data types; it's not yet known how to completely change the trigger type. You might be able to at least change trigger types if the Vars would be the same data types/names - for example if two trigger types both use { var1 = float, var2 = string, var3 = a Clump of Hulls, var4 = boolean } you could possibly switch from one type to the other and change the Var values and it would work; not tested yet.
====Creating new Triggers====
====Creating new Triggers====
You can temporarily create new triggers basically the same way, by adding the CreateTriggers function:
You can temporarily create new triggers basically the same way, by adding the CreateTriggers function:


<code>
<code>
TriggerInstances = { ObjectType = "TriggerInstances", { ObjectName = "NewTrigger", GardenerName = "NewTrigger_G(this name doesn't matter?)", Type = "whatever type you want", Shortcut = "", Vars = { whatever vars that type of trigger uses } }, {same for more new triggers} }
TriggerInstances = { ObjectType = "TriggerInstances", { ObjectName = "NewTrigger", GardenerName = "NewTrigger_G(this name doesn't matter?)", Type = "whatever type you want", Shortcut = "", Vars = { whatever vars that type of trigger uses } }, {same for more new triggers} }
CreateTriggers( game:eventBarn() )
CreateTriggers( game:eventBarn() )
ResolveTriggerNames( game:eventBarn(), game:clumpBarn() )
ResolveTriggerNames( game:eventBarn(), game:clumpBarn() )
</code>
</code>
Line 221: Line 206:
<code>
<code>
TriggerInstances = { ObjectType = "TriggerInstances", { ObjectName = "TextTrigger", GardenerName = "TextTrigger_G", Type = "DisplayText", Shortcut = "", Vars = {text = "Check this out, I can do a backflip", x = 0, y = 0, duration = 3, fadeTime = 0.5 } }, { ObjectName = "AnimTrigger", GardenerName = "AnimTrigger_G", Type = "PlayDudeAnim", Shortcut = "", Vars = { animName = "HitBodyMAir", clearAnimQueue = true, useLocal = true} } }
TriggerInstances = { ObjectType = "TriggerInstances", { ObjectName = "TextTrigger", GardenerName = "TextTrigger_G", Type = "DisplayText", Shortcut = "", Vars = {text = "Check this out, I can do a backflip", x = 0, y = 0, duration = 3, fadeTime = 0.5 } }, { ObjectName = "AnimTrigger", GardenerName = "AnimTrigger_G", Type = "PlayDudeAnim", Shortcut = "", Vars = { animName = "HitBodyMAir", clearAnimQueue = true, useLocal = true} } }
CreateTriggers( game:eventBarn() )
CreateTriggers( game:eventBarn() )


Line 229: Line 213:


ActivateTriggerByName( "TextTrigger" )
ActivateTriggerByName( "TextTrigger" )
ActivateTriggerByName( "AnimTrigger" )
ActivateTriggerByName( "AnimTrigger" )
</code>
</code>


With this method alone, new Trigger ObjectNames are added to the Names table for the rest of the level, but the actual data defining what the Trigger should do is '''not''' added to the section of memory where normal Trigger data is stored. Wherever the data is, the Trigger does exist at least until the end of the frame (maybe a few frames) and it can be activated for that short time, but then the data is overwritten and your game will crash if it tries to activate the nonexistent Trigger.  
With this method alone, new Trigger ObjectNames are added to the Names table for the rest of the level, but the actual data defining what the Trigger should do is '''not''' added to the section of memory where normal Trigger data is stored. Wherever the data is, the Trigger does exist at least until the end of the frame (maybe a few frames) and it can be activated for that short time, but then the data is overwritten and your game will crash if it tries to activate the nonexistent Trigger.  


It is not yet known how to make the actual Trigger persist for the entire level. Until we figure that out, one solution to create new "persistent" Triggers might be to have this code running every frame - though you'd have to make it not run while the normal level load/unload stuff is happening. Haven't tested that yet.
It is not yet known how to make the actual Trigger persist for the entire level. Until we figure that out, one solution to create new "persistent" Triggers might be to have this code running every frame - though you'd have to make it not run while the normal level load/unload stuff is happening. Haven't tested that yet.


Once you have created a new Trigger, you only need to use ResolveTriggerNames() to re-create it after its data is deleted. However, there does not appear to be any harm in using CreateTriggers() for it again, it just won't do anything - the Names table will stay the same.
Once you have created a new Trigger, you only need to use ResolveTriggerNames() to re-create it after its data is deleted. However, there does not appear to be any harm in using CreateTriggers() for it again, it just won't do anything - the Names table will stay the same.
====Spawning instant one-time Triggers====
====Spawning instant one-time Triggers====
If you just want to make a new Trigger-like event that instantly happens and then disappears ( instead of being a "normal" Trigger that relies on other Triggers to make it happen and can be reused) you can use the SpawnEvent function from PWeb3.lua. It seems that SpawnEvent is not a global function, so you have to re-define it to use it globally.
If you just want to make a new Trigger-like event that instantly happens and then disappears ( instead of being a "normal" Trigger that relies on other Triggers to make it happen and can be reused) you can use the SpawnEvent function from PWeb3.lua. It seems that SpawnEvent is not a global function, so you have to re-define it to use it globally.


Line 246: Line 227:
SpawnEvent { TriggerType = { var1 = x, var2 = y, var3 = z... basically the same kind of Vars table that would be used with that Trigger type }
SpawnEvent { TriggerType = { var1 = x, var2 = y, var3 = z... basically the same kind of Vars table that would be used with that Trigger type }
</code>
</code>
===Clumps===
===Clumps===
Clumps are groups of multiple objects, used so that a single Trigger can reference and/or manipulate everything in the Clump at once.
Clumps are groups of multiple objects, used so that a single Trigger can reference and/or manipulate everything in the Clump at once.
All Objects in a clump might need to be the same type of object, like they must be all Triggers, or all Hulls, or all Meshes, etc - not tested yet.
All Objects in a clump might need to be the same type of object, like they must be all Triggers, or all Hulls, or all Meshes, etc - not tested yet.
Line 254: Line 233:


The functions in this category are from ClumpLoader.lua.
The functions in this category are from ClumpLoader.lua.
====Creating new Clumps====
====Creating new Clumps====
To create new Clumps, define the ClumpInstances table with the same format used in ClumpInstances.lua (but concatenated, not with line breaks). Then run the LoadClumps function, then the SetClumpMembers function.
To create new Clumps, define the ClumpInstances table with the same format used in ClumpInstances.lua (but concatenated, not with line breaks). Then run the LoadClumps function, then the SetClumpMembers function.
Code "template":
Code "template":


<code>
<code>
ClumpInstances = { ObjectType = "ClumpInstances", { ObjectName = "NewClumpName", GardenerName = "NewClumpName_G(this name doesn't matter?)", Objects = { "some object's ObjectName", "another object's ObjectName", "and another, etc" } }, { same thing for another new Clump }, {another, etc} }
ClumpInstances = { ObjectType = "ClumpInstances", { ObjectName = "NewClumpName", GardenerName = "NewClumpName_G(this name doesn't matter?)", Objects = { "some object's ObjectName", "another object's ObjectName", "and another, etc" } }, { same thing for another new Clump }, {another, etc} }  
 
LoadClumps( game:clumpBarn() )
LoadClumps( game:clumpBarn() )
 
SetClumpMembers( game:clumpBarn() )
SetClumpMembers( game:clumpBarn() )
</code>
</code>
Line 271: Line 246:


<code>
<code>
ClumpInstances = { ObjectType = "ClumpInstances", { ObjectName = "NewGameStandUpEvents", GardenerName = "NewGameStandUpEvents_G", Objects = { "70c1306a4a41c624a9ff123f344ef6ad", "d951daedc9f58a681efd39a8875f32c6", "1a737db99c2f3a7733e5c6426211d0e8", "daa0dd930abe88d5d241e02752c636d4", "f26b85b929a3e48edf43a5ec1f076c91", "65eef6f7bcbfcfa07b32c34d07e53132", "796c354ac61bd62ac987778b9fa96c4c", "5abf9a5b00e2e29c36f7871028e9c1e5", "e4fada7df843f162ebb2ee56a0e9f40f" } } }
ClumpInstances = { ObjectType = "ClumpInstances", { ObjectName = "NewGameStandUpEvents", GardenerName = "NewGameStandUpEvents_G", Objects = { "70c1306a4a41c624a9ff123f344ef6ad", "d951daedc9f58a681efd39a8875f32c6", "1a737db99c2f3a7733e5c6426211d0e8", "daa0dd930abe88d5d241e02752c636d4", "f26b85b929a3e48edf43a5ec1f076c91", "65eef6f7bcbfcfa07b32c34d07e53132", "796c354ac61bd62ac987778b9fa96c4c", "5abf9a5b00e2e29c36f7871028e9c1e5", "e4fada7df843f162ebb2ee56a0e9f40f" } } }  
 
LoadClumps( game:clumpBarn() )
LoadClumps( game:clumpBarn() )
 
SetClumpMembers( game:clumpBarn() )
SetClumpMembers( game:clumpBarn() )
</code>
</code>
Line 282: Line 255:
<code>
<code>
SpawnEvent {EventAfterDelay = {delay = 0.01, effects = Names["NewGameStandUpEvents"]}}
SpawnEvent {EventAfterDelay = {delay = 0.01, effects = Names["NewGameStandUpEvents"]}}
</code></blockquote>


Demonstration of the Clump it's duplicating:
Demonstration of the Clump it's duplicating:
Line 289: Line 261:
SpawnEvent {EventAfterDelay = {delay = 0.01, effects = Names["e7fa07adac45a2fb9a369edcf5964ec7"]}}
SpawnEvent {EventAfterDelay = {delay = 0.01, effects = Names["e7fa07adac45a2fb9a369edcf5964ec7"]}}
</code>
</code>
====Modifying Clumps====
====Modifying Clumps====
To modify existing Clumps, get them from ClumpInstances.lua and do basically the same code, but without the LoadClumps function:
To modify existing Clumps, get them from ClumpInstances.lua and do basically the same code, but without the LoadClumps function:


Line 303: Line 273:


<code>
<code>
ClumpInstances = { ObjectType = "ClumpInstances", { ObjectName = "e7fa07adac45a2fb9a369edcf5964ec7", GardenerName = "|Clump849|Clump849_GARDENER", Objects = { "27b916d7b9ebb26d91432e16257cba5d", "d951daedc9f58a681efd39a8875f32c6", "1a737db99c2f3a7733e5c6426211d0e8", "daa0dd930abe88d5d241e02752c636d4", "f26b85b929a3e48edf43a5ec1f076c91", "65eef6f7bcbfcfa07b32c34d07e53132", "796c354ac61bd62ac987778b9fa96c4c", "5abf9a5b00e2e29c36f7871028e9c1e5", "e4fada7df843f162ebb2ee56a0e9f40f", "ed489eebe96121c9de7ccce1d25d6ced" } } }  
ClumpInstances = { ObjectType = "ClumpInstances", { ObjectName = "e7fa07adac45a2fb9a369edcf5964ec7", GardenerName = "|Clump849|Clump849_GARDENER", Objects = { "27b916d7b9ebb26d91432e16257cba5d", "d951daedc9f58a681efd39a8875f32c6", "1a737db99c2f3a7733e5c6426211d0e8", "daa0dd930abe88d5d241e02752c636d4", "f26b85b929a3e48edf43a5ec1f076c91", "65eef6f7bcbfcfa07b32c34d07e53132", "796c354ac61bd62ac987778b9fa96c4c", "5abf9a5b00e2e29c36f7871028e9c1e5", "e4fada7df843f162ebb2ee56a0e9f40f", "ed489eebe96121c9de7ccce1d25d6ced" } } }  


SetClumpMembers( game:clumpBarn() )
SetClumpMembers( game:clumpBarn() )
Line 313: Line 283:
SpawnEvent {EventAfterDelay = {delay = 0.01, effects = Names["e7fa07adac45a2fb9a369edcf5964ec7"]}}
SpawnEvent {EventAfterDelay = {delay = 0.01, effects = Names["e7fa07adac45a2fb9a369edcf5964ec7"]}}
</code>
</code>
===Timelines===
===Timelines===
Timelines are timed sequences of Trigger activations, used to organize cutscenes and other events where a bunch of Triggers get activated in a row.  
Timelines are timed sequences of Trigger activations, used to organize cutscenes and other events where a bunch of Triggers get activated in a row.  


Line 325: Line 293:
LoadTimelines( game:timelineBarn() )
LoadTimelines( game:timelineBarn() )
</code>
</code>
===Creatures / Dynamic Nodes===
===Creatures / Dynamic Nodes===
 
Creatures are dynamically moving and interacting objects, other than the player avatars and the tiny flying cloths (Strands).
Creatures are dynamically moving and interacting objects, other than the player avatars and the tiny flying cloths (Strands).  
Although code comments say that Creatures are just a Type of Dynamic Node, it appears that ''all'' Dynamic Nodes have their Type set as "Creatures" anyway.
Although code comments say that Creatures are just a Type of Dynamic Node, it appears that ''all'' Dynamic Nodes have their Type set as "Creatures" anyway.  
Creatures include Fish/Carpets, Guardians/War Machines, Cloth Guardians/Whales/Dragons, Jellyfish, Flow creatures, and Meteors/Comets/Shooting Stars.
Creatures include Fish/Carpets, Guardians/War Machines, Cloth Guardians/Whales/Dragons, Jellyfish, Flow creatures, and Meteors/Comets/Shooting Stars.


Line 336: Line 302:
<code>
<code>
DynamicNodeInstances = { construct based on the format in DynamicNodeInstances.lua }
DynamicNodeInstances = { construct based on the format in DynamicNodeInstances.lua }
CreateCreatures( game:creatureBarn() ) --only used when adding new creatures(?)
CreateCreatures( game:creatureBarn() ) --only used when adding new creatures(?)
ResolveCreatureNames( game:creatureBarn() )
ResolveCreatureNames( game:creatureBarn() )
</code>
</code>


It seems that Creatures/Dynamic Nodes might actually be Triggers, based on how TriggerInitialize() is also used in Creatures.lua - so new Creatures might have the same issue as Triggers, and might disappear from the Names table shortly after being created with this method.
It seems that Creatures/Dynamic Nodes might actually be Triggers, based on how TriggerInitialize() is also used in Creatures.lua - so new Creatures might have the same issue as Triggers, and might disappear from the Names table shortly after being created with this method.
===Particle Emitters===
===Particle Emitters===
Particle Emitters emit particles (sparkles, puffs of sand/dust, etc.)
Particle Emitters emit particles (sparkles, puffs of sand/dust, etc.)


Line 353: Line 315:
ParticleEmitterInstances = { construct based on the format in ParticleEmitterInstances.lua }
ParticleEmitterInstances = { construct based on the format in ParticleEmitterInstances.lua }


LoadEnvFXParticles( game:envFXBarn(), game:particleBarn() )  
LoadEnvFXParticles( game:envFXBarn(), game:particleBarn() )
</code>
</code>


Based on tests where logging code was put in LoadEnvFXParticles() and ParticleEmitterInitialize() and nothing was logged, it seems this script doesn't actually run at level start, so Particle Emitters might be added to Names table by another Lua script or by inaccessible C code. But maybe this function could still add/modify Particle Emitters anyway.
Based on tests where logging code was put in LoadEnvFXParticles() and ParticleEmitterInitialize() and nothing was logged, it seems this script doesn't actually run at level start, so Particle Emitters might be added to Names table by another Lua script or by inaccessible C code. But maybe this function could still add/modify Particle Emitters anyway.
===Sound Emitters===
===Sound Emitters===
Sound Emitters emit sounds. They can be stationary or attached to a moving object somehow. It seems that most looping music is played by Sound Emitters that are set to be audible at the same volume anywhere in the level.
Sound Emitters emit sounds. They can be stationary or attached to a moving object somehow. It seems that most looping music is played by Sound Emitters that are set to be audible at the same volume anywhere in the level.


Line 371: Line 331:


Based on tests where logging code was put in LoadSoundEmitters() and SoundEmitterInitialize() and nothing was logged, it seems this script doesn't actually run at level start, so Sound Emitters might be added to Names table by another Lua script or by inaccessible C code. But maybe this function could still add/modify Sound Emitters anyway.
Based on tests where logging code was put in LoadSoundEmitters() and SoundEmitterInitialize() and nothing was logged, it seems this script doesn't actually run at level start, so Sound Emitters might be added to Names table by another Lua script or by inaccessible C code. But maybe this function could still add/modify Sound Emitters anyway.
===Environment Nodes===
===Environment Nodes===
Environment/Env Nodes set the environmental effects within certain regions, such as lighting, sky color, shadows, fog, bloom, etc.
Environment/Env Nodes set the environmental effects within certain regions, such as lighting, sky color, shadows, fog, bloom, etc.
"IsZNode = true" nodes affect the region all along a certain space on the z-axis (usually toward/away from mountain), "= false" nodes affect a certain cube-shaped volume.
"IsZNode = true" nodes affect the region all along a certain space on the z-axis (usually toward/away from mountain), "= false" nodes affect a certain cube-shaped volume.


It has not yet been tested, but Env Nodes can probably be added and/or modified in a similar way to Triggers and Clumps, based on EnvNodeInstances.lua's and the functions in Environment.lua:
It has not yet been tested, but Env Nodes can probably be added and/or modified in a similar way to Triggers and Clumps, based on EnvNodeInstances.lua's and the functions in Environment.lua:
Line 382: Line 340:
EnvNodeInstances = { construct based on the format in EnvNodeInstances.lua }
EnvNodeInstances = { construct based on the format in EnvNodeInstances.lua }


LoadEnvNodes( game:environment() )  
LoadEnvNodes( game:environment() )
</code>
</code>


Line 388: Line 346:


LoadEnvNodes does ''not'' clear the EnvNodeInstances table automatically upon completion, and the code comments say this causes a crash, so might be a bad idea to do it yourself.
LoadEnvNodes does ''not'' clear the EnvNodeInstances table automatically upon completion, and the code comments say this causes a crash, so might be a bad idea to do it yourself.
===Markers===
===Markers===
Markers seem to be locations, or regions of space around locations, which objects can be aimed or moved towards/away from by certain Triggers. Also, Markers can be moved around or attached to moving objects by Triggers.
Markers seem to be locations, or regions of space around locations, which objects can be aimed or moved towards/away from by certain Triggers. Also, Markers can be moved around or attached to moving objects by Triggers.


Line 400: Line 356:
LoadMarkers( game:markerBarn() )
LoadMarkers( game:markerBarn() )
</code>
</code>
===Rails===
===Rails===
Rails seem to be paths that objects can move along.
Rails seem to be paths that objects can move along.


Line 413: Line 367:
</code>
</code>
( There doesn't seem to be a game:railBarn(), so maybe it's a different name in game:, or maybe it's used like RailBarn, RailBarn() or RailBarn(game). )
( There doesn't seem to be a game:railBarn(), so maybe it's a different name in game:, or maybe it's used like RailBarn, RailBarn() or RailBarn(game). )
===Camera Key Frames===
===Camera Key Frames===
Camera Key Frames seem to be the data for basically any location/angle/etc that the camera can get moved to, whenever the camera moves by itself - not just following behind the player, or being moved around by the player.
Camera Key Frames seem to be the data for basically any location/angle/etc that the camera can get moved to, whenever the camera moves by itself - not just following behind the player, or being moved around by the player.


Line 425: Line 377:
LoadCameraKeyFrames( game:cameraSystem() )
LoadCameraKeyFrames( game:cameraSystem() )
</code>
</code>
===Jets===
===Jets===
Jets seem to be the physics effect that pushes your avatar down when you are inside certain sandfalls, and maybe other effects. Could probably be used to push the avatar in other directions as well.
Jets seem to be the physics effect that pushes your avatar down when you are inside certain sandfalls, and maybe other effects. Could probably be used to push the avatar in other directions as well.


Line 438: Line 388:
</code>
</code>
( There doesn't seem to be a game:jetBarn(), so maybe it's a different name in game:, or maybe it's used like JetBarn, JetBarn() or JetBarn(game). )
( There doesn't seem to be a game:jetBarn(), so maybe it's a different name in game:, or maybe it's used like JetBarn, JetBarn() or JetBarn(game). )
===Signs===
===Signs===
Signs are scrolling text, used only in the credits. Could maybe be used for other scrolling text as well, although there seem to already be scrolling text Triggers that might be easier for that.
Signs are scrolling text, used only in the credits. Could maybe be used for other scrolling text as well, although there seem to already be scrolling text Triggers that might be easier for that.


Line 447: Line 395:
<code>
<code>
Credits = { construct based on the format in SignData.lua }
Credits = { construct based on the format in SignData.lua }
SignInstances = { construct based on the format in SignInstances.lua }
SignInstances = { construct based on the format in SignInstances.lua }
InitializeSignBarn( signBarn, signData, signInstances )
InitializeSignBarn( signBarn, signData, signInstances )
</code>
</code>
( There doesn't seem to be a game:signBarn(), so maybe it's a different name in game:, or maybe it's used like SignBarn, SignBarn() or SignBarn(game). Also, signData/signInstances don't seem to actually be referenced inside InitializeSignBarn, so they might not be required, but if required then maybe signData is Credits and signInstances is SignInstances. )
( There doesn't seem to be a game:signBarn(), so maybe it's a different name in game:, or maybe it's used like SignBarn, SignBarn() or SignBarn(game). Also, signData/signInstances don't seem to actually be referenced inside InitializeSignBarn, so they might not be required, but if required then maybe signData is Credits and signInstances is SignInstances. )
===TBD / Other===
===TBD / Other===
These are the remaining types of level resources. They can *maybe* be manipulated in real time with Lua code, but it's not yet known if/how it can be done.
These are the remaining types of level resources. They can *maybe* be manipulated in real time with Lua code, but it's not yet known if/how it can be done.
 
====Hulls====
====Hulls====  
 
Hulls are regions of space that never move(?), which are used as physical collision geometry and/or a way for Triggers to determine "if ''this thing'' is touching/inside ''this region'', do ''this event''".
Hulls are regions of space that never move(?), which are used as physical collision geometry and/or a way for Triggers to determine "if ''this thing'' is touching/inside ''this region'', do ''this event''".


You can change the Hulls loaded at start of level by editing HullInstances.lua in the folder for that level under Data/Scripts.
You can change the Hulls loaded at start of level by editing HullInstances.lua in the folder for that level under Data/Scripts.
====Decoration Meshes====
====Decoration Meshes====
Decoration Meshes are any visible object other than terrain, fog, particle effects, Strands(?), and the basic "naked" player avatar. By themselves, they are not physical objects, their collision data comes from physical Hulls put in the same place.
Decoration Meshes are any visible object other than terrain, fog, particle effects, Strands(?), and the basic "naked" player avatar. By themselves, they are not physical objects, their collision data comes from physical Hulls put in the same place.


You can change the Decoration Meshes loaded at start of level by editing DecorationMeshInstances.lua.bin in the folder for that level under Data/Scripts.
You can change the Decoration Meshes loaded at start of level by editing DecorationMeshInstances.lua.bin in the folder for that level under Data/Scripts.
====Sound Banks====
====Sound Banks====
SFX Banks contain sounds, and references/mixing data for longer sounds streamed from the audio files in Data/Sounds/Streams.
SFX Banks contain sounds, and references/mixing data for longer sounds streamed from the audio files in Data/Sounds/Streams.


Line 477: Line 415:


These are LevelSfx''Somewhere''-SFX.bnk and LevelMus''Somewhere''-MUS.bnk in the Data/Sounds folder; not much is known yet about how to edit these at all, other than renaming files to swap one level for another.
These are LevelSfx''Somewhere''-SFX.bnk and LevelMus''Somewhere''-MUS.bnk in the Data/Sounds folder; not much is known yet about how to edit these at all, other than renaming files to swap one level for another.
====TerrainData====
====TerrainData====
Terrain Data determines the terrain's Height Map and Land Color (the color of sand/snow added to the sides and/or top of some Decoration Meshes, and that collects on the player avatars). It also determines Mask Data which seems to be unused.
Terrain Data determines the terrain's Height Map and Land Color (the color of sand/snow added to the sides and/or top of some Decoration Meshes, and that collects on the player avatars). It also determines Mask Data which seems to be unused.


You can change what is loaded at start of level by editing the TerrainData.bin inside TerrainData.bin.gz in the Data/Terrain/Level_''Somewhere'' folder.  
You can change what is loaded at start of level by editing the TerrainData.bin inside TerrainData.bin.gz in the Data/Terrain/Level_''Somewhere'' folder.  
====DuneColorShadow====
====DuneColorShadow====
DuneColorShadow is a texture that determines the color of the terrain in a level, and which areas are in shadow.  
DuneColorShadow is a texture that determines the color of the terrain in a level, and which areas are in shadow.  


You can change what is loaded at the start of a level by editing/replacing the DuneColorShadow.dds texture inside the DuneColorShadow.dds.D3D11x64 file in the Data/Textures/Level_''Somewhere''/Bin folder.
You can change what is loaded at the start of a level by editing/replacing the DuneColorShadow.dds texture inside the DuneColorShadow.dds.D3D11x64 file in the Data/Textures/Level_''Somewhere''/Bin folder.

Revision as of 03:00, 10 June 2021

This is what has been found so far to add and/or modify level resource objects using a Lua script, along with brief descriptions of what these objects do.

Loading Resource Files

To make different sets of resources load at the start of a level, one way is to run code that alters the resTbl table used within the QueueResourcesForLoad() function. This function is initially defined in plain text in Journey.exe . (Research on this and the other functions in that ".lua" will probably reveal how to load completely customized resources from outside the .exe)

For example, if you inject this script, then whenever you define swapLevel as an internal level name ("Graveyard","Barrens",etc) the next level's resources will all be replaced with the ones from Level_swapLevel.

swapLevel = ""

function QueueResourcesForLoad( resManager, resTbl, alloc )
	DebugPrint( "Loading: Queuing resources for load." )
	
	--this if/then block is the modded code, the rest is original
	if swapLevel ~= "" then
		for i,v in ipairs (resTbl) do
			if v.class == "Script" then	v.source = "Level_"..swapLevel.."/"..v.name..".lua"
			elseif v.class == "Binary" then	v.source = "Scripts/Level_"..swapLevel.."/"..v.name..".lua"
			elseif v.name == "LvlMus" then v.source = swapLevel.."_MUS.bnk"
			elseif v.name == "LvlSfx" then v.source = swapLevel.."_SFX.bnk"
			elseif v.class == "Texture" then v.source = "Level_"..swapLevel.."/DuneColorShadow.dds"
			elseif v.class == "TerrainData" then 
				v.outDir = "Level_"..swapLevel.."/"
				v.srcGround = "Level_"..swapLevel.."/LandColor.dds"
				v.srcHeight = "Level_"..swapLevel.."/Heightmap.dds"
				v.srcMask = "Level_"..swapLevel.."/MaskData.dds"
			end
		end
		swapLevel = ""
	end	
	
	for i,resCons in ipairs( resTbl ) do
		DebugPrint( "Loading: Queuing "..resCons.name..", a "..resCons.class.."." )

		-- Allocate the resource object.
		local metatbl = _G[ resCons.class ]
		local res = metatbl.new( alloc )

		-- Initialize some values in the resource object based on the
		-- construction table.
		resCons.class = nil
		for varName,varValue in pairs( resCons ) do
			local varAccessor = res.__vars[ varName ]
			if varAccessor then varAccessor( res, varValue ) end
		end

		-- Tell the C++ end this resource is ready to be loaded.
		resManager:AddToLoadList( res )
	end
end

"Script" resources are .lua's embedded in Journey.exe, but all other resources are loaded from the game folders.

Keep in mind that some resource types reference each other in a way that will cause the game to crash if something is wrong/missing, so some resource sets can only be safely replaced if you properly replace certain others as well.

If you try to load a set of resources that doesn't exist, some resource types will allow it and just load none of that resource, but other types will make the game crash.

Here is the format of resTbl as it gets passed to QueueResourcesForLoad:

{ 
{
class = "ScreamBank",
name = "LvlSfx",
source = "Graveyard-SFX.bank"
}, {
class = "ScreamBank",
name = "LvlMus",
source = "Graveyard-MUS.bank"
}, {
class = "TerrainData",
name = "TerrainData",
outDir = "Level_Graveyard/",
srcGround = "Level_Graveyard/LandColor.dds",
srcHeight = "Level_Graveyard/Heightmap.dds",
srcMask = "Level_Graveyard/MaskData.dds"
}, {
class = "Binary",
name = "HullInstances",
source = "Scripts/Level_Graveyard/HullInstances.lua",
type = "Hull"
}, {
class = "Binary",
name = "DecorationMeshInstances",
source = "Scripts/Level_Graveyard/DecorationMeshInstances.lua",
type = "Decoration"
}, {
class = "Script",
name = "EnvNodeInstances",
source = "Level_Graveyard/EnvNodeInstances.lua"
}, {
class = "Script",
name = "MarkerInstances",
source = "Level_Graveyard/MarkerInstances.lua"
}, {
class = "Script",
name = "ParticleEmitterInstances",
source = "Level_Graveyard/ParticleEmitterInstances.lua"
}, {
class = "Script",
name = "SoundEmitterInstances",
source = "Level_Graveyard/SoundEmitterInstances.lua"
}, {
class = "Script",
name = "SignInstances",
source = "Level_Graveyard/SignInstances.lua"
}, {
class = "Script",
name = "SignData",
source = "Level_Graveyard/SignData.lua"
}, {
class = "Script",
name = "JetInstances",
source = "Level_Graveyard/JetInstances.lua"
}, {
class = "Script",
name = "DynamicNodeInstances",
source = "Level_Graveyard/DynamicNodeInstances.lua"
}, {
class = "Script",
name = "CameraKeyFrameInstances",
source = "Level_Graveyard/CameraKeyFrameInstances.lua"
}, {
class = "Script",
name = "StartLocation",
source = "Level_Graveyard/StartLocation.lua"
}, {
class = "Script",
name = "TimelineInstances",
source = "Level_Graveyard/TimelineInstances.lua"
}, {
class = "Script",
name = "TriggerInstances",
source = "Level_Graveyard/TriggerInstances.lua"
}, {
class = "Script",
name = "ClumpInstances",
source = "Level_Graveyard/ClumpInstances.lua"
}, {
class = "Script",
name = "RailInstances",
source = "Level_Graveyard/RailInstances.lua"
}, {
class = "Texture",
gammaRemap = true,
name = "DuneColorShadow",
source = "Level_Graveyard/DuneColorShadow.dds"
} 
}

Adding/Modifying Individual Resources

Important things to remember

These methods do not actually edit how the game reads an embedded Level_Somewhere/SomethingInstances.lua , so these changes only last until the level is unloaded. When a level unloads, it seems the Names table is completely cleared, including any new objects you created that weren't in the original SomethingInstances.lua.

If you reference an ObjectName that exists in the Names table, but the associated object no longer exists in memory or is corrupted, your game will most likely crash.

Although the SomethingInstances.lua's all have each part of their table on its own line, it seems like the entire table needs to be on the same line to be defined properly in Lua.

The SomethingInstances/etc tables that you construct to load resources into the Names table are usually (but not always) cleared automatically after the functions run, to free up memory.

From tests done by inserting logging code into the resource-loading functions, this appears to always be the order that resources get loaded into the Names table, in all levels. You should probably use the same order when adding/modifying different types of resources at the same time, it is not yet known if doing them in a different order can break anything.

  1. Camera Key Frames
  2. Hulls
  3. Environment Nodes
  4. Markers
  5. Jets
  6. Signs
  7. Timelines
  8. Rails
  9. Clumps
  10. Creatures
  11. Triggers
  12. Decoration Mesh Instances
  13. PrintNameTableSize() from Helpers.lua is called - might be a safe and good function to edit to add/modify all the resources you want ready when the level actually "starts", not tested yet
  14. Environment Nodes (apparently gets loaded twice)
  • Load order not yet known for Particle Emitters, Sound Emitters, Terrain Data, the SFX/Music Banks, or the DuneColorShadow texture.

Triggers

Triggers are various types of events, including events that activate other events. They control basically everything that ever "happens" in a level, apart from basic player actions. The functions in this category are mainly from EventSetter.lua .

Modifying Triggers

To modify existing Triggers, find them in TriggerInstances.lua, define them in the TriggerInstances table with the same format used in that lua, then run the ResolveTriggerNames function. Code "template":

TriggerInstances = { ObjectType = "TriggerInstances", {ObjectName = "the original trigger's ObjectName", GardenerName = "the original GardenerName", Type = "the original Type", Shortcut = "", Vars = { customize the vars however you want } }, { all that same stuff for another trigger }, {and another, etc} }

ResolveTriggerNames( game:eventBarn(), game:clumpBarn() )

Code example - using this in CS will change the music cue when you collect the first symbol at the broken statue, so you hear the start-game cutscene music instead:

TriggerInstances = { ObjectType = "TriggerInstances", {ObjectName = "ed489eebe96121c9de7ccce1d25d6ced", GardenerName = "|PlaySFXTrigger270|PlaySFXTrigger270_G", Type = "PlaySFXTrigger", Shortcut = "", Vars = { bankName = "LvlMus", soundName = "M_0m1", vol = 1} } }

ResolveTriggerNames( game:eventBarn(), game:clumpBarn() )

With this method by itself, if you change the trigger type to a different one than the original, it seems to (sometimes?) not work due to it expecting different data types; it's not yet known how to completely change the trigger type. You might be able to at least change trigger types if the Vars would be the same data types/names - for example if two trigger types both use { var1 = float, var2 = string, var3 = a Clump of Hulls, var4 = boolean } you could possibly switch from one type to the other and change the Var values and it would work; not tested yet.

Creating new Triggers

You can temporarily create new triggers basically the same way, by adding the CreateTriggers function:

TriggerInstances = { ObjectType = "TriggerInstances", { ObjectName = "NewTrigger", GardenerName = "NewTrigger_G(this name doesn't matter?)", Type = "whatever type you want", Shortcut = "", Vars = { whatever vars that type of trigger uses } }, {same for more new triggers} } CreateTriggers( game:eventBarn() ) ResolveTriggerNames( game:eventBarn(), game:clumpBarn() )

For example:

TriggerInstances = { ObjectType = "TriggerInstances", { ObjectName = "TextTrigger", GardenerName = "TextTrigger_G", Type = "DisplayText", Shortcut = "", Vars = {text = "Check this out, I can do a backflip", x = 0, y = 0, duration = 3, fadeTime = 0.5 } }, { ObjectName = "AnimTrigger", GardenerName = "AnimTrigger_G", Type = "PlayDudeAnim", Shortcut = "", Vars = { animName = "HitBodyMAir", clearAnimQueue = true, useLocal = true} } } CreateTriggers( game:eventBarn() )

ResolveTriggerNames( game:eventBarn(), game:clumpBarn() )

-- these activations aren't necessary to load the triggers, just done on the same frame to demonstrate that they work; see below for why it's done on same frame

ActivateTriggerByName( "TextTrigger" ) ActivateTriggerByName( "AnimTrigger" )

With this method alone, new Trigger ObjectNames are added to the Names table for the rest of the level, but the actual data defining what the Trigger should do is not added to the section of memory where normal Trigger data is stored. Wherever the data is, the Trigger does exist at least until the end of the frame (maybe a few frames) and it can be activated for that short time, but then the data is overwritten and your game will crash if it tries to activate the nonexistent Trigger.

It is not yet known how to make the actual Trigger persist for the entire level. Until we figure that out, one solution to create new "persistent" Triggers might be to have this code running every frame - though you'd have to make it not run while the normal level load/unload stuff is happening. Haven't tested that yet.

Once you have created a new Trigger, you only need to use ResolveTriggerNames() to re-create it after its data is deleted. However, there does not appear to be any harm in using CreateTriggers() for it again, it just won't do anything - the Names table will stay the same.

Spawning instant one-time Triggers

If you just want to make a new Trigger-like event that instantly happens and then disappears ( instead of being a "normal" Trigger that relies on other Triggers to make it happen and can be reused) you can use the SpawnEvent function from PWeb3.lua. It seems that SpawnEvent is not a global function, so you have to re-define it to use it globally.

SpawnEvent { TriggerType = { var1 = x, var2 = y, var3 = z... basically the same kind of Vars table that would be used with that Trigger type }

Clumps

Clumps are groups of multiple objects, used so that a single Trigger can reference and/or manipulate everything in the Clump at once. All Objects in a clump might need to be the same type of object, like they must be all Triggers, or all Hulls, or all Meshes, etc - not tested yet. Also, a Clump can contain other Clumps.

The functions in this category are from ClumpLoader.lua.

Creating new Clumps

To create new Clumps, define the ClumpInstances table with the same format used in ClumpInstances.lua (but concatenated, not with line breaks). Then run the LoadClumps function, then the SetClumpMembers function. Code "template":

ClumpInstances = { ObjectType = "ClumpInstances", { ObjectName = "NewClumpName", GardenerName = "NewClumpName_G(this name doesn't matter?)", Objects = { "some object's ObjectName", "another object's ObjectName", "and another, etc" } }, { same thing for another new Clump }, {another, etc} } LoadClumps( game:clumpBarn() ) SetClumpMembers( game:clumpBarn() )

Code example - using this in CS will create a new Clump that duplicates the Clump of Triggers which activates when you stand up after the new-game cutscene:

ClumpInstances = { ObjectType = "ClumpInstances", { ObjectName = "NewGameStandUpEvents", GardenerName = "NewGameStandUpEvents_G", Objects = { "70c1306a4a41c624a9ff123f344ef6ad", "d951daedc9f58a681efd39a8875f32c6", "1a737db99c2f3a7733e5c6426211d0e8", "daa0dd930abe88d5d241e02752c636d4", "f26b85b929a3e48edf43a5ec1f076c91", "65eef6f7bcbfcfa07b32c34d07e53132", "796c354ac61bd62ac987778b9fa96c4c", "5abf9a5b00e2e29c36f7871028e9c1e5", "e4fada7df843f162ebb2ee56a0e9f40f" } } } LoadClumps( game:clumpBarn() ) SetClumpMembers( game:clumpBarn() )

Demonstration of the new Clump:

SpawnEvent {EventAfterDelay = {delay = 0.01, effects = Names["NewGameStandUpEvents"]}}

Demonstration of the Clump it's duplicating:

SpawnEvent {EventAfterDelay = {delay = 0.01, effects = Names["e7fa07adac45a2fb9a369edcf5964ec7"]}}

Modifying Clumps

To modify existing Clumps, get them from ClumpInstances.lua and do basically the same code, but without the LoadClumps function:

ClumpInstances = { ObjectType = "ClumpInstances", { ObjectName = "original Clump's ObjectName", GardenerName = "original Clump's GardenerName", Objects = { the ObjectNames of whatever objects you want to be in that Clump } }, { same thing for another Clump }, {another, etc} }

SetClumpMembers( game:clumpBarn() )

For example, using this in CS will modify the existing Clump of stand-up-after-new-game Triggers (same as the new Clump example), so that you get knocked over by wind instead of standing up (replacing first Trigger in Objects list) and you hear the music cue from the broken statue symbol cutscene (adding that Trigger at end of list):

ClumpInstances = { ObjectType = "ClumpInstances", { ObjectName = "e7fa07adac45a2fb9a369edcf5964ec7", GardenerName = "|Clump849|Clump849_GARDENER", Objects = { "27b916d7b9ebb26d91432e16257cba5d", "d951daedc9f58a681efd39a8875f32c6", "1a737db99c2f3a7733e5c6426211d0e8", "daa0dd930abe88d5d241e02752c636d4", "f26b85b929a3e48edf43a5ec1f076c91", "65eef6f7bcbfcfa07b32c34d07e53132", "796c354ac61bd62ac987778b9fa96c4c", "5abf9a5b00e2e29c36f7871028e9c1e5", "e4fada7df843f162ebb2ee56a0e9f40f", "ed489eebe96121c9de7ccce1d25d6ced" } } }

SetClumpMembers( game:clumpBarn() )

Demonstration:

SpawnEvent {EventAfterDelay = {delay = 0.01, effects = Names["e7fa07adac45a2fb9a369edcf5964ec7"]}}

Timelines

Timelines are timed sequences of Trigger activations, used to organize cutscenes and other events where a bunch of Triggers get activated in a row.

It has not yet been tested, but Timelines can probably be added and/or modified in a similar way to Triggers and Clumps, based on TimelineInstances.lua's and the functions in TimelineBarn.lua:

TimelineInstances = { construct based on the format in TimelineInstances.lua }

LoadTimelines( game:timelineBarn() )

Creatures / Dynamic Nodes

Creatures are dynamically moving and interacting objects, other than the player avatars and the tiny flying cloths (Strands). Although code comments say that Creatures are just a Type of Dynamic Node, it appears that all Dynamic Nodes have their Type set as "Creatures" anyway. Creatures include Fish/Carpets, Guardians/War Machines, Cloth Guardians/Whales/Dragons, Jellyfish, Flow creatures, and Meteors/Comets/Shooting Stars.

It has not yet been tested, but Creatures can probably be added and/or modified in a similar way to Triggers and Clumps, based on DynamicNodeInstances.lua's and the functions in Creatures.lua:

DynamicNodeInstances = { construct based on the format in DynamicNodeInstances.lua } CreateCreatures( game:creatureBarn() ) --only used when adding new creatures(?) ResolveCreatureNames( game:creatureBarn() )

It seems that Creatures/Dynamic Nodes might actually be Triggers, based on how TriggerInitialize() is also used in Creatures.lua - so new Creatures might have the same issue as Triggers, and might disappear from the Names table shortly after being created with this method.

Particle Emitters

Particle Emitters emit particles (sparkles, puffs of sand/dust, etc.)

It has not yet been tested, but Particle Emitters can probably be added and/or modified in a similar way to Triggers and Clumps, based on ParticleEmitterInstances.lua's and the functions in EnvFX.lua:

ParticleEmitterInstances = { construct based on the format in ParticleEmitterInstances.lua }

LoadEnvFXParticles( game:envFXBarn(), game:particleBarn() )

Based on tests where logging code was put in LoadEnvFXParticles() and ParticleEmitterInitialize() and nothing was logged, it seems this script doesn't actually run at level start, so Particle Emitters might be added to Names table by another Lua script or by inaccessible C code. But maybe this function could still add/modify Particle Emitters anyway.

Sound Emitters

Sound Emitters emit sounds. They can be stationary or attached to a moving object somehow. It seems that most looping music is played by Sound Emitters that are set to be audible at the same volume anywhere in the level.

It has not yet been tested, but Sound Emitters can probably be added and/or modified in a similar way to Triggers and Clumps, based on SoundEmitterInstances.lua's and the functions in SoundBarn.lua:

SoundEmitterInstances = { construct based on the format in SoundEmitterInstances.lua }

LoadSoundEmitters( game:soundBarn() )

Based on tests where logging code was put in LoadSoundEmitters() and SoundEmitterInitialize() and nothing was logged, it seems this script doesn't actually run at level start, so Sound Emitters might be added to Names table by another Lua script or by inaccessible C code. But maybe this function could still add/modify Sound Emitters anyway.

Environment Nodes

Environment/Env Nodes set the environmental effects within certain regions, such as lighting, sky color, shadows, fog, bloom, etc. "IsZNode = true" nodes affect the region all along a certain space on the z-axis (usually toward/away from mountain), "= false" nodes affect a certain cube-shaped volume.

It has not yet been tested, but Env Nodes can probably be added and/or modified in a similar way to Triggers and Clumps, based on EnvNodeInstances.lua's and the functions in Environment.lua:

EnvNodeInstances = { construct based on the format in EnvNodeInstances.lua }

LoadEnvNodes( game:environment() )

There is also an UpdateEnvNamesTableEntry() function in Environment.lua that might be for modifying existing Env Nodes somehow.

LoadEnvNodes does not clear the EnvNodeInstances table automatically upon completion, and the code comments say this causes a crash, so might be a bad idea to do it yourself.

Markers

Markers seem to be locations, or regions of space around locations, which objects can be aimed or moved towards/away from by certain Triggers. Also, Markers can be moved around or attached to moving objects by Triggers.

It has not yet been tested, but Markers can probably be added and/or modified in a similar way to Triggers and Clumps, based on MarkerInstances.lua's and the functions in Markers.lua:

MarkerInstances = { construct based on the format in MarkerInstances.lua }

LoadMarkers( game:markerBarn() )

Rails

Rails seem to be paths that objects can move along.

It has not yet been tested, but Rails can probably be added and/or modified in a similar way to Triggers and Clumps, based on RailInstances.lua's and the functions in RailBarn.lua:

RailInstances = { construct based on the format in RailInstances.lua }

LoadRails( railBarn ) ( There doesn't seem to be a game:railBarn(), so maybe it's a different name in game:, or maybe it's used like RailBarn, RailBarn() or RailBarn(game). )

Camera Key Frames

Camera Key Frames seem to be the data for basically any location/angle/etc that the camera can get moved to, whenever the camera moves by itself - not just following behind the player, or being moved around by the player.

It has not yet been tested, but Camera Key Frames can probably be added and/or modified in a similar way to Triggers and Clumps, based on CameraKeyFrameInstances.lua's and the functions in CameraInit.lua:

CameraKeyFrameInstances = { construct based on the format in CameraKeyFrameInstances.lua }

LoadCameraKeyFrames( game:cameraSystem() )

Jets

Jets seem to be the physics effect that pushes your avatar down when you are inside certain sandfalls, and maybe other effects. Could probably be used to push the avatar in other directions as well.

It has not yet been tested, but Jets can probably be added and/or modified in a similar way to Triggers and Clumps, based on JetInstances.lua's and the functions in Jets.lua:

JetInstances = { construct based on the format in JetInstances.lua }

LoadJets( JetBarn ) ( There doesn't seem to be a game:jetBarn(), so maybe it's a different name in game:, or maybe it's used like JetBarn, JetBarn() or JetBarn(game). )

Signs

Signs are scrolling text, used only in the credits. Could maybe be used for other scrolling text as well, although there seem to already be scrolling text Triggers that might be easier for that.

It has not yet been tested, but Signs can probably be added and/or modified in a similar way to Triggers and Clumps, based on SignData.lua's, SignInstances.lua's and the functions in SignBarn.lua:

Credits = { construct based on the format in SignData.lua } SignInstances = { construct based on the format in SignInstances.lua } InitializeSignBarn( signBarn, signData, signInstances ) ( There doesn't seem to be a game:signBarn(), so maybe it's a different name in game:, or maybe it's used like SignBarn, SignBarn() or SignBarn(game). Also, signData/signInstances don't seem to actually be referenced inside InitializeSignBarn, so they might not be required, but if required then maybe signData is Credits and signInstances is SignInstances. )

TBD / Other

These are the remaining types of level resources. They can *maybe* be manipulated in real time with Lua code, but it's not yet known if/how it can be done.

Hulls

Hulls are regions of space that never move(?), which are used as physical collision geometry and/or a way for Triggers to determine "if this thing is touching/inside this region, do this event".

You can change the Hulls loaded at start of level by editing HullInstances.lua in the folder for that level under Data/Scripts.

Decoration Meshes

Decoration Meshes are any visible object other than terrain, fog, particle effects, Strands(?), and the basic "naked" player avatar. By themselves, they are not physical objects, their collision data comes from physical Hulls put in the same place.

You can change the Decoration Meshes loaded at start of level by editing DecorationMeshInstances.lua.bin in the folder for that level under Data/Scripts.

Sound Banks

SFX Banks contain sounds, and references/mixing data for longer sounds streamed from the audio files in Data/Sounds/Streams.

Music Banks just contain references/mixing data for music audio files streamed in from Data/Sounds/Streams.

These are LevelSfxSomewhere-SFX.bnk and LevelMusSomewhere-MUS.bnk in the Data/Sounds folder; not much is known yet about how to edit these at all, other than renaming files to swap one level for another.

TerrainData

Terrain Data determines the terrain's Height Map and Land Color (the color of sand/snow added to the sides and/or top of some Decoration Meshes, and that collects on the player avatars). It also determines Mask Data which seems to be unused.

You can change what is loaded at start of level by editing the TerrainData.bin inside TerrainData.bin.gz in the Data/Terrain/Level_Somewhere folder.

DuneColorShadow

DuneColorShadow is a texture that determines the color of the terrain in a level, and which areas are in shadow.

You can change what is loaded at the start of a level by editing/replacing the DuneColorShadow.dds texture inside the DuneColorShadow.dds.D3D11x64 file in the Data/Textures/Level_Somewhere/Bin folder.