-
Notifications
You must be signed in to change notification settings - Fork 18
Leviathon Language Reference
The following is a terse description of the Leviathon language. This is not intended to be read as prose but act as encyclopedic entries on the different data types as seen by the compiler.
The Leviathon language has a reference compiler, the Amsterdam-Bucharest Compiler which can be downloaded from here. The project page can be found here.
The language was first proposed by AsteriskAmpersand as a way of realizing an earlier proposal by Stracker to define a compillable higher level language for monster THKs. The language syntax and semantics were agreed upon by the Leviathon Language Design Committee which included NackDN, Fandirus, Silvris, Fexty and AsteriskAmpersand. The compiler-decompiler was written by AsteriskAmpersand who had help in verification and testing from Fandirus.
- NACK Files
- Compilation Mechanisms
- FAND Files
- FEXTY Files
Asignments consist of a pair of Local Identifier and Numeric Literal joined by an equal sign =
. The compiler will resolve all instances of the local identifier to the numeric liteeral on the right side.
Imports consist of two parts. A resource identifier and an alias. The alias must be a Local Identifier. The resource identifier can either be an enquoted windows path (either relative or absolute) or a THK Identifier declared in the FAND File.
Library imports allow accessing the local identifiers in a separate source file, this includes both assignment and node names.
Internal Library Imports correspond to imports where the resource identifier is a THK Identifiers declared in the FAND File. This is resolved to the path bound to the THK Identifier, with a metadata tag related to the entry's id on the FAND File. Example: importlibrary Global as Global
External Library Imports are imports where the resource identifier is a quoted windows path. Relative paths are resolved relative to the FAND File used for project level compilation, or the current file for file level compilation. Example: importlibrary "./CombatOpening.nack" as opening
Action Imports correspond to a monster's named list of actions. They correspond to the relevant monster named by the action import. Example: importactions Behemoth as behemoth
, importactions Odogaron as clifford
It's possible to specify different action sets through compiler directives. This is the exhaustive list of legal action scopes for the default action set.
Anjanath | Rathalos | Aptonoth | Jagras | Zorah_Magdaros |
Mosswine | Gajau | Great_Jagras | KestodonM | Rathian |
Pink_Rathian | Azure_Rathalos | Diablos | Black_Diablos | Kirin |
Behemoth | Kushala_Daora | Lunastra | Teostra | Lavasioth |
Deviljho | Barroth | Uragaan | Leshen | PukeiPukei |
Nergigante | Xeno'jiiva | KuluYaKu | TzitziYaKu | Jyuratodus |
TobiKadachi | Paolumu | Legiana | Great_Girros | Odogaron |
Radobaan | Vaal_Hazak | Dodogama | Kulve_Taroth | Bazelgeuse |
Apceros | Kelbi | Kelbi2 | Hornetaur | Vespoid |
Mernos | KestodonF | Raphinos | Shamos | Barnos |
Girros | Ancient_Leshen | Gastodon | Noios | Magmacore |
Magmacore2 | Gajalaka | Magmacore3 | Tigrex | Nargacuga |
Barioth | Savage_Deviljho | Brachydios | Glavenus | Acidic_Glavenus |
Fulgur_Anjanath | Coral_PukeiPukei | Ruiner_Nergigante | Viper_TobiKadachi | Nightshade_Paolumu |
Shrieking_Legiana | Ebony_Odogaron | Blackveil_Vaal_Hazak | Seething_Bazelgeuse | Beotodus |
Banbaro | Velkhana | Namielle | Shara_Ishvalda | Popo |
Anteka | Wulg | Cortos | Boaboa | Alatreon |
Gold_Rathian | Silver_Rathalos | Yian_Garuga | Rajang | Furious_Rajang |
Brute_Tigrex | Zinogre | Stygian_Zinogre | Raging_Brachydios | Safi'jiiva |
Scarred_Yian_Garuga | Frostfang_Barioth | Fatalis |
A node is a grouping of execution sections. It's equivalent to a subprocess in imperative languages. Nodes start with a Node Header, have a body consisting of Segments and end with the keyword endf
. Nodes are executed sequentially, segment by segment. If execution reaches a node's end it does not return it halts.
A node header consists of the keyword def
followed by the Node Aliases. It can optionally be followed by an Explicit ID which is followed optionally by an Explicit Index
Node aliases are a list of Unscoped Identifiers separated by &
. They are used by Named Calls to jump execution to the node. During compilation node aliases are discarded and only used for resolving jumps.
Explicit ID enable overriding the compiler's auto-generation of ids to explicitly assign an id to a node. An Explicit ID consists of :
and a Numeric Literal. IDs are used by the underlying compiled THK for resolving external references.
Explicit Indices enable specifying the node's index, overriding the file's positional ordering and the compiler's auto-generation of indices. An Explicit Index consists of @
and a Numeric Literal. Indices are relevant during compilation as it changes the ordering of nodes in the resulting file. Additionally compiled THK use indices for resolving internal references.
The Node Format can roughly be read:
def alias0 & alias1 & ... & aliasn : ExplicitId @ ExplicitIndex
Segment0
...
SegmentM
endf
An example of a node:
def distance_checks & node_004 : 240
if self.distance_3d_to_target().leq(650)
self.enraged() >> node_014 => reset
>> node_013 => reset
elif self.distance_3d_to_target().leq(1600)
self.enraged() >> node_017 => reset
>> node_016 => reset
endif
return
endf
A segment is the smallest unit of reorderable code exection (a segment is however NOT atomic, parts of the segment are executed sequentially in a specific order).
A segment is either the finalizer, or a any combination of the following optional elements in the given order:
Formally speaking the sub-elements of a segment execute in the following order:
- Function
- Flow Control
- Action
- Call
- Directive
A function without a flow control element in the segment represents an inline conditional and will skip the execution of the remainder of the segment.
The finalizer is a the keyword conclude
that indicates a THK has finished executing and is used by interrupts to indicate they are handing control back to the main context or that the THK has no executbale code from the given entry points. It's most commonly seen in the Global THK's first node.
The Segment Format can roughly be read:
conclude
or
flowControl function -> action >> call => directive @ metadata
When directives are alone their symbol can be ellided
directive @ metadata
Example segments
if self.current_quest().is_rank(rank.AT)
>> node_028 @ parameter2:1000
self.distance_3d_to_target().leq(700) >> node_022 => return
Flow Control redirects the flow of execution locally by defining sub-execution scopes. There are two types of flow control operators, Conditionals and Randomization.
Conditionals alter the flow of execution based on the return of the Function within the same segment. Conditionals manipulate context and are not meaningful as single segments but as a contiguous grouping of segments within a node.
The keyword if
opens a conditional scope which is only executed if the function on the same segment evaluates to true. It also starts the series of conditional scopes. A scope spans multiple segments until a scope close is found. It's a syntax error if there is no corresponding scope close before the node's end.
The following keywords are only valid if there's an already open conditional scope.
The keyword else
closes the previous conditional scope and opens a separate one. This scope is only executed if none of the previous scopes in the series of conditional scopes has evaluated true previously.
The keyword elif
closes the previous conditional scope and opens a separate one. This scope is only executed if none of the previous scopes in the series of conditional scopes has evaluated true previously and the function within the same segment evaluated to true.
The keyword endif
closes the previous conditional scope and ends the series of conditional scopes. It cannot be followed by any other segment contents except metaparameters.
The keyword endwith
closes the previous conditional scope and ends the series of conditional scopes. It executes the remaining contents of the segment BEFORE closing the scope IF the previous scope was executed.
Conditional follows the following rough formats:
if function
...
elif function
...
else
...
endif @ metaparameters
or
if function
...
elif function
...
else
...
endwith function action directive @ metaparameters
Examples of conditional usage.
if self.mounted()
-> behemoth.ride_rage_head()
else
-> behemoth.ride_rage_head_lv2()
endif
if function#10F()
>> node_077
elif self.current_quest().is_rank(rank.AT)
>> node_076
else
>> node_075
endwith >> node_084
Randomization alter the flow of execution based on the result of a random value. Randomization Elements manipulate context and are not meaningful as single segments but as a contiguous grouping of segments within a node. Randomization Elements are accompanied by the percentage chance of its scope being executed. Only one scope is executed per series of randomization blocks.
The following two randomization elements must be followed by a parenthesis with an identifier that should resolve to an integer which is the chance of the subsequent scope being executed.
The keyword random
indicates the start of a random scope and a random sequence.
The keyword elseRandom
and elser
close the previous randomization scope and opens a separate one.
The keywords endr
and endrandom
close the previous scope and terminates the seuqence of randomization blocks. It cannot be followed by any other segment contents except metaparameters.
The keyword endwith
and endrwith
closes the previous conditional scope and ends the series of randomization blocks. It executes the remaining contents of the segment BEFORE closing the scope IF the previous scope was executed.
Random follows the following rough formats:
random(chance)
...
elser(chance)
...
endr @ metaparameters
or
random(chance)
...
elser(chance)
...
endrwith >> call => directive @ metaparameters
Examples of random usage.
random (80) >> node_070
elser (20)
endr
random (LOW_CHANCE)
self.targetEnemy(21) @ parameter2:1000
elser (LOW_CHANCE)
self.targetEnemy(22) @ parameter2:1000
elser (MEDIUM_CHANCE)
self.targetEnemy(11) @ parameter2:1000
elser (60)
self.targetEnemy(12) @ parameter2:1000
endrwith >> 522
Funcions correspond to ingame checks. Functions always have a boolean return, they might or might not have side-effects. Functions with side-effects as convention tend to have a fixed return of false. Using the ETL, it's possible to negate a function or a register comparison's return by prepending not
.
Functions can take up to 2 meaningful parameters, but as part of their definition they can have multiple dummy parameters. Functions arguments additioanlly can belong to special scopes that are resolved separately from scopes.
Function signatures determine the syntax and positions of the arguments and parenthesis. This are specified by the FEXTY File used in the compilation process.
Example: function#52()
, not function#32()
, self.enrage()
, not self.flying()
Function Literals allow specifying the function through their ID. They consist of the keyword function
followed by the pound literal #
, the function ID in hexadecimal and a matched pair of parenthesis. Optionally up to 2 arguments can be provided. Example: function#A2()
Non-Literal functions are always prefaced by the keyword self
, however their syntax is entirely dependent on the FEXTY File used for compilation. The FEXTY File specification explains how to read FEXTY Files to produce the valid function expressions. Because this files run through a separate compilation process with significant freedoms, while the syntax is formalized it can be as expressive as the author of the file wishes. Leviathon provides a default fexty file and the exhaustive syntax of its functions is provided within the specification example.
An example from the default fexty file: self.targetEnemy(target_em.last_attacker)
Register operations are considered functions in terms of their syntactical role, however they have unique syntax. Register operations are always enclodes in pairs of matching angled brackets [ ... ]
. Register Operations can be classified in 2 ways. By the number of arguments they take: Unary and Binary Operations. By wether they are part of the default THK Register Operations or if they are part of the Extended THK Library. Extended operations are tagged as [ETL]
and will simply not execute when the game interpreting the resulting THK is missing the ETL Plugin.
All register operations consist of minimally a register, which can be a Register Literal or a Register Identifier, and an operator represented as a valid symbol.
A register literal consists of the dollar sign $
followed by a valid register character (any capital letter from A
to V
). Example $S
A register identifier is simply any valid Identifier. The identifier is treated separately from others and can only be coerced at project level within the FAND File. Multipler register usages throughout a project using the same identifier will be mapped to the same register on compilation. Example: RegVar1
.
Unary operations consist of a Register Identifier followed by either one of the two traditional unary operation literals or one of the three ETL unary operation literals.
This are:
-
|-
: Reset the register to 0 -
++
: Increment the register by 1
The extended literals are:
-
--
: Decrement the register by 1 -
##
: Write current system time to the register -
#-
: Write difference from stored time to current time to the register -
||
: Writes the absolute value of the stored value to the register
Example: [$A ++]
, [RegVar |-]
Binary operations consist of a Register Identifier followed by either one of the six traditional unary operation literals or one of the eight ETL binary operation literals, which is then followed by the operand. Without the ETL the operand must be an Identifier which correctly resolves to a numeric value. Within the ETL the operand can also be a non-conflicting Register Identifier. The non-conflicting condition means that the register identifier cannot be a valid numeric-resolving identifier within the current namespace otherwise it will simply resolve to a number first. This means the ETL allows operations between registers, which combined with the extended literals enables the full range of arithmetic operators and makes them function identical to regular assembly registers.
The binary comparison literals are:
-
==
: Equal -
<=
: Lesser Equal -
<
: Less Than -
>=
: Greater Equal -
>
: Greater Than -
!=
: Not Equal
The extended literals:
-
:=
Assigns directly to the register -
+=
Adds and stores the result on the register -
-=
Substracts from and stores the result on the register -
*=
Multiplies and stores the result on the register -
/=
Divides by the operand and stores the result on the register -
#>
Checks if time elapsed relative to the value stored on the register is greater than the operand -
#<
Checks if time elapsed relative to the value stored on the register is less than the operand -
%=
Modulos by the operand and stores the result on the register
All of the ETL binary operators except the Time Elapsed checks return false.
Examples: [RegisterVar1 == 1]
, [FlightTimer #< 6000]
A Call modifies the segment pointer to the start of the target node. At high level the target node might be on a separate file. Whenever a call is executed the current segment position is placed on a stack. The return
directive pops this stack and continues execcution on the line AFTER the call.
All calls must be prepended by the call sign >>
.
A call literal consists of the keyword call
followed by the pound literal #
and finally the node index in decimal. Example: >> call#122
.
A local call consists of an identifier. The identiier must resolve to an existing node alias in the same NACK file. Example: >> node_000
A scoped call consists of a scoped identifier. The identifier must resolve to an existing node alias in the import corresponding to the scope. Example: >> Global.node_032
A scoped call consists of an identifier, the dot literal .
, the pound literal #
and finally the node id in decimal. The identifier must resolve to an existing scope. Example: >> Global.#152
Directives modify the segment pointer, which determines the next segment that will be executed.
The keyword return
sets the segment pointer to the segment which follows the call to the current node.
The keyword repeat
sets the segment pointer to the start of the current node.
The keyword reset
resets the segment pointer to the first node of the THK that corresponds to the active context (a reset inside the Global THK will reset to the first node of the THK which called it).
Actions map to monster actions. Monster actions correspond to a set of animations, shell and collision calls, and executable code mapped to the respective action class.
All actions must be prefaced with the action sign ->
and be followed by matched parenthesis pair containingthe action arguments (empty parenthesis will pass the default arguments).
An action literal consists of the keyword action
followed by the pound literal #
and finally the action id in hexadecimal. Example: -> action#2B()
A scoped action consists of a scoped identifier. The scope must correspond to an action import and the target must correespond to a valid action with the action import. Example: -> behemoth.normal_turn()
An action can have up to 5 arguments. Each argument must be an identifier and resolve to a number. Example: -> behemoth.dying_walk(2,Global.PARAMETER_VAL,0,PARAMETER_CONSTANT)
A numeric literal ccan be either a decimal literal or a hexadecimal literal. A decimal literal consists of a string of numeric characters that start with a non-zero character. A hexadecimal literal consists of numeric characters or the letters A to F, it must be prefaced with the hex identifier 0x
. This preface is omitted in function literals which are hexadecimal in all instances. Example: 0xDEADBEEF
, 314159
, 0x1E62A32
.
Local or Unscoped Identifiers consist of any alphabetic character followed any number of alphanumeric characters or the underscore or the apostrophe character ([A-Za-z][a-zA-Z_'0-9]*). Local Identifiers resolve only on the current namespace. Example: Safi'Jiiva
, node_000
, integral_area
,varNumber5
.
A scoped identifier is a pair of Local Identifiers separated by a dot literal .
. The first identifier must correspond to an imported scope name, either a Library Import or an Action Import or one of the reserved scopes. The seccond identifier must be a valid identifier within the scope define by the first identifier. Example: Global.node_005
, Global.DISTANCE_MID
, behemoth.walk
, ImportedLib.CHANCE_LOW
.
A metadata declaration is preceded by the meta literal @
. It consists of pairs of THK Field and numeric literal, separated by a colon :
. The valid THK Field names are:
endRandom | flowControl | branchingControl | unkn1 | unkn2 | functionType | parameter1 | unkn3 |
unkn4 | comboSetting | unkn6 | parameter2 | nodeEndingData | extRefThkID | extRefNodeID | localRefNodeID |
unkn7 | unkn8 | unkn9 | unkn10 | unkn11 | actionID | actionUnkn0 | actionUnkn1 |
actionUnkn2 | unkn12 | actionUnkn3 | actionUnkn4 | unknExtra0 | unknExtra1 | unknExtra2 |
It's NOT RECOMMENDED to use metadata identifiers directly for fields that have non-metadata syntactical constructs. The compiler does not use metadata fields until the last phases of compilation, as a result there's a significant absencce of error checking on the data placed on this section.
positional arguments:
input File to decompile (.thk or thklst)
optional arguments:
-h, --help show this help message and exit
-nullShow Keep empty nodes on decompiled output
-fixShow Keep placeholder empty instruction for decompiler corrections
-lastShow Keep the last node even if empty
-indexShow Preserve each node's index in the file
-idShow Preserve node Id in the file
-noActions Do not resolve actions to names
-raiseInvalidReferences
Stop decompilation if an illegal call is found
-warningsHide Hide decompilation warnings as comments on offending segments
-keepRegisters Keep registers as fixed identifiers during decompilation
-renameNackFiles Rename .nack files to their function
-analyze Analyse the code flow of the project and report on action, register and call usage
-outputPath OUTPUTPATH
Root folder where the decompiled files will be outputted
-analysisOutputPath ANALYSISOUTPUTPATH
Folder to output the results of the code analysis
-fexty FEXTY Forked Functional Extension input file
Either the THKLST file for project level decompilation or the THK file for file level decompilation. If you intend to edit more than one file, or the global THK DO NOT USE FILE LEVEL DECOMPILATION.
-nullShow
Preserves empty nodes instead of elliding them in decompilation results. Relevant if something is calling empty nodes (none of the game''s THK do this thankfully). Generally should not be used.
-fixShow
Generates empty instructions when one is commented out due to illegal context. This is useful if for some reason one wishes to preserve segment count despite illegal instructions inflating said count. Generally should not be used.
-lastShow
Preserves the last node and adds an explicit index. This ensures the node count on compilation is preserved. Generally should not be used.
-indexShow
Writes the index of each node to its header. This ensures nodes are on the same index on compilation. Generally should not be used.
-idShow
Writes the index of each node to its header. This ensures nodes use the same index on compilation. Generally should not be used. It has a use ccase when one (against previous advice) is editing a Global THK at file level. Excessive care should be taken to ensure that references from other THKs are not altered in such a way that it breaks the expectations of the callers. Additionally it might prove usefull when updating non-Leviathon edited THKs as it ensures node ids are kept consistent.
-noActions
Does not resolve actions to the monster scope declared on the file but keeps them as Action Literals. Not recommended for decompiling in-game THKs but proves useful when dealing with non-standard edits made without any preservation of the format specification.
-raiseInvalidReferences
Stops decompilation if an invalid call dereference is found. Useful to determine if the file one is decompiling will function or re-compile properly.
-warningsHide
Hide warning comments generated by the Decompiler when fixing issues with the original file during the decompilation process.
-keepRegisters
Writes a hardcoded assignment of each register variable into the FAND file. This ensures that registers will compile to the same letter when being written back. Generally should not be used.
-renameNackFiles
Names the resulting NACK files to the function they have on the THKLST, for example instead of em000_55.nack it would produce Global.nack.
-analyze
-analysisOutputPath
Produces a code analysis report at the specified folder path with the filename of analysis.txt.
-outputPath
Specificifies the folder where the decompilation process will output files.
-fexty
Allows inputing a custom .fexty file for resolving function ids into function names and argument structure.
positional arguments:
input Project file to compile (.fand)
optional arguments:
-h, --help show this help message and exit
-verbose Print intermediate compilation process information
-display DISPLAY Output compilation reports to the given file
-monLib MONLIB Override Default Monster Library
-fexty FEXTY Override Default Function Resolver (.fexty)
-thkNames THKNAMES Override Default THK Names
-directForeign Use Direct Reference to Foreign Imports instead of inlining
-inlineGlobal Inline Global Functions
-projectNames {function,nackFile,index}
Function: Outputs the thks names as their scope name.
Nackfile: Outputs the thks names as the name of the nackfile.
Index: Outputs the thks names as their index in the thklst
-preprocessor PREPROCESSOR
[Non-Standard] Run the macro prepropcessor
-symbols Export Debugging Symbols
-skipVerify Skip internal compiler state verification during compilation
-forceCritical Convert all errors into critical errors that automatically stop compilation
-forceError Convert all warnings into errors
-repeatedProperty REPEATEDPROPERTY
Error level for repeated properties [Warning,Error,CriticalError]
-outputName OUTPUTNAME
Output THKList Name
-outputRoot OUTPUTROOT
-verbose
Outputs all compilation intermediate phases.
-display
Set a file as output for ccompiler messages.
-monLib
Set a folder with custom monster action listings to be used as the monster library during compilation.
-fexty
Set a custom FEXTY File to resolve function names and argument positions.
-thkNames
Overrides the default THKLST index to function mapping with a custom one.
-directForeign
Treat internal imports from non-Global nodes as external calls, reeplacing the inlining operation to substituting it for an external call with the THK Index as its External THK ID and the node ID as External Node ID.
-inlineGlobal
Treat calls to the Global THK as inline functions. Performing the inlining operation instead of resolving to a single segment call.
-projectNames
Options:
Function
Names the output THK to the THKLST context connected to the THK. Example: Global.thk
Nackfile
Names the output THK after the NACK file used to compile it.
Index
Names the output THK after the output THKLST and the THKLST index of the node. Example: em001_02.thk
-preprocessor
Executes the Macro preprocessor on all of the NACK files before performing the compilation. Details on the Preprocessor
-symbols
Exports debugging symbols for use with the THK Debugger. This enables line number on nodes, and readable segment names.
-skipVerify
Skips the compiler internal state verification which ensures there's no broken inheritance produced through erroneous in-lining or invalid internal states that result in suppressing errors. Skipping verification improves performance but might hide compilation errors.
-forceCritical
Treats all errors as critical errors and immediately stops compilation on them.
-forceError
Treats all warnings as errors.
-repeatedProperty
Set the error level for property repetition (due to metadata entries adding fields that already exist on the segment.
-outputName
Set the output thklst name. Defaults to em000.thklst
.
-outputRoot
Set the folder where the compiled files will be output. Defaults to the folder with the FAND File.
Macros are an optional aspect of the language that are not part of the langage specification and are providedd as a convenience. Macro preprocessing must be specified as a compile option.
Macros consists of macro declarations and replacement points.
Declarations are structured as:
~~macroName(argumentName0,...,argumentNameN) ! replacementFormat
The corresponding invocation is structured as:
contextLeft ~macroName(argumentName0,...,argumentNameN) contextRight
Where macroName is the name for the macro which will be used to invoke them in further lines. Argument Names are used as replacement mappings in the replacementFormatString. The Left and Right Context are kept intact.
Example:
~~wait(WAITTIME) ! def waitNode_WAITTIME \[WaitVar ##]\>>waitLoop_WAITTIME\return\endf\def waitLoop_WAITTIME \[WaitVar #-] return\repeat\endf
~wait(50)
~wait(100)
~wait(150)
def startNode
-> monster.take_off >> waitNode_50
-> monster.land >> wait_Node_150
endf
Resolves to
~~wait(WAITTIME) ! def waitNode_WAITTIME \[WaitVar ##]\>>waitLoop_WAITTIME\return\endf\def waitLoop_WAITTIME \[WaitVar #-] return\repeat\endf
def waitNode_50
[WaitVar ##]
>>waitLoop_50
return
endf
def waitLoop_50
[WaitVar #-] return
repeat
endf
def waitNode_100
[WaitVar ##]
>>waitLoop_100
return
endf
def waitLoop_100
[WaitVar #-] return
repeat
endf
def waitNode_150
[WaitVar ##]
>>waitLoop_150
return
endf
def waitLoop_150
[WaitVar #-] return
repeat
endf
def startNode
-> monster.take_off >> waitNode_50
-> monster.land >> wait_Node_150
endf
The macro system is extremely rudimentary. It performs a direct replcement of all ocurrences of argument names in the replacement format. Because the argumetn names are used as a string replacement, it's recommended to make them sufficiently unique to avoid spuriours replacements. Macro evaluation occurs simultaneously on all arguments, as a result, the result of an earlier argument will not be a match for succesive arguments.
The THK Low-Level specification does not have a notion of imports. It's only external node option is when calling nodes from the Global THK, as a result, to be able to implement imports it's necessary to either extend the game or perform an inlining procedure. Inlining consists of making a copy of the node in the file performing the import. However this also requires importing ALL of the nodes on which this node depends.
The Inlining pass is a recursive depth first procedure where inlines are resolved bottom up. On each step it resolves Caller scope identifiers and calls. If multiple files import a single file, the inlining procedure will treat each as a separate file, as a result multiple files with a single dependency do not crosscontaminate on caller scope resolution.
Its possible to explicitly set node id and indices. Explicit Indices are used to pre-fill the node list on a THK and the remaining nodes fill around them. Similarly explicit IDs are used to seed the automatic id generator. In the case of ID and index collisions an exception will be raised.
Registers can be harcoded to specific letters on the FAND File. During compilation the FAND File is parsed first for hardcoded register mappings and register names. Then all of the FAND File NACK files are parsed to extract register identifiers. From the complete list each register is assigned an unused letter. In case there are more registers than there are possible letters the compiler outputs an error.
FAND Files are project orchestrators which specify the mapping of THK to concrete game situations. The function on the ith slot is consistently used for the same purpose in most cases. This is why Global THK are often referred as THK 55, as this is the index in the THKLST which determines what file is used for the Global Table.
FAND Files consist of an optional Chunk Path, followed by a mandatory Relative Path, followed by an optional Monster Specifier, followed by an optional list of Register Declarations, followed by a list of Scope Resolution Entries and finally an optional Entry Count.
Example:
at D:\Games SSD\MHW\chunk
through em\em121\00\data
is Behemoth
Register AreaChangeCommunication
Register GlobalVar0 as $B
Combat_Main = tu0Event/em121_00.nack @ 1131A51E
THK_06 = em121_03.nack @ 0A3FC6CC
Rage_Enter = em121_07.nack @ 0A3FC6CC
Global = em121_55.nack @ 54F7F8EC
has 72 entries
Optionally specifies the chunk location. Discarded by the compiler, mostly for documentation purposes. Prepended by the keyword at
Specifies the path to prepend to all of the NACK File paths during compilation. Corresponds to the relative path to the highest common folder between the THKLST and all of its summoned THKs. Prepended by the keyword through
.
Optionally specifies to which monster action resolution scope all of the generic monster
scope within the NACK files will resolve. Prepended by is
Optionally reserves identifiers as registers, optionally can hardcode the target register letter trough the keyword as
and prepending the dollar sign $
to the register letter. Prepended by the keyword Register
. Example: Register RegisterVar4 as $V
A scope resolution entry maps a NACK File to a THKLST entry. Consists of either a known context or an indexed THK identifier of the form THK_%02d
, followed by an equality sign =
, followed by the implementation path, and optionally a metadata specifier prefaced by the at sign @
. At the time it is not known what this metadata specifier does. The metadata specifier is in hexadecimal by default.
Example: THK_29 = em121_29.nack @ 8A8254DB
, Global= em092/05/data/em092_55.nack @ 8A8254DB
The list of known contexts:
THK_00 = Combat_Main
THK_01 = Combat_Enter
THK_02 = Search
THK_03 = Discover
THK_07 = Rage_Enter
THK_09 = Exhaust_Enter
THK_10 = Exhaust_End
THK_11 = Mount
THK_15 = Non_Combat_Main
THK_17 = Combat_Area_Change
THK_18 = Non_Combat_Hit
THK_20 = Combat_Critical_HP
THK_23 = Combat_Turf_War_A
THK_36 = Combat_Blinded
THK_37 = Combat_Blinded_Recovery
THK_50 = Combat_Turf_War_B
THK_55 = Global
Optionally specifies the size of the THKLST list (filling empty spaces with empty paths). Prepended by the keyword has
, optionally can have the keyword entries
after but the compiler discards it regardless.
FEXTY Files provide function resolution mechanisms through a simple syntax. The language is kept relatively simple but highly extensible. Permitting strong expressive constructions for use with Leviathon.
FEXTY Files consist of an optional numerical type specification (Use hex will cause all values in the file to be read as hexadecimal without a starting prefix), followed by a series of function id resolution rules.
Using a substitute FEXTY file will completely replace the default (not extend). So if you want to add functionality you will have to start from the default file (found here) and add your extensions from there. You might also modify existing entries if you want to re-define elements of the function call syntax or targets.
A function string consists of identifiers optionally followed by a matched parenthesis pair with either comma separated replaceable arguments or a dummy identifier inside, which is in turn followed by a dot .
.
Example: distance_3d_to_target().leq(parameter1)
, target_em.random_player_or_cat
A replaceable argument is any THK Field Name. During ccompilation this are treated as formal arguments and will accept numeric values and evaluate the relevant field to the value passed as argument on the given position.
Its possible to specify special enum elements. This translate a parameter to a scoped identifier during decompilation and a scoped identifier to an integer during compilation. This are expresed through a matching brackets expression with the scope separated from the THK Field by a colon :
. The 3 special translation elements are: em_enum and st_enum. Example: in_map({st_enum:parameter1}).in_area(parameter2)
Dummy identifier are strings not meant to resolve to anything and simply acting as syntactic sugar for function decompilation and compilation. Example: current_quest().is_rank(rank.HR)
An inline rule consists of the function id in the specified numerical format continued by a colon :
and followed by a single function string.
Example: 1A: angular_1A(parameter1,parameter2)
A rule consists of the function id in the specified numerical format, continued by a bracket which starts a scope which must be evnetually closed. The scope consists of an optional prefix which will be included in all of the successive function strings, followed by a series of resolution rules. Example:
3{
>targetEnemy
parameter1 == 1 : (target_em.random_player_or_cat)
parameter1 == 2 : (target_em.closest_entity)
parameter1 == 13 : (target_em.last_attacker)
parameter1 == 29 : (target_em.any_monster)
parameter1 == 41 : (target_em.nearest_monster)
parameter1 == 66 : (target_em.last_target)
otherwise : (parameter1)
}
Which produces the following rules:
self.targetEnemy(target_em.random_player_or_cat)
self.targetEnemy(target_em.closest_entity)
self.targetEnemy(target_em.last_attacker)
self.targetEnemy(target_em.any_monster)
self.targetEnemy(target_em.nearest_monster)
self.targetEnemy(target_em.last_target)
self.targetEnemy(parameter1)
Where all of the functions except the last are basically literals, and the last takes a single argument which is then assigned to the parameter1 field on compilation.
A resolution rule consists of comma separated list of pairs of THK Field Name and a numerical value, separated by the equality comparison ==
or the fallthough rule otherwise
which catches any set of values not specified by previous rules. Reoslution rules are used during decompilation to map function id and segment parameters into a function string, and during compilation to map functions into their low level binary representation. Example parameter1 == 1 , parameter2 == 0
, otherwise
The following is the list of all function rules provided by the default fexty file:
self.targetEnemy(target_em.random_player_or_cat)
self.targetEnemy(target_em.closest_entity)
self.targetEnemy(target_em.last_attacker)
self.targetEnemy(target_em.any_monster)
self.targetEnemy(target_em.nearest_monster)
self.targetEnemy(target_em.random_player)
self.targetEnemy(target_em.last_target)
self.targetEnemy(arg:parameter1)
self.targetUnknown(arg:parameter1,arg:parameter2)
self.targetArea(target_area.nearest_entrance)
self.targetArea(target_area.next_exit)
self.targetArea(target_area.global_center)
self.targetArea(target_area.area_center)
self.targetArea(target_area.area_aerial_center)
self.targetArea(target_area.nearest_monster_area)
self.targetArea(target_area.unknown_aerial)
self.targetArea(target_area.unknown_ground)
self.targetArea(arg:parameter1)
self.distance_3d_to_target().leq(arg:parameter1)
self.distance_3d_to_target().gt(arg:parameter1)
self.distance_2d_to_target().leq(arg:parameter1)
self.distance_2d_to_target().gt(arg:parameter1)
self.distance_3d_recalc_to_target().leq(arg:parameter1)
self.distance_3d_recalc_to_target().gt(arg:parameter1)
self.distance_2d_recalc_to_target().leq(arg:parameter1)
self.distance_2d_recalc_to_target().gt(arg:parameter1)
self.vertical_distance_to_target().leq(arg:parameter1)
self.vertical_distance_to_target().gt(arg:parameter1)
self.above_target()
self.below_target()
self.above_area()
self.below_area()
self.angle_2d_ccw_between(arg:parameter1,arg:parameter2)
self.angle_2d_cw_between(arg:parameter1,arg:parameter2)
self.angular_18(arg:parameter1,arg:parameter2)
self.angular_1A(arg:parameter1,arg:parameter2)
self.angular_15(arg:parameter1,arg:parameter2)
self.angular_17(arg:parameter1,arg:parameter2)
self.angular_19(arg:parameter1,arg:parameter2)
self.angular_1B(arg:parameter1,arg:parameter2)
self.in_combat()
self.alert_out_of_combat()
self.enraged()
self.fatigued()
self.poisoned()
self.defense_downed()
self.miasmaed()
self.hookable()
self.target_on_part(arg:parameter1)
self.mounted()
self.mount_finisher_ready()
self.mount_stabbed()
self.mount_staggered_twice()
self.target.pinned()
self.enrage_time_left().leq(arg:parameter2)
self.fatigue_time_left().leq(arg:parameter2)
self.hp_percent().leq(arg:parameter2)
self.target.helpless_0()
self.target.helpless_1()
self.target.in_combat()
self.target.poisoned()
self.target.deadly_poisoned()
self.target.bleed()
self.target.blast_blighted()
self.target.paralyzed()
self.target.stunned_0()
self.target.sleeping()
self.target.fire_blighted()
self.target.thunder_blighted()
self.target.water_blighted()
self.target.ice_blighted()
self.target.dragon_blighted()
self.target.defense_downed()
self.target.stunned_1()
self.target.miasmaed()
self.target.mudded()
self.target(arg:parameter1)
self.target_is(em_enum:parameter1)
self.quest_id(arg:parameter2)
self.flying()
self.part(arg:parameter1).is_broken(arg:parameter2)
self.force_area_change()
self.force_area_change2()
self.current_quest().is_rank(rank.LR)
self.current_quest().is_rank(rank.HR)
self.current_quest().is_rank(rank.MR)
self.current_quest().is_rank(rank.AT)
self.in_map(st_enum:parameter1).in_area(arg:parameter2)
The following functions correspond to the ETL:
self.heal(arg:parameter2)
self.heal_perdecmil(arg:parameter2)
self.damage(arg:parameter2)
self.damage_perdecmil(arg:parameter2)
self.hp.return_rax()
self.max_hp.return_rax()
self.stamina.increase(arg:parameter2)
self.stamina.increase_perdecmil(arg:parameter2)
self.stamina.return_rax()
self.enrage()
self.refresh_enrage()
self.corpseDuration(arg:parameter2)
self.corpseDuration.return_rax()
General Tutorials
Animation Tutorials
Audio Tutorials
File & In Game IDs
- Accessory and Specialized Tool IDs (Asset)
- Armor IDs (Asset)
- Decorations IDs
- EFX IDs
- Endemic Critter IDs (Asset)
- Face IDs (Asset)
- Furniture IDs (Asset)
- Gimmick IDs (Asset)
- Hairstyle IDs (Asset)
- Item IDs
- LMT IDs
- Material IDs
- Medal IDs
- Model Bone Function IDs
- Monster IDs
- Monster Shell IDs (A-P)
- Monster Shell IDs (Q-Z)
- NPC IDs (Asset)
- NPC Base Model (Asset)
- Palico Equipment IDs (Asset)
- Pendant IDs (Asset)
- Poogie Clothes IDs
- Quest IDs
- Skill IDs
- Stage IDs (Asset)
- Player Weapon Action IDs
- Weapon IDs (Asset)
- Weapon ID Dump (GS,SnS,DB)
- Weapon ID Dump (LS,Ham,HH)
- Weapon ID Dump (Lan,GL)
- Weapon ID Dump (SA,CB)
- Weapon ID Dump (Bow,HBG,LBG)
Model Tutorials
- Quick Guide to Importing Models (Blender 2.79)
- Walkthrough of a Weapon Import
- Basics of Exporting Into the .mod3 Format
- How To Fix UVs Sharing a Seam
- How To Separate Mesh Parts in Blender
- Rotating, Moving and Resizing in Blender
- How to Split a Single Mesh Outfit into Player Equippable Parts
- Jigglebone Chains (.ctc) and Colliders (.ccl)
- Axial CTC Rotations
- Editing Hair Models and Materials
- Useful Blender Scripts
- [external page] How to Make All Polygons Into Triangles (Required for MHW models)
- [external page] How to Convert Triangles Back Into Quads
- [external page] How to Change The View-port clipping Parameters For Large Models
- [external page] How to Set Origin to Vertex in Edit Mode
- [external page] Shortcut to repeat the last operation in Blender
- [external page] Transferring Rig Weights From One Mesh To Another
- [external page] How to Copy Paint Weights From One Object to Another
- [external page] How to Remove All Zero-Weight Vertex Groups
- [external page] How to Weight Paint Against Current Pose
- [external page] Making a Hair Rig
- [external page] Physics Transfer
EFX Tutorials
FSM Editing
MRL3 Tutorials
NPC Editing
Plugins and Memory Editing
Monster AI Editing
General Texture Tutorials
- Obtaining, Converting and Replacing Textures
- Textures with Paint NET
- How To Open DDS Files
- Editing Textures
- Understanding Alpha Channels
- Exporting Textures with Transparency
- How To Achieve Glass Texture
- How to create Crystal materials with Alpha Textures
- Working Around the Mip Map Loading Bug
- [external page] Extracting a UV Layout
Specific Texture Tutorials
Asterisk's Plugin Notes
Miscellaneous Tutorials
Outdated Tutorials
- How to Make an NPC Skin Material Support Skin Color Customization
- Plugin Comparison
- A Theoretical Proposal on Skeleton Extension
- Monster Hunter World Save Editor Tutorial
- Making Copies of a Save Slot
- Making a Green Screen
- Notes on CTC Physics Jiggle Files
- Opening MRL3 file with a template
- Transferring MRL3 Files Between Models
- Expanding mrl3 Reference List
- How to Edit Eye Color in mrl3 Files