- Welcome to Touhou Wiki!
- Registering is temporarily disabled. Check in our Discord server to request an account and for assistance of any kind.
User:Mddass/Touhou File Format Specification/ECL
ECL/.ecl files (敵コントロール言語 teki-kontorōru gengo, enemy control language) are compiled scripts (stack-based in V2+) and correspond to the stage enemies in the Touhou games, basically making them the veins and arteries from which the game's blood engine flows. They're responsible for stage enemy timings, bullet patterns, enemy movement, some background (3D) particle effects, boss setup, etc..
Versions
The ECL format has changed constantly over the years, specially in regards to the functions that it executes. From MoF onwards (version 2), ECL is a stack-based compiled script language.
- ECL V1: Th06-Th095 - First version of ECL, notable for not using a stack and having timeline sections for stage timing.
- ECL V2: Th10-Th16 - Complete revamp of the first version. Functions are named now, and introduces the stack system.
Syntax (thtk)
TBD
Syntax (TSA)
TBD, there are enough screenshots to know.
Variables (V1)
These lists are incomplete and are subject to change over time.
Th06: EoSD
These are the variables that can be used in EoSD ECL. They're very few (just 25), even when compared to its immediate successor: PCB.
- [-10001]: Local variable (32-bit integer).
- [-10002]: Local variable (32-bit integer).
- [-10003]: Local variable (32-bit integer).
- [-10004]: Local variable (32-bit integer).
- [-10005.0f]: Local variable (single-precision floating point).
- [-10006.0f]: Local variable (single-precision floating point).
- [-10007.0f]: Local variable (single-precision floating point).
- [-10008.0f]: Local variable (single-precision floating point).
- [-10009]: Local variable (32-bit integer).
- [-10010]: Local variable (32-bit integer).
- [-10011]: Local variable (32-bit integer).
- [-10012]: Local variable (32-bit integer).
- [-10013]: Rank (Easy: 0; Normal: 1; Hard: 2; Lunatic: 3; Extra: 4)
- [-10014]: Difficulty.
- [-10015.0f]: Self X-position.
- [-10016.0f]: Self Y-position.
- [-10017.0f]: Self Z-position.
- [-10018.0f]: Player X-position.
- [-10019.0f]: Player Y-position.
- [-10020.0f]: Player Z-position.
- [-10021.0f]: Angle to player.
- [-10022]: Current time.
- [-10023.0f]: Distance from player.
- [-10024]: Current life.
- [-10025]: Shot-type. (ReimuA: 0; MarisaA: 2; etc.)
Th07: PCB (Final)
The variables that can be used in PCB ECL are much more than that of EoSD ECL. Improvements include more local variables, inheritable variables, randomness variables, movement-related variables, and much more.
- [10000]: Local variable (32-bit integer).
- [10001]: Local variable (32-bit integer).
- [10002]: Local variable (32-bit integer).
- [10003]: Local variable (32-bit integer).
- [10004.0f]: Local variable (single-precision floating point).
- [10005.0f]: Local variable (single-precision floating point).
- [10006.0f]: Local variable (single-precision floating point).
- [10007.0f]: Local variable (single-precision floating point).
- [10008.0f]: Local variable (single-precision floating point).
- [10009.0f]: Local variable (single-precision floating point).
- [10010.0f]: Local variable (single-precision floating point).
- [10011.0f]: Local variable (single-precision floating point).
- [10012]: Local variable (32-bit integer).
- [10013]: Local variable (32-bit integer).
- [10014]: Local variable (32-bit integer).
- [10015]: Local variable (32-bit integer).
- [10016]: Rank (Easy: 0; Normal: 1; Hard: 2; Lunatic: 3; Extra: 4; Phantasm: 5)
- [10017]: Difficulty.
- [10018.0f]: Self X-position.
- [10019.0f]: Self Y-position.
- [10020.0f]: Self Z-position.
- [10021.0f]: Player X-position.
- [10022.0f]: Player Y-position.
- [10023.0f]: Player Z-position.
- [10024.0f]: Angle to player.
- [10025]: Current time.
- [10026.0f]: Distance from player.
- [10027]: Current life.
- [10028]: Shot-type. (ReimuA: 0; MarisaA: 2; SakuyaA: 4; etc.)
- [10029]: Function-wide integer.
- [10030]: Function-wide integer.
- [10031]: Function-wide integer.
- [10032]: Function-wide integer.
- [10033.0f]: Function-wide float.
- [10034.0f]: Function-wide float.
- [10035.0f]: Function-wide float.
- [10036.0f]: Function-wide float.
- [10037]: Function-wide integer. When a function is called from this, copied to the function's [-10029].
- [10038]: Function-wide integer. When a function is called from this, copied to the function's [-10030].
- [10039]: Function-wide integer. When a function is called from this, copied to the function's [-10031].
- [10040]: Function-wide integer. When a function is called from this, copied to the function's [-10032].
- [10041.0f]: Function-wide float. When a function is called from this, copied to the function's [-10033.0f].
- [10042.0f]: Function-wide float. When a function is called from this, copied to the function's [-10034.0f].
- [10043.0f]: Function-wide float. When a function is called from this, copied to the function's [-10035.0f].
- [10044.0f]: Function-wide float. When a function is called from this, copied to the function's [-10036.0f].
- [10045.0f]: Angle to origin point (orbital movement).
- [10046.0f]: Rotation speed (orbital movement).
- [10049.0f]: Distance from origin point (orbital movement).
- [10050.0f]: Origin point X-coord (orbital movement).
- [10051.0f]: Origin point Y-coord (orbital movement).
- [10052.0f]: Origin point Z-coord (orbital movement).
- [10053.0f]: Unknown float. Used by Yukari's last spell to store some angle. Angle to origin point + pi?
- [10054.0f]: Current angle.
- [10055.0f]: Returns a random value between
0.0f
and1.0f
. - [10056.0f]: Returns a random value between
[10034.0f]
and[10034.0f] + [10033.0f]
. - [10057.0f]: X-offset to target point.
- [10058.0f]: Y-offset to target point.
- [10059.0f]: Z-offset to target point.
- [10060.0f]: Returns a random radian value.
- [10061]: Unknown. Used by the Prismrivers.
- [10062]: Unknown byte. Seems to be some state byte. Used by the Prismrivers.
- [10066]: Boss life threshold.
- [10067]: Boss life threshold. What's the difference from [10066]?
- [10068]: Minimum life. If the enemy life drops down to this, it will immediately be deleted.
- [10069]: Minimum life. If the enemy life drops down to this, it will immediately be deleted. What's the difference from [10068]?
- [10070]: Item reward on death.
- [10071]: Score reward on death.
- [10072.0f]: Local variable? Used by Yuyuko to store X-offset to spawn bullets from.
- [10073.0f]: Local variable? Used by Yuyuko to store Y-offset to spawn bullets from.
Variables (V2)
Note: All variables from MoF onwards carry over to the next games.
Missing variables are either unknown or unused.
Th10: MoF
- [-10000]: Random integer value.
- [-9999.0f]: Random float value from 0.0f to 1.0f.
- [-9998.0f]: Random value from -pi to pi.
- [-9997.0f]: Final X-coordinate.
- [-9996.0f]: Final Y-coordinate.
- [-9995.0f]: Absolute X-coordinate.
- [-9994.0f]: Abdolute Y-coordinate.
- [-9993.0f]: Relative X-coordinate.
- [-9992.0f]: Relative Y-coordinate.
- [-9991.0f]: Player X-pos.
- [-9990.0f]: Player Y-pos.
- [-9989.0f]: Angle to player (based on current position).
- [-9988]: Time elapsed (in current sub?)
- [-9987.0f]: Random float value from -1.0f to 1.0f.
- [-9986]: Becomes non-null when a timeout occurs.
- [-9985]: Local variable.
- [-9984]: Local variable.
- [-9983]: Local variable.
- [-9982]: Local variable.
- [-9981.0f]: Local variable.
- [-9980.0f]: Local variable.
- [-9979.0f]: Local variable.
- [-9978.0f]: Local variable.
- [-9977.0f]: Same as [-9997.0f].
- [-9976.0f]: Same as [-9996.0f].
- [-9975.0f]: Same as [-9995.0f].
- [-9974.0f]: Same as [-9994.0f].
- [-9973.0f]: Same as [-9993.0f].
- [-9972.0f]: Same as [-9992.0f].
- [-9971.0f]: Absolute current angular direction.
- [-9970.0f]: Relative current angular direction.
- [-9969.0f]: Absolute current movement speed.
- [-9968.0f]: Relative current movement speed.
- [-9967.0f]: Absolute radius from origin point.
- [-9966.0f]: Relative radius from origin point.
- [-9965.0f]: Same as [-9991.0f].
- [-9964.0f]: Same as [-9990.0f].
- [-9963.0f]: Boss 0 X-pos.
- [-9962.0f]: Boss 0 Y-pos.
- [-9961]: 未知 (地址1120)
- [-9960]: Game difficulty.
- [-9959]: Rank. E = 0; N = 1; H = 2; L = 3; X = 4; O = 5.
- [-9958]: Final angular direction.
- [-9957]: 1
- [-9954]: Current life.
- [-9953]: Easy mode flag.
- [-9952]: Normal mode flag.
- [-9951]: Hard mode flag.
- [-9950]: Lunatic mode flag.
Th11: SA
- [-9949]: Miss count
- [-9948]: Bomb count
- [-9947]: Becomes null on timeout, miss or bomb (or any other game-specific condition i.e. trance).
- [-9946]: Amount of units on-screen.
- [-9945]: Shot type.
- [-9944.0f]: Distance from player.
- [-9943]: Boss local variable.
- [-9942]: Boss local variable.
- [-9941]: Boss local variable.
- [-9940]: Boss local variable.
- [-9939.0f]: Boss local variable.
- [-9938.0f]: Boss local variable.
- [-9937.0f]: Boss local variable.
- [-9936.0f]: Boss local variable.
- [-9935.0f]: Local variable.
- [-9934.0f]: Local variable.
- [-9933.0f]: Local variable.
- [-9932.0f]: Local variable.
Th12: UFO
- [-9931]: 已经出现单位数量-1,包含main
- [-9930]: Power.
Th12.5: DS
- [-9926]: Global variable.
- [-9925]: Global variable.
- [-9924]: Global variable.
- [-9923]: Global variable.
- [-9922.0f]: Global variable.
- [-9921.0f]: Global variable.
- [-9920.0f]: Global variable.
- [-9919.0f]: Global variable.
- [-9918.0f]: Global variable.
- [-9917.0f]: Global variable.
- [-9916.0f]: Global variable.
- [-9915.0f]: Global variable.
- [-9914]: Unit ID.
- [-9913]: Photo count.
- [-9911.0f]: Current movement angle.
Th12.8: GFW
Th13: TD
- [-9908]: Killable unit count.
- [-9907]: Current spell practice spellcard ID.
Th16: HSiFS
- [-9903]: Current subseason.
ECL Instruction Table (V1)
Main Instruction Table
NOTE: Currently, only EoSD (th06) and PCB (th07) have been properly and thourougly checked. This table will most likely not be accurate for games other than those.
This list below lists the opcode, mnemonic/name, format and a description for each main ECL instruction used in the subroutines section of the ECL file.
Certain things might be game-specific. In which case, they will have a specific text color. This game-specific stuff will apply to the game that the color corresponds to, as well as any games afterwards. Color code:
- EoSD (th06): Dark red.
- PCB (th07): Medium violet red.
- IN (th08): Orange red.
- PoFV (th09): Green.
- StB (th095): Saddle brown.
Name | Opcode | Format | Description |
---|---|---|---|
nop | 0 | void | No operation. |
delete | 1 | int a
void |
Stop script execution on this enemy and delete it if a == 1.
Stop script execution on this enemy and delete it. |
jmp | 2
4 |
int t, int cp | Unconditionally sets current time to t and sets the current seek offset in this function to cp. |
jmp_ex | 3
5 |
int t, int cp, int& var | If var != 0, sets current time to t, sets the current seek offset in this function to cp and decrements var by 1. |
iset | 4
6 |
int &var, int a | Sets var to a. Equiv.: var = a |
fset | 5
7 |
int &var, float a
float &var, float a |
Sets var to a. |
iset_r | 6 | int &var, int a | Sets var to a random value in the range [0, a). Equiv.: var = rand() % a |
iset_r2 | 7 | int &var, int min, int a | Sets var to a random value in the range [min, min+a). Equiv.: var = rand() % a + min |
fset_r | 8 | int &var, float a
int &var, float a |
Sets var to a random value in the range [0, a). |
fset_r2 | 9 | int &var, float min, float a
int &var, float min, float a |
Sets var to a random value in the range [min, min+a). |
get_x | 10 | int &var | Sets var to the enemy's x-position. |
get_y | 11 | int &var | Sets var to the enemy's y-position. |
get_z | 12 | int &var | Sets var to the enemy's z-position. |
iadd | 13 | int &var, int l, int r | Sets var to the result of l + r. Equiv.: var = l + r |
isub | 14 | int &var, int l, int r | Sets var to the result of l - r. Equiv.: var = l - r |
imul | 15 | int &var, int l, int r | Sets var to the result of l * r. Equiv.: var = l * r |
idiv | 16 | int &var, int l, int r | Sets var to the result of l / r. Equiv.: var = l / r |
imod | 17 | int &var, int l, int r | Sets var to the result of l % r. Equiv.: var = l % r |
inc | 18 | int &var | Increment var by 1. |
dec | 19 | int &var | Decrement var by 1. |
fadd | 20 | int &var, float l, float r | Sets var to the result of l + r. Equiv.: var = l + r |
fsub | 21 | int &var, float l, float r | Sets var to the result of l - r. Equiv.: var = l - r |
fmul | 22 | int &var, float l, float r | Sets var to the result of l * r. Equiv.: var = l * r |
fdiv | 23 | int &var, float l, float r | Sets var to the result of l / r. Equiv.: var = l / r |
fmod | 24 | int &var, float l, float r | Sets var to the result of l % r. Equiv.: var = l % r |
atan2 | 25 | int &var, float ox, float oy, float tx, float ty | Sets var to the result of atan2(tx - ox, ty - oy). Effectively gives the angle towards tx,ty with ox,oy as the origin. |
norm_r | 26 | int &var | Normalizes var into a value that's within the (-pi, pi) range. var will be reduced by 2*pi until it is within that range if it is greater than pi, or incresed by 2*pi until it is within that range if it is lower than pi. |
itest | 27 | int a, int b | Compares a with b (integer values). The result will be stored in the enemy's comparison register. The result is -1 if a < b, 0 if a == b or 1 if a > b. |
ftest | 28 | float a, float b | Compares a with b (floating-point values). The result will be stored in the enemy's comparison register. The result is -1 if a < b, 0 if a == b or 1 if a > b. |
jmp_l | 29 | int t, int cp | If the comparison register is -1, sets current time to t and sets the current seek offset in this function to cp. |
jmp_le | 30 | int t, int cp | If the comparison register is -1 or 0, sets current time to t and sets the current seek offset in this function to cp. |
jmp_e | 31 | int t, int cp | If the comparison register is 0, sets current time to t and sets the current seek offset in this function to cp. |
jmp_g | 32 | int t, int cp | If the comparison register is 1, sets current time to t and sets the current seek offset in this function to cp. |
jmp_ge | 33 | int t, int cp | If the comparison register is 1 or 0, sets current time to t and sets the current seek offset in this function to cp. |
jmp_n | 34 | int t, int cp | If the comparison register is -1 or 1, sets current time to t and sets the current seek offset in this function to cp. |
call | 35 | int func, int iarg, float farg | Copies variables -10001 to -10012 to backup registers and calls function func. Also, -10001 is set to iarg and -10005 is set to farg. |
return | 36 | void | Returns to the parent function and copies variables -10001 to -10012 from the backup registers. Crashes the game if there is no parent function. |
call_l | 37 | int func, int iarg, float farg, int a, int b | Runs call(func, iarg, farg) if a < b. |
call_le | 38 | int func, int iarg, float farg, int a, int b | Runs call(func, iarg, farg) if a <= b. |
call_e | 39 | int func, int iarg, float farg, int a, int b | Runs call(func, iarg, farg) if a == b. |
call_g | 40 | int func, int iarg, float farg, int a, int b | Runs call(func, iarg, farg) if a > b. |
call_ge | 41 | int func, int iarg, float farg, int a, int b | Runs call(func, iarg, farg) if a >= b. |
call_n | 42 | int func, int iarg, float farg, int a, int b | Runs call(func, iarg, farg) if a != b. |
pos | 43 | float x, float y, float z | Sets the enemy position accordingly. |
44 | float a, float b, float c | Unknown. Sets 3 values in the enemy. | |
dir | 45 | float r, float s | Sets enemy movement direction to r and movement speed to s. |