If you have any additions for this article or the reference sheets feel free to ping JeNoVaViRuS on the ?ServerName? Discord Server.
Tools:
- FXMLR
- Witchy
- Notepad++
General
SFX are the special visual effects that use models and textures to "simulate" glowing, fire, flames and magic. They are located in the "sfx" folder of the game:
1. There is the "FRPG_SfxBnd_CommonEffects.ffxbnd(.dcx)" that contains SFX that can appear everywhere which also includes magic.
2. There are e.g. "FRPG_SfxBnd_m10.ffxbnd(.dcx)" which are the map SFX.
3. There are e.g. "FRPG_SfxBnd_c3471.ffxbnd(.dcx)" which are the SFX for special enemies.
After you unpacked one with Witchy there are some folders:
- "tex" which holds the textures as .tpf files, can be (un)packed with Witchy
- "model" which holds the models as .flver files, can only be converted/exported/imported
- "OutputData" which holds the .ffx files
The .ffx files determine how the SFX looks. To edit them, you need to convert them with "FXMLR" to .xml. When you have finished editing, the .xml must be converted back to .ffx and you must repack the folder with Witchy (as well as adding the entries in the Witchy .xml if you created additional .ffx files). You can download it from the tools page.
References
These sheets contain all the calls of the FFX file which are explained in the example further down.
IDs
The IDs are systematic:
0-1000: using items SFX
2000 - 2099: predefined SFX Effects
2100 - 2199: predefined SFX Effects
3000 - 3999: environment e.g. water splashes
4000 - 4999:
5000 - 5999: arrow SFX
6000 - 6999: bolt SFX
10000 - 19999: enemy SFX
20000 - 20099: casting SFX
20100 - 20199: bullet SFX
20200 - 20299: impact SFX
20300 - 20399: magic orbs SFX
20700 - 20799: Dragon Torso SFX
21818: corrosion cloud
22000 - 22999: item SFX
23000 - 23999: special attacks of weapons SFX
24000 - 24099:
80000 - 80999: fog gates
90000 - 90099: bonfires
95000 - 99999: dropped items
1200000 - 1200099:
100000000 - 100000099:
100001000 - 100001099:
…
Spell SFX example
To give an overview you can convert a spell .ffx file e.g. Fireball: f0020085.ffx -> f0020085.xml and then open it with Notepad++. In Notepad++, collapse all sections so it looks like this:
Overview
<?xml version="1.0" encoding="utf-8"?> <fxr1> <fxr1_data> <dcx_compression>None</dcx_compression> <big_endian>false</big_endian> <wide>false</wide> <unk1>1</unk1> <unk2>0</unk2> <root struct_type="EFFECT_CONSTRUCTOR" EffectID="20085" _varint_Unknown8="8"> <inline_fx_parameter_container name="Container1" unk1="0" unk2="0" unk3="1" /> <inline_fx_parameter_container name="Container2" unk1="0" unk2="0" unk3="1"> <fx_state_ref refid="FXState_0x680" /> <fx_state_ref refid="FXState_0x698" /> </root> <all_states> <fx_state refid="FXState_0x680"> <fx_action action_type="FXActionType14"> <fx_container name="action_parameter_container" unk1="0" unk2="0" unk3="1"> <fx_parameter struct_type="EFFECT_CALL" EffectID="0" _varint_Unknown2="0">" <fx_parameter struct_type="EFFECT_CALL" EffectID="2101" _varint_Unknown2="0"> <fx_parameter struct_type="EFFECT_CALL" EffectID="0" _varint_Unknown2="0"> <fx_parameter struct_type="EFFECT_CALL" EffectID="0" _varint_Unknown2="0"> <fx_parameter struct_type="EFFECT_CALL" EffectID="0" _varint_Unknown2="0"> <fx_parameter struct_type="EFFECT_CALL" EffectID="0" _varint_Unknown2="0"> <fx_parameter struct_type="EFFECT_CALL" EffectID="0" _varint_Unknown2="0"> <fx_parameter struct_type="EFFECT_CALL" EffectID="0" _varint_Unknown2="0"> <fx_modifier struct_type="FXModifier0_TrimParamList" LastEnabledParamListIndex="1" /> </fx_container> </fx_action> <fx_transition_ref refid="FXTransition_0x6B0" /> </fx_state> <fx_state refid="FXState_0x698"> <fx_action action_type="FXActionType5"> <fx_container name="action_parameter_container" unk1="0" unk2="0" unk3="1" /> </fx_action> </fx_state> </all_states> <all_transitions> </fxr1_data> <fxr1_template> </fxr1>
There you can see a root structure with an "EFFECT_CONSTRUCTOR" and an "EffectID". You need to set the ID so the game knows which ID it should pick to display the SFX as it looks up the params. The .ffx file name should always be the same ID as the "EffectID".
The states and transitions are typically 2 states, where the first state is the SFX and then the transition transfers it to the second state and the SFX ends. The root structure calls the states and the first state executes the "Effect_Calls".
The template is exactly what it says. It is there so the file can be converted back and you can look up the structure of other calls.
So the important part is everything within "<fx_state refid="FXState_0x680">".
Effect calls
Within it there are predefined "effect calls" e.g. "2121" which contain other child predefined effect calls e.g. "2023" and "2101". Within those are usually the "action calls".
Expand the effect call "2101" and collapse the sections within so it looks like this:
<fx_parameter struct_type="EFFECT_CALL" EffectID="2101" _varint_Unknown2="0"> <fx_parameter_container name="ParamContainer" unk1="0" unk2="1" unk3="1"> <fx_parameter struct_type="TimeParameter" Seconds="-0.0166666675" /> <fx_parameter struct_type="IntParameterType1" Value="1" /> <fx_parameter struct_type="ACTION_CALL" ActionID="0" _varint_Unknown2="0"> <fx_parameter struct_type="TimeParameter" Seconds="0" /> <fx_parameter struct_type="ACTION_CALL" ActionID="0" _varint_Unknown2="0"> <fx_parameter struct_type="ACTION_CALL" ActionID="35" _varint_Unknown2="0"> <fx_parameter struct_type="ACTION_CALL" ActionID="1" _varint_Unknown2="0"> <fx_parameter struct_type="IntParameterType1" Value="1" /> <fx_parameter struct_type="ACTION_CALL" ActionID="14" _varint_Unknown2="0"> <fx_modifier struct_type="FXModifier6_SetLifetimeB" Lifetime="-0.0166666675" _s32_Unknown1="1" /> </fx_parameter_container> </fx_parameter>
Ignore the empty action calls "0". Every effect call has a predefined amount of action calls which must be present so the file can convert back but they can also be 0 if that action is unused.
The action calls "35" and "1" influence the whole SFX meaning all child calls thus everything within is e.g. rotated instead of giving every single child some values to rotate it.
ActionID "14" calls another effect as a child. It belongs to "special action calls" which only occur as the last action call in an effect.
Effect calls have different presets of available values and they are always around the action calls.
e.g. for "2101" in this example:
at the top:
TimeParameter
IntParameterType1
TimeParameter
TimeParameter
<- Action Calls ->
at the bottom:
FXModifier0_TrimParamList
These values influence e.g. how much the main action call is delayed, how often it can occur at once, at which rate etc.
An effect call is mostly one main action call but being present multiple times, spawning again and again in short intervals. For instance a fireball spawns many "flames" over and over which also have their own texture frames which it cycles through to look like the fire is moving. It obviously wouldn't look good if it only spawned once and would look like a picture of a static flame…
Now collapse the action call "14" section so it looks like this:
<fx_parameter struct_type="ACTION_CALL" ActionID="14" _varint_Unknown2="0"> <fx_parameter_container name="ParamContainer" unk1="0" unk2="0" unk3="1"> <fx_parameter struct_type="EFFECT_CALL" EffectID="2101" _varint_Unknown2="0" tag="red flame around the middle"> <fx_parameter struct_type="EFFECT_CALL" EffectID="2101" _varint_Unknown2="0"> <fx_parameter struct_type="EFFECT_CALL" EffectID="2023" _varint_Unknown2="0"> <fx_parameter struct_type="EFFECT_CALL" EffectID="0" _varint_Unknown2="0"> <fx_parameter struct_type="EFFECT_CALL" EffectID="2023" _varint_Unknown2="0"> <fx_parameter struct_type="EFFECT_CALL" EffectID="0" _varint_Unknown2="0"> <fx_parameter_container name="ParamContainer" /> </fx_parameter> <fx_parameter struct_type="EFFECT_CALL" EffectID="0" _varint_Unknown2="0"> <fx_parameter struct_type="EFFECT_CALL" EffectID="0" _varint_Unknown2="0"> <fx_modifier struct_type="FXModifier0_TrimParamList" LastEnabledParamListIndex="4" /> </fx_parameter_container> </fx_parameter>
As you can see I added a tag after one effect call:
tag="red flame around the middle"
I advise to do this too when you figured out which part of the SFX this effect call is.
This makes editing WAY easier.
Important to note is that if you replace empty effect calls (EffectID="0") or simply add them then you also need to edit the fx modifier "FXModifier0_TrimParamList". This index value starts counting at 0 (meaning one entry used starting at the top) and can only be the amount of effect calls minus 1 at maximum. If you enter a higher number the game will crash.
Each single "Effect_Call" holds mostly one and sometimes multiple main action calls (by calling special actions that can again call another effect).
Multiple ones are called for e.g. multiple layers to hide something behind them or one main call is the actual effect and then there is another one that is supposed to appear as a trace of it in-game.
Action calls
Within an effect call there are often one or more main action calls and some secondary action calls always below a main one.
The main action call determines the general usage e.g. 40 for light circles, 59/71 typically for detailed textures and 108 for models etc. which you can look up in the reference sheet. They have a different amount of values specific to their usage in-game.
The secondary action calls modify the main call e.g. length, shifting, angle, range, extension etc.
Here is a typical structure (collapsed in Notepad++):
<fx_parameter struct_type="ACTION_CALL" ActionID="71" _varint_Unknown2="0"> <fx_parameter struct_type="ACTION_CALL" ActionID="28" _varint_Unknown2="0"> <fx_parameter struct_type="ACTION_CALL" ActionID="105" _varint_Unknown2="0"> <fx_parameter struct_type="ACTION_CALL" ActionID="35" _varint_Unknown2="0"> <fx_parameter struct_type="ACTION_CALL" ActionID="106" _varint_Unknown2="0"> <fx_parameter struct_type="IntParameterType1" Value="1" /> <fx_parameter struct_type="ACTION_CALL" ActionID="79" _varint_Unknown2="0">
We can see that "71" is the first one so it is the main action call that determines the general type of this SFX part. Below is the secondary call "28" for offsetting the position where it can spawn.
Below this one is the next secondary call "105" that e.g. can drag the SFX into a direction on the X axis or influence how close it will drag to the emitter. The emitter is basically the invisible spawn point that every SFX has and is determined by the DmyPolyId of the tae event. The position of the emitter itself can also be changed with the secondary call "1".
Note that some secondary action calls can only be replaced, e.g. 28 with 32. They can't both exist below the main call. Also there is a certain order you need to follow for secondary action calls. Which and how many secondary ones can be used is defined by the effect call. Looking at multiple different FFX files for spells will give a clear picture.
In this example you can also see a special action call "79" which contains a single child effect.
Here is an example of a main action call:
<fx_parameter struct_type="ACTION_CALL" ActionID="71" _varint_Unknown2="0"> <fx_parameter_container name="ParamContainer" unk1="0" unk2="0" unk3="1"> <fx_action_data struct_type="ActionParamStructType71"> <resources /> <f32 name="Duration" value="0.5" /> <f32 name="UnkFloat2" value="1" /> <f32 name="UnkFloat3" value="0" /> <s32 name="UnkInt4" value="0" /> <f32 name="RenderDepth" value="0" /> <s32 name="TextureID" value="20016" /> <s32 name="UnkInt7" value="0" /> <s32 name="UnkInt8" value="1" /> <s32 name="BlendMode" value="0" /> <s32 name="UnkInt10" value="0" /> <fx_value_node name="Scale1X" struct_type="RangedFloat" Value="0.0250000004" Min="0.25" Max="1.5" /> <fx_value_node name="Scale1Y" struct_type="EmptyValueNode" /> <fx_value_node name="Scale2X" struct_type="FloatSequence"> <float_tick time="0" value="1" /> <float_tick time="0.5" value="0.25" /> </fx_value_node> <fx_value_node name="Scale2Y" struct_type="FloatSequence"> <float_tick time="0" value="1" /> <float_tick time="0.5" value="0.25" /> </fx_value_node> <fx_value_node name="RotX" struct_type="EmptyValueNode" /> <fx_value_node name="RotY" struct_type="EmptyValueNode" /> <fx_value_node name="RotZ" struct_type="ConstFloat" Value="6.28000021" /> <fx_value_node name="RotSpeedX" struct_type="EmptyValueNode" /> <fx_value_node name="RotSpeedY" struct_type="EmptyValueNode" /> <fx_value_node name="RotSpeedZ" struct_type="RangedFloat" Value="0.790000021" Min="-1" Max="2" /> <s32 name="AnimFrameSliceCountPerDimension" value="8" /> <s32 name="AnimFrameTotalCount" value="32" /> <fx_value_node name="UnkField14_1" struct_type="RangedFloat" Value="1" Min="0" Max="32" /> <fx_value_node name="AnimFrameSelection" struct_type="RepeatingFloatSequence"> <float_tick time="0" value="0" /> <float_tick time="0.5" value="32" /> </fx_value_node> <fx_value_node name="Color1R" struct_type="ConstFloat" Value="1" /> <fx_value_node name="Color1G" struct_type="ConstFloat" Value="1" /> <fx_value_node name="Color1B" struct_type="ConstFloat" Value="1" /> <fx_value_node name="Color1A" struct_type="ConstFloat" Value="1" /> <fx_value_node name="Color2R" struct_type="FloatSequence"> <float_tick time="0" value="1" /> <float_tick time="0.400000006" value="1" /> <float_tick time="0.5" value="1" /> </fx_value_node> <fx_value_node name="Color2G" struct_type="FloatSequence"> <float_tick time="0" value="0.56078434" /> <float_tick time="0.400000006" value="0.56078434" /> <float_tick time="0.5" value="0.56078434" /> </fx_value_node> <fx_value_node name="Color2B" struct_type="FloatSequence"> <float_tick time="0" value="0.250980407" /> <float_tick time="0.400000006" value="0.250980407" /> <float_tick time="0.5" value="0.250980407" /> </fx_value_node> <fx_value_node name="Color2A" struct_type="FloatSequence"> <float_tick time="0" value="1" /> <float_tick time="0.400000006" value="1" /> <float_tick time="0.5" value="0" /> </fx_value_node> <s32 name="DS1R_UnkA1" value="4" /> <s32 name="DS1R_UnkA2" value="0" /> <s32 name="DS1R_UnkA3" value="0" /> <s32 name="DS1R_UnkA4" value="0" /> <s32 name="UnkInt15" value="0" /> <s32 name="UnkInt16" value="-1" /> <s32 name="UnkInt17" value="-1" /> <s32 name="UnkInt18" value="-1" /> <f32 name="UnkFloat19" value="0" /> <s32 name="UnkInt20" value="0" /> <f32 name="UnkFloat21" value="-1" /> <s32 name="UnkInt22" value="1" /> <fx_value_node name="DS1R_ColorModR" struct_type="ConstFloat" Value="5" /> <fx_value_node name="DS1R_ColorModG" struct_type="ConstFloat" Value="2.5" /> <fx_value_node name="DS1R_ColorModB" struct_type="ConstFloat" Value="1.25" /> <fx_value_node name="DS1R_ColorModA" struct_type="ConstFloat" Value="1" /> <fx_value_node name="DS1R_ColorModMult" struct_type="ConstFloat" Value="1" /> <f32 name="DS1R_UnkFloat6" value="0" /> <s32 name="DS1R_UnkInt7" value="0" /> <s32 name="DS1R_UnkInt8" value="0" /> <s32 name="DS1R_UnkInt9" value="0" /> <s32 name="DS1R_UnkInt10" value="0" /> <s32 name="DS1R_UnkInt11" value="0" /> </fx_action_data> </fx_parameter_container> </fx_parameter>
It might look confusing at first but that's simply because some values are not uncovered yet.
Each value has a different "name" so you can recognize what it does. The sheets already cover many ones that are not named yet.
Important to note is that the "fx_value_node" lines can be different types:
"ConstFloat" = one value:
<fx_value_node name="RotZ" struct_type="ConstFloat" Value="6.28000021" />
"RangedFloat" = one value but the min and max values are are random range differences each time this main action call is called:
<fx_value_node name="Scale1X" struct_type="RangedFloat" Value="0.0250000004" Min="0.25" Max="1.5" />
"FloatSequence" = multiple values, each line has a time value and one value for the actual modifier:
<fx_value_node name="Scale2X" struct_type="FloatSequence"> <float_tick time="0" value="1" /> <float_tick time="0.5" value="0.25" /> </fx_value_node>
Meaning at time 0 it will have scaling multiplicator of 1, at time 0.5 it will have 0.25.
"RangedFloatEx" = uses a value, min+max and the resources tag
Resources (below the "ActionParamStructType" line):
<resource_entry unk="1" struct_type="FloatSequence"> <float_tick time="0" value="0.0719999969" /> <float_tick time="1" value="0.180000007" /> </resource_entry> <resource_entry unk="1" struct_type="FloatSequence"> <float_tick time="0" value="0.0719999969" /> <float_tick time="1" value="0.180000007" /> </resource_entry> <resource_entry unk="1" struct_type="FloatSequence"> <float_tick time="0" value="0.0719999969" /> <float_tick time="1" value="0.180000007" /> </resource_entry>
Actual line:
<fx_value_node name="UnkField4_1" struct_type="RangedFloatEx" Value="1" Min="1" Max="1" ResourceIndex="0" />
Adding new models
1. Add the .flver file to the models folder. Open the file in FLVER Editor 2 and go to "Materials" tab.
2. One or more entries will be there and have a material set. The path or file extension is never needed.
3. The material files are in the "<game>\mtd" folder packed in the "Mtd.mtdbnd" file. Always use "_Alp" materials e.g. "P_Metal[DSB]_Alp" to make them appear properly with Action 108.
4. You can click on "Edit textures" on one of them to set the new "Path" (file name) of those textures.
5. Model textures need to be added to the "sfx\tex" folder as e.g. s00502.tpf file
6. A weapon model has mostly 3 textures (diffuse, specular, bump) so you can just add your texture files and also the fields like:
diffuse: s00502
specular: s00502_s
bump: s00502_n
This covers most of it. Playing around with some existing SFX is advised to get into it. You can also copy whole effect calls from one FFX file to another, but be aware of the "TrimParamList" value and of the general syntax. Completely writing one from scratch has no benefit. Simply deleting everything within the parent effect call and writing your own effect calls etc. is the maximum editing you should do.