WeiDU DocumentationFredrik Lindgren, a.k.a. Wisp |
The main home page for WeiDU is: http://weidu.org/. I encourage you to download the latest version.
WeiDU is designed to make it easier to write and distribute modifications to Infinity Engine games. It can load and modify Infinity Engine resources according to instructions you provide. WeiDU is ideal for packaging modifications that include dialogue or that want to be compatible with other modifications.
I’ll be honest with you up front: WeiDU is initially harder to use than some of its alternatives. However, most users report that (1) the alternatives are insufficient because they lack features that only WeiDU provides and (2) WeiDU grows on you over time.
You are welcome to use these utilities to make and distribute your own Infinity Engine mods. This utility is covered by the GNU General Public License, but you are also allowed to distribute an unmodified binary copy of WeiDU.EXE (without the source code) with your mod if you like.
I decided to write my own Infinity Engine DLG and TLK utilities because I was unable to get the TeamBG DLG Editor and Mass Converter to work properly. Either they wouldn’t parse the strings or they would mangle the text or they would randomly crash ... it was bad all around. Also, they were all GUIs. As a unix weenie I’m in love with command line utilities and as a PL doctoral student I love making little languages and compilers. WeiDU was originally a family of small programs with unimaginative names like DC, DD and TP. The more appealing term “WeiDU” (which rhymes with “IDU”, Eye-Dee-You) was coined by Jason Compton and Ghreyfain, noted BGII mod authors.
Step-By-Step Beginner’s Guide to WeiDU:
This section is a gentle introduction to how Infinity Engine DLG files are structured. First, let’s use WeiDU to create SCSARLES.D and take a look at the dialogue of Sir Sarles.
You may install WeiDU.exe anywhere on your system. However, I recommend that you put it in your Baldur’s Gate 2 installation directory. However, WeiDU will use the Windows Registry to attempt to locate your BG2 game files.
To run the effect described, open up a DOS prompt window and change directories to get to your BGII directory. Then just type in the text in red at the DOS Prompt.
C:\Program Files\Black Isle\BGII - SoA\> weidu SCSARLES.DLG
This will create a text file called SCSARLES.D in the current directory. Open it up with Notepad or Microsoft Word or something. It’s just a text file that describes the game dialogue.
It will look something like:
// creator : c:\bgate\weidu\weidu.exe // argument : SCSARLES.DLG // game : C:\Program Files\Black Isle\BGII - SoA // source : C:\Program Files\Black Isle\BGII - SoA\data\Dialog.bif // dialog : C:\Program Files\Black Isle\BGII - SoA\DIALOG.TLK // dialogF : (none) BEGIN ~SCSARLES~ IF ~NumTimesTalkedTo(0)~ THEN BEGIN 0 // from: SAY #28655 /* ~Who is it? Might I ask why you have disturbed my meditations? My creative muse must be gently awakened, and your stomping about is simply not conducive to this.~ [SARLES02] */ IF ~~ THEN REPLY #28656 /* ~My apologies. I will leave you to your thinking.~ */ GOTO 1 IF ~~ THEN REPLY #28657 /* ~I apologize, but I have come to request your talent on a commissioned artwork.~ */ DO ~SetGlobal("TalkedToSarles","GLOBAL",1)~ GOTO 2 END IF ~~ THEN BEGIN 1 // from: 0.0 SAY #28661 /* ~Then I shall forget you were ever here. Actually, it is an astoundingly easy thing to do.~ */ IF ~~ THEN DO ~SetNumTimesTalkedTo(0)~ EXIT ENDDialogues in Infinity Engine games behave like finite state machines. If you aren’t familiar with the concept of a finite state machine, see http://whatis.techtarget.com/definition/0,,sid9_gci213052,00.html or http://www.c3.lanl.gov/mega-math/workbk/machine/mabkgd.html. Each block of the form:
IF ~Initial Condition~ THEN BEGIN state1 SAY ~Something~ IF ~Reply Condition~ THEN REPLY ~Reply Text~ GOTO state2 ENDrepresents a state (more details below). When the player starts a conversation with an NPC, the game engine scans through all of the states in that NPC’s DLG file in a special WEIGHTed order and picks the one with a non-empty and true "Initial Condition". If no state has a non-empty and true "Initial Condition" then you get that “Bob - has nothing to say to you.” message. Don’t worry about the weighting process for now.
The speaker (in this case, Sir Sarles) then says whatever appears after SAY. The REPLY lines represent responses the PC can say back. If the "Reply Condition" is true, the player is given the option of saying the "Reply Text" and moving to another state in the dialogue (where Sarles will probably say something else).
Remember: SAY is for what the NPC says, REPLY is for what the player says back. If you think carefully, you’ll notice that all dialogue in Infinity Engine games is structed in this manner.
Conditions use the same syntax as triggers do in Infinity Engine BCS scripting. You will need to learn Infinity Engine scripting before too long. Strings are delineated by tildes or %% or "" (your choice, but WeiDU uses the tilde by default). After SAY or REPLY or JOURNAL you may give two Strings instead of one. The first is used with DIALOG.TLK, the second is used with DIALOGF.TLK (foreign language version for when the main character is female). If you do not give two Strings, the one String you gave is used for both.
You may also use raw numbers prefaced with a number sign (like #1234) to specify a strref inside DIALOG.TLK directly. This is useful when modifying existing dialogues (say, the Fate Spirit in ToB) so that you if a foreign user installs your dialogue they will retain all of the foreign versions of the strings you didn’t change or add. Normally the string reference numbers are put right after the SAY keyword and the string text is put in comments. The --text command-line option causes string text to be emitted with the string number in comments.
You may also indicate that a sound file (WAV/WAVC) should be associated with a given String by including its up-to-8-letter resource name in [brackets] after the string, as in:
SAY ~Hello~ [HELLO]Comments are C/C++ style: everything from // to the end of the line is a comment, as is /* everything in these star-slash things */. Comments are ignored by WeiDU. They are there for your benefit. Example:
SAY ~Hello~ [HELLO] // this is a comment ... way out to here! IF /* this is also a comment */ ~~ THEN EXITReplies can also contain actions (using the DO keyword) which behave just like Infinity Engine BCS script actions. They can also add Journal entries, end the dialogue or transfer to another speaker.
Examples:
IF ~~ THEN BEGIN 2 // from: 0.1This line marks the beginning of state 2 in a dialogue. The comment tells you that it can be reached by the first reply transition from state 0.
IF ~~ THEN REPLY ~My apologies. I will leave you to your thinking.~ // #28656 GOTO 1This REPLY can always be chosen and involves the spoken text "My apologies...". That text is string reference number 28656. If the PC chooses that reply, it transitions to state 1.
Finally, a transition may also take the form:
COPY_TRANS filename labelDuring processing, COPY_TRANS will be replaced by all of the transitions from state "label" of file "filename". The copying takes place before all other D actions.
The D file format is a way of describing Infinity Engine dialogues and modifications to Infinity Engine Dialogues in a portable, easy-to-understand format. It supports foreign language translations and allows you to describe extensions to existing game dialogues without forcing you to describe their content. This allows you to write mods that work with mods written by others.
The D file format is presented here in an extended context-free grammar notation. If you are unfamiliar with CFGs, take a look http://www.wikipedia.com/wiki/Context-free_grammar, http://cs.wpi.edu/~kal/PLT/PLT2.1.2.html or http://www.cs.rochester.edu/users/faculty/nelson/courses/csc_173/grammars/cfg.html. You don’t really need to understand a CFG formally, though.
To get a real idea of how they work, use WeiDU to create JAHEIRA.D for yourself and look at it in a text editor. You can also browse the examples and test directories that come with WeiDU.
All of the syntax keywords are given in a UPPERCASE COURIER. All other keywords are symbolic. Notes:
D File | A D File is a text file that contains a number of D Actions. D Files tell WeiDU how to create and modify Infinity Engine DLG files. | |
is | D Action list | A D File is a list of D Actions. Typically the first and only one is BEGIN, which defines the content of a new dialogue. Other D Actions can be used to modify existing dialogues. |
D Action | A D Action tells WeiDU how to create or modify Infinity Engine DLG files. | |
is | BEGIN filename [ nonPausing ] state list | BEGIN tells WeiDU that you are creating a new DLG file from scratch. Any existing DLG file with the same name will be overwritten. The new DLG file contains exactly the states in the list. If you set nonPausing to a non-zero integer, the game will not “stop time” while the conversation takes place. By default time stops during conversations. |
or | APPEND [ IF_FILE_EXISTS ] filename state list END | This tells WeiDU to place the given states at the end of the already-existing dialogue filename.DLG. If there is IF_FILE_EXISTS and the file doesn’t exists, this action is skipped. |
or | APPEND_EARLY [ IF_FILE_EXISTS ] filename state list END | Works like APPEND, but the states are added early on in the compilation timeline (just after BEGIN is processed). Thus they can be the targets for INTERJECT_COPY_TRANS and friends. |
or | CHAIN [ IF [ WEIGHT #weight ] stateTriggerString THEN ] [ IF_FILE_EXISTS ] entryFilename entryLabel chainText list chainEpilogue | This instructs WeiDU to make a long conversation in which the PC can say nothing. This is useful when you want the NPCs to talk among themselves for a long time. It and its friends, INTERJECT and INTERJECT_COPY_TRANS can incredible time-savers when you’re writing non-trivial dialogue. See the examples for ideas. CHAIN will only append to existing dialogues. You cannot use CHAIN to create a new DLG. If the entryFilename file is missing and there is IF_FILE_EXISTS, the whole CHAIN is not compiled. |
or | INTERJECT entryFilename entryLabel globalVariable chainText list chainEpilogue | Behaves like CHAIN except that all of the chainText is additionally guarded by the transition predicate Global("globalVariable","GLOBAL",0) and accompanied by the action SetGlobal("globalVariable","GLOBAL",1). If you pick globalVariable to be unique, this will ensure that the chainText is only ever seen once per game. This is useful for making interjections. |
or | INTERJECT_COPY_TRANS [ SAFE ] entryFilename entryLabel globalVariable chainText list | This behaves just like INTERJECT except that the exitFilename and exitLabel are not present. Instead, whenever the dialogue would pass out of the chainText it follows a copy of the transitions that were at the state with stateLabel originally. This is convenient for making quick interjections from NPCs that do not actually change the true flow of the conversation. See the transition COPY_TRANS and the INTERJECT_COPY_TRANS tutorial for more information about this idea. If SAFE is specified, INTERJECT_COPY_TRANS will not warn about potentially unsafe uses (use this if you know it is safe). |
or | INTERJECT_COPY_TRANS2 [ SAFE ] entryFilename entryLabel globalVariable chainText list | This works just like INTERJECT_COPY_TRANS, except that any actions taken in the transitions of the state specified by entryFilename and entryLabel are preserved and kept with the speaker associated with entryFilename (rather than being mistakenly performed by your new speaker). See the INTERJECT_COPY_TRANS2 tutorial. |
or | INTERJECT_COPY_TRANS3 [ SAFE ] entryFilename entryLabel globalVariable chainText list | This works just like INTERJECT_COPY_TRANS, except that all states in chainText get a link in the entry state, rather than only the first one. Expecting documentation. |
or | INTERJECT_COPY_TRANS4 [ SAFE ] entryFilename entryLabel globalVariable chainText list | This is either INTERJECT_COPY_TRANS3 with INTERJECT_COPY_TRANS2-style action handling, or INTERJECT_COPY_TRANS2 with the extended INTERJECT_COPY_TRANS3 state creation rules, depending at how you look at it. |
or | EXTEND_TOP filename stateLabel list [ #positionNumber ] transition list END | This instructs WeiDU to add the transitions in list to the top of
the transition list for the specified states in filename.DLG
(which must already exist). If a positionNumber is given, WeiDU to insert the transitions just between already-existing transitions #positionNumber and #positionNumber+1 in the given states for the given file. The first transition is number 1. |
or | EXTEND_BOTTOM filename stateNumber list [ #positionNumber ] transition list END | Behaves just like EXTEND_TOP but adds the transitions to the bottom of the list instead. |
or | ADD_STATE_TRIGGER filename stateNumber stateTriggerString [ stateNumber list ] dActionWhen list | This instructs WeiDU to add the stateTriggerString to all of the states with the given stateNumbers in the file filename.DLG (which must already exist). This is handy for adding extra conditions to an existing dialogue state. |
or | ADD_TRANS_TRIGGER filename stateNumber transTriggerString [ moreStateNumbers list ] [ DO transNumber list ] dActionWhen list | This instructs WeiDU to add the transTriggerString to all of the transitions in all of the states with the given stateNumbers in the file filename.DLG (which must already exist). This is often used in conjunction with EXTEND_BOTTOM to make a new branch in an existing state. Use ADD_TRANS_TRIGGER to add the negation of some predicate to all of the existing transitions, then use EXTEND_BOTTOM to add a transition with that predicate to that state. If a list of transNumbers is specified, only those transitions will have transTriggerString added to them. If such a list is not specified, every transition in every specified state will be modified. Note that the “first” transition is number 0. |
or | ADD_TRANS_ACTION filename BEGIN stateNumber list END BEGIN transNumber list END transActionString dActionWhen list | This instructs WeiDU to add the transActionString to all of the actions in all the transitions in all of the states specified by the stateNumber list and the transNumber list. You may use state labels in the stateNumber list. If the transNumber list is empty, the text added to all transitions on all listed states. Note that the BEGIN and END keywords must be present, even if you specify an empty list of transNumbers. The “first” transition is number 0. Any out-of-bounds transNumbers are silently ignored. The transActionString is prepended to any existing action text on a per-transition, per-state basis. |
or | REPLACE_TRANS_ACTION filename BEGIN stateNumber list END BEGIN transNumber list END oldText newText dActionWhen list | This instructs WeiDU to replace all instances of oldText in newText to all of the actions in all the transitions in all of the states specified by the stateNumber list and the transNumber list. You may use state labels in the stateNumber list. If the transNumber list is empty, the text added to all transitions on all listed states. Note that the BEGIN and END keywords must be present, even if you specify an empty list of transNumbers. The “first” transition is number 0. Any out-of-bounds transNumbers are silently ignored. |
or | REPLACE_TRANS_TRIGGER filename BEGIN stateNumber list END BEGIN transNumber list END oldText newText dActionWhen list | This instructs WeiDU to replace all instances of oldText in newText to all of the triggers in all of the transitions in all of the states specified by the stateNumber list and the transNumber list. You may use state labels in the stateNumber list. If the transNumber list is empty, the text added to all transitions on all listed states. Note that the BEGIN and END keywords must be present, even if you specify an empty list of transNumbers. The “first” transition is number 0. Any out-of-bounds transNumbers are silently ignored. |
or | ALTER_TRANS filename BEGIN stateNumber list END BEGIN transNumber list END BEGIN changeWhat changeInto list END | Fine altering of a single transition. See the ALTER_TRANS tutorial. |
or | REPLACE filename state list END | This instructs WeiDU to load filename.DLG and replace some of its states with the new ones described in the state list. All of the states should have numeric stateLabels (e.g., "5" or "67"). A new state with label X will replace the old state number X. |
or | SET_WEIGHT filename stateLabel #stateWeight | This instructs WeiDU to destructively change the WEIGHT of the given state in filename.DLG (which must exist). This should only be used to patch or workaround existing dialogues. Never use SET_WEIGHT if you can help it. |
or | REPLACE_SAY filename stateLabel sayText | This instructs WeiDU to destructively change the sayText of the given state in filename.DLG (which must exist). This should only be used to patch or workaround existing dialogues. Never use REPLACE_SAY if you can help it. |
or | REPLACE_STATE_TRIGGER filename stateNumber stateTriggerString [ stateNumber list ] dActionWhen list | This instructs WeiDU to destructively set the stateTriggerString of all of the states with the given stateNumbers in the file filename.DLG (which must already exist). It should be used with caution. |
or | REPLACE_TRIGGER_TEXT filename oldText newText dActionWhen list | This instructs WeiDU to destructively replace every occurrence of oldText (which may be a regexp) in the stateTriggerStrings and transTriggerStrings of filename.DLG (which must exist). This should only be used to patch or workaround existing dialogues. Never use this if you can help it. |
or | REPLACE_TRIGGER_TEXT_REGEXP filenameRegexp oldText newText dActionWhen list | Just like REPLACE_TRIGGER_TEXT but the filename is a regexp. The .DLG is implied. |
or | REPLACE_ACTION_TEXT filename oldText newText [ moreFilenames ] dActionWhen list | This instructs WeiDU to destructively replace every occurrence of oldText (which may be a regexp) in the stateActionStrings of filename.DLG (which must exist). This should only be used to patch or workaround existing dialogues. Never use this if you can help it. |
or | REPLACE_ACTION_TEXT_REGEXP filenameRegexp oldText newText [ moreFilenameRegexps ] dActionWhen list | Just like REPLACE_ACTION_TEXT but the filenames are regexps. The .DLG is implied, do not include it in your regexps. |
or | REPLACE_ACTION_TEXT_PROCESS filename oldText newText [ moreFilenames ] dActionWhen list | This instructs WeiDU to destructively replace every occurrence of oldText
(which may be a regexp) in the stateActionStrings
of filename.DLG (which must exist) with newText. However,
newText is first compiled as a BAF action list. In particular,
this means that replacing with commands like:
~DisplayString(Myself,@123)~... will do what you expect. This should only be used to patch or workaround existing dialogues. Never use this if you can help it. |
or | R_A_T_P_R filenameRegexp oldText newText [ moreFilenameRegexps ] dActionWhen list | Just like REPLACE_ACTION_TEXT_PROCESS, but the filenames are regexps. The .DLG is implied. R_A_T_P_R is shorthand for REPLACE_ACTION_TEXT_PROCESS_REGEXP (to avoid undue scrollbars in the readme on a 1024x768 monitor). |
dActionWhen | defines when a D action (E.G. ADD_TRANS_ACTION) will be processed or not. | |
is | IF String | The action is processed if the element being altered/replaced/added to contains a substring matching against the String regexp. |
or | UNLESS String | The action is not processed if the element being altered/replaced/added to contains a substring matching against the String regexp. |
chainEpilogue | Determines where the dialogue should flow at the end of the CHAIN. | |
is | END filename stateNumber | Transfer to the given state in the given dialogue file. |
or | EXTERN filename stateNumber | Transfer to the given state in the given dialogue file. |
or | COPY_TRANS [ SAFE ] filename stateNumber | At the end of the CHAIN text, copy all transitions from the given state in the given file. This is useful for interjections (see INTERJECT). If SAFE is specified, CHAIN will not warn about potentially unsafe uses of COPY_TRANS (use this if you know it is safe). |
or | COPY_TRANS_LATE [ SAFE ] filename stateNumber | Same as above, but only copy after all the other D Actions have been applied. |
or | EXIT | At the end of the CHAIN text, exit the dialogue. |
or | END transition list | Execute the given transitions after the final state in the CHAIN. |
state | In Infinity Engine games, this is the fundamental unit of dialogue. | |
is | IF [ WEIGHT #weightNumber ] stateTriggerString [ THEN ] [ BEGIN ] stateLabel SAY sayText [ = sayText ... ] transition list END | When you start conversing with a creature that uses a DLG file, the
Infinity Engine searches through all of the states in that file
in order of increasing WEIGHT and selects the first one it finds
for which the stateTriggerString is both true and not empty.
The creature then says all of the associated sayText. Finally,
the transitions are evaluated in bottom-up (i.e., reverse) order.
If a transition is found with a transTriggerString that
evaluates to True and no replyText, that transition is
immediately executed. Otherwise, all of the transitions are
presented as options to the PC. If a stateLabel is an integer it is called a stateNumber. All of the states in the DLG files that come with the original game use stateNumbers. Only D files use symbolic strings for stateLabels. Including more than one bit of sayText here is often called Multisay. Finally, once you are familiar with the syntax you may omit the THEN and BEGIN keywords if you like. |
or | APPENDI filename state list END | This is legacy syntax that behaves just like the D Action APPEND but is considered a state. Avoid it. |
or | CHAIN2 entryFilename entryLabel chain2Text list exitFilename exitLabel | This is legacy syntax that behaves somewhat like the D Action CHAIN but is considered a state. In addition, chain2Text is slightly different from chainText. Avoid this construction. |
sayText | sayText and replyText are displayed to the user as part of a dialogue. | |
is | text | sayText and replyText are both text. |
transition | Transitions determine how dialogue flows from one state to another. | |
is | IF transTriggerString [ THEN ] transFeature list transNext | If the transTriggerString evaluates to true or is empty, this transition is viable. If it contains no replyText within its transFeature list, it is immediately taken. Otherwise, the replyText is presented as an option to the user. If the transition is taken, any actions in the transFeature list are performed and the dialogue flows to the point indicated by the transNext. transitions are evaluated in "reverse order". That is, the "bottom" or "last" response for a state is checked first. If its transTriggerString evaluates to true and it has no REPLY text, that transition is immediately taken. See SAREV25A state 1 for an example of a state with all kinds of transitions. |
or | + [ transTriggerString ] + replyText transFeature list transNext | This abbreviated syntax for transitions that would contain REPLY (which is by far the most common case) allows you to save yourself some time and typing. It behaves like the full form above. |
or | COPY_TRANS [ SAFE ] filename stateLabel | This instructs WeiDU to copy all of the transitions from the
state with the given stateLabel in filename.DLG. This
copying takes place before all other D Actions. For example,
this is a valid transition list:
IF ~Before()~ THEN GOTO my_state COPY_TRANS PLAYER1 33 IF ~After()~ THEN EXTERN SOLA 55If SAFE is specified, COPY_TRANS will not warn about potentially unsafe uses (use this if you know it is safe). |
or | COPY_TRANS_LATE [ SAFE ] filename stateLabel | Same as COPY_TRANS, but is invoked after all other D Actions. |
transFeature | These are features or actions associated with taking a transition. | |
is | REPLY replyText | If this transition is taken, the PC says the replyText. |
or | DO stateActionString | If this transition is taken, the stateActionString is executed. |
or | JOURNAL text | If this transition is taken, the text is added to the PC’s journal. |
or | SOLVED_JOURNAL text | If this transition is taken, the text is added to the “solved” section of the PC’s journal. |
or | UNSOLVED_JOURNAL text | If this transition is taken, the text is added to the “unsolved” section of the PC’s journal. |
or | FLAGS integer | This allows you to set the features associated with a transition directly using the binary format of DLG files. |
transNext | This determines where dialogue flows after a transition has been taken. | |
is | GOTO stateLabel | The dialogue continues at the state with label stateLabel in the same DLG file as the current state. |
or | EXTERN [ IF_FILE_EXISTS ] filename stateLabel | The dialogue continues at the state with label stateLabel in the file filename.DLG. The whole transition is not compiled if there’s IF_FILE_EXISTS and the file filename doesn’t exist. |
or | EXIT | The conversation ends. |
or | + stateLabel | This is a synonym for GOTO. |
chainText | This is a rapid shorthand for chaining together many little bits of dialogue when the PC is not saying anything. | |
is | [ IF transTriggerString THEN ] sayText = sayText ... | |
followed by | [ == [ IF_FILE_EXISTS ] fileName [ IF transTriggerString THEN ] sayText = sayText ... ] | The == (that’s two consecutive equal signs) marks the beginning of a new speaker (indicated by fileName). If the transTriggerString is true or if it is not present, this new speaker says all of its sayText in order. If the IF_FILE_EXISTS part is present, these lines are not compiled at all if the current file is not missing. |
or followed by | BRANCH transTriggerString BEGIN [ == [ IF_FILE_EXISTS ] fileName [ IF transTriggerString THEN ] sayText = sayText ... ] END | As above, except that the first transTriggerString is appended to all existing dialogue units. |
text | This represents strings that are shown to the player, rather than strings that the game uses internally for predicates and actions. | |
is | String [ [WAVEFILE] ] | The given string is used for both male and female players. The optional [WAVEFILE] is the associated sound. |
or | String [ [WAVEFILE] ] String [ [WAVEFILE] ] | The first string and sound file are used if the PC is male, the second string and sound file are used if the PC is female. This is useful mainly for international versions of Infinity Engine games. |
or | !integer text | Forced String Reference. As with text in general, but rather than being assigned a new, previously-unused DIALOG.TLK string entry (or merging with an existing one that has the same text), this text is written over DIALOG.TLK string entry #integer. Do not use this without a real reason. |
or | reference | |
reference | This is a subset of text. | |
is | #integer | The string with reference number #integer from DIALOG.TLK should be used unchanged. |
or | @integer | The last definition of the translation string @integer given in any TRA file should be used. |
or | ( AT "var" ) | The last definition of the translation string (with value defined by a variable) given in any TRA file should be used. |
String | This is how you tell WeiDU what text you want shown to the player. For international mods or international translations, you may use any encoding you like (that is, you are not restricted to 7-bit characters or Latin-1 or anything like that). | |
is | "abcdef" | A string can be any sequence of characters not including a " that is enclosed in ""s. |
or | ~ abcdef~ | A string can be any sequence of
characters not including a ~ that is enclosed in
~~ s. |
or | %abcdef% | A string can be any sequence of characters not
including a % that is enclosed in %%s. This is handy for Big5
translations, since " and ~ can be part of Big5-encoded characters.
|
or | ~~~~~ abcdef~~~~~ | That’s five consecutive
tildes on each side. A string can be any
sequence of characters not including ~~~~~ that is enclosed in
~~~~~ s. For example,
string #8750 is
~!@#$\%^&*()_+-=[]{}\|;:'",<.>/?
and can be given to WeiDU as
~~~~~~!@#$\%^&*()_+-=[]{}\|;:'",<.>/?~~~~~
(the content of the string is shown in red for clarity).
|
or | String ^ String | String literal concatenation. The second string is appended to the first string. No whitespace is added. Thus "hello" ^ "World" is the same as "helloWorld". |
The Infinity Engine uses scripts in a bytecode format, known as BCS. Bytecode is not human-friendly, and the textual format BAF was derived from BCS. WeiDU includes a BAF compiler for turning BAF scripts into BCS scripts and a decompiler for the reverse transformation. The BCS format will not be documented here, but the format is described by the IESDP.
The BAF format accepted by WeiDU’s compiler is documented here in an extended context-free grammar notation.
Syntactical keywords are given in a UPPERCASE COURIER. Other keywords are symbolic, with the exception of TriggerOverride which is not symbolic and not written in uppercase. Notes:
BAF File | A BAF File is a text file that contains zero or more script blocks. BAF Files are compiled into BCS files, either by the COMPILE TP2 action or on the command line. | |
is | script block list | |
script block | ||
is | IF script trigger ... THEN script response ... END | If the script trigger(s) are true, the script response(s) are evaluated. |
script trigger | ||
is | [ ! ] Infinity Engine trigger | What constitutes an Infinity Engine trigger (trigger) varies between different versions of the Infinity Engine and will not be documented here. Refer to the right section of the IESDP. A preceding exclamation mark negates the trigger. |
or | [ ! ] TriggerOverride(Infinity Engine object,Infinity Engine trigger) |
TriggerOverride() is a form of syntactical sugar enabled by
WeiDU. It does not constitute a trigger of itself, does not exist in
BCS form and it is only available on versions of the Infinity
Engine which implement the NextObjectTrigger()
trigger. TriggerOverride() is compiled into
NextObjectTrigger() and NextObjectTrigger() is decompiled
into TriggerOverride(). Use of TriggerOverride() is
semantically identical to use of NextObjectTrigger() but may be
a syntactically preferable short-hand. An exclamation mark before
TriggerOverride negates the trigger. What constitutes an Infinity Engine object varies between different versions of the Infinity Engine and will not be documented here. |
script response | ||
is | RESPONSE #weight Infinity Engine action ... | weight is a non-negative integer that gives the evaluation probability of the response block relative to any other response blocks in the same script block. What constitutes an Infinity Engine action (action) varies between different versions of the Infinity Engine and will not be documented here. Refer to the right section of the IESDP. Any time an Infinity Engine String Reference (strref) is expected by an action, you can use text. When the script is compiled, WeiDU will add the text as a string and insert the corresponding strref in its stead. |
WeiDU is a command-line utility. GUIs are available, but this document only describes command-line invocation. Use the DOS Shell ("command" or "cmd") to run WeiDU. You control its behavior by passing arguments to it on the command line.
You invoke WeiDU by typing WeiDU and then any number of options and files, as described below.
Note: since it may not be obvious, if an option accepts ’X’ and says to be cumulative, if you want to apply it multiple times you have to use the following hideousness:
weidu --string 1 --string 2 --string 3
This does not apply to *-rest commands like --biff-get-rest and --force-install-rest.
Moreover, *-rest commands must be the last, EG
weidu --biff-get-rest sw1h01.itm --out foo
will try to extract sw1h01.itm, --out and foo from the biffs; you have to express the above as
weidu --out foo --biff-get-rest sw1h01.itm
*-list commands work like the -rest variants, except that they stop parsing for the current switch once they find an option starting with ’-’. Basically, this works like you’d expect:
weidu --biff-get-list sw1h01.itm --out foo
Compiling And Decompiling | |
FILE.D | Compile FILE to a DLG (dialogue file). |
FILE.DLG | Decompile FILE to a D (dialogue text file). |
number | When decompiling a DLG file to a D file, emit only state number. You may specify this multiple times and only the states you specify will be emitted. |
numberA-numberB | When decompiling a DLG file to a D file, emit only states between numberA and numberB (inclusive). You may specify this multiple times and only the states you specify will be emitted. |
FILE.BAF | Compile FILE to a BCS (script file). |
FILE.BCS | Decompile FILE to a BAF (script text file). |
--script-style X | Use the given BAF/BCS scripting style. X must be BG or BG1 or BG2 or PST or IWD or IWD1 or IWD2. |
--transin X | Use FILE as a source of translation strings when processing D and BAF files. |
FILE.TRA | Equivalent to --transin FILE.TRA. |
Module Packaging Input And Control | |
FILE.TP or FILE.TP2 | Read FILE and ask the user whether to install, reinstall or uninstall its TP2 Components. |
--yes | Answer all TP2 questions with ’Yes’ and do not prompt for a key to be pressed at the end of TP2 processing. |
--uninstall | Answer all TP2 questions with ’Uninstall’ and do not prompt for a key to be pressed at the end of TP2 processing. |
--reinstall | Re-install all TP2 components that are already installed and do not prompt for a key to be pressed at the end of TP2 processing. |
--language X | Sets the TP2 Language to the one passed here. Has no effect if the value is bigger than the language count (I.E. you’ll get asked for the language, unless you gave --uninstall, --reinstall or --force-uninstall). |
--force-install X | installs component number X, skips the others (cumulative). |
--force-uninstall X | uninstalls component number X, skips the others (cumulative). If there is no --force-install, you don’t get asked for the language. |
--force-install-rest X Y... | installs component number X Y..., skips the others (cumulative). |
--force-uninstall-rest X Y... | uninstalls component number X Y..., skips the others (cumulative). |
--force-install-list X Y... | installs component number X Y..., skips the others (cumulative). |
--force-uninstall-list X Y... | uninstalls component number X Y..., skips the others (cumulative). |
--quick-menu X | installs the QUICK_MENU selection X. Can be combined with --force-install and friends, but only if the latter components are defined in ALWAYS_ASK. |
--skip-at-view | AT_* VIEW this actions (and the extra chromosome versions like NOTEPAD this ) aren’t processed, while still processing batch files and similia. |
--safe-exit | Save weidu.log every time a component installation is begun. This makes it impossible to uninstall components (don’t ask), but allows the user to kill the weidu process (E.G. closing the DOS console via the X button) without leaving the game in an unrecoverably inconsistent state (you can [R]e or [U]ninstall the component and the game will be consistent again). This might break with mods using overly complicated AT_* actions (basically don’t use this with Big Mods). |
--save-components-name | Reprints weidu.log. Useful if you install with --quick-log. |
--ask-every | Behave as if ASK_EVERY_COMPONENT were present for all TP2 components. |
--ask-only X Y ... | Limit the interactive installer to only asking about components number X Y..., skipping the others (cumulative) |
--continue | Continue TP2 processing despite TP2 Action errors. |
--args X | X will be stored in the tp2 variable %argv[n]% , where
n is 0 for the first argument, 1 for the second, etc.If the installation is non-interactive, said variables will be loaded from the last interactive session. |
--args-rest X Y... | X Y Z will be stored in the tp2 variables %argv[n]% ,
where n is 0 for the first argument, 1 for the second, etc.If the installation is non-interactive, said variables will be loaded from the last interactive session. |
--args-list X Y... | X Y Z will be stored in the tp2 variables %argv[n]% ,
where n is 0 for the first argument, 1 for the second, etc.If the installation is non-interactive, said variables will be loaded from the last interactive session. |
--debug-assign | Print out all values assigned to TP2 variables (even implicit ones created by WeiDU). |
--debug-value | Print out all values encountered in TP2 processing and the results they evaluate to. Among other things, this is useful for catching parenthesis errors in your values. |
--modder [ X Y ] list | enables the MODDER mode and sets the MODDER option X to Y (cumulative, -list syntax). |
--clear-memory | calls CLEAR_MEMORY after every TP2 action. |
Automatic Updating Options | |
--update-all | Auto-update all WeiDU setup files (e.g., Setup-MyMod.exe) in the current directory. |
--noautoupdate | If you are running WeiDU as Setup-MyMod.exe, do not attempt to update yourself or any other mod. |
--no-auto-tp2 | Do not run setup-mymod.tp2 even if WeiDU is running as setup-mymod.exe. |
--noselfupdatemsg | If you are running WeiDU as Setup-MyMod.exe and it automatically updates itself, do not display a message or ask the user to press return. |
Infinity Engine Game Location Options | |
--game X | Set main game directory to X. WeiDU looks for CHITIN.KEY and DIALOG.TLK and the override directory in the main game directory (but see --tlkin and --search). WeiDU will look in the current directory and use the registry to find your game. If this fails, you will need to run WeiDU using the --game switch to define the full path to the BG2 directory. WeiDU will also search for BG1, IWD and PST. |
--nogame | Do not load any default game files. Unless you also specified --tlkin, no DIALOG.TLK will be loaded. Unless you also specified --search, no override directory will be used. |
--game-by-type X | tries to guess the game path by reading the registry; X can be one of BG1, BG2, PST, IWD1 or IWD2. BGEE is unsupported. |
--search X | Look in X for input files (cumulative). X is treated as an override directory and is given priority over the default override directory. |
Game Text (TLK) File Input | |
--use-lang X | On multi-language games (e.g., BGEE), use the files in lang/X/ instead of asking which lang/X/ to use. This answer is saved and --use-lang can override a previously saved answer. |
--tlkin X | Use X as DIALOG.TLK (instead of looking for DIALOG.TLK in the game directory). |
--ftlkin X | Use X as DIALOGF.TLK (instead of looking for DIALOGF.TLK in the game directory). |
FILE.TLK | Equivalent to --tlkin X. |
--tlkmerge X | Merge strings from X over the strings from any other loaded DIALOG.TLK. |
General Output Options | |
--out X | Emit most output files generated by command-line options (e.g., D, DLG, kits, --biff-get, BAF, BCS, --automate, --traify-tlk, --extract-kits, --list-biffs, --cmp-from, --dcmp-from, etc.) to file X. If X is a directory, certain commands (e.g., D, DLG, --biff-get, etc.) will place their output there. Does not affect TP2 processing. |
--append X | Like --out, but if X is an existing file then the result will be appended to it instead of overwriting it. |
--backup X | Backup files to directory X before overwriting. Does not affect TP2 processing. |
--tlkout X | Redirect the output dialog to X. If any strings were added to or changed in the loaded DIALOG.TLK, emit X as an updated version that reflects those changes. Many operations (e.g., compiling D files, --tlkmerge, STRING_SET) can add to or modify DIALOG.TLK. If this option is not provided, the output dialog is the same as the input dialog. |
--ftlkout X | Redirect the output dialogf to X. If any strings were added to or changed in the loaded DIALOGF.TLK, emit X as an updated version that reflects those changes. If this options is not provided, the output dialogf is the same as the input dialogf. |
--version | Print version number and exit. |
--parse-check X Y | Parses file Y as file type X and returns 0 if the file was parsed without errors. X must be one of D, BAF, TP2, TPA, or TPP. |
Dialogue Text File (D) Options | |
--noheader | Do not emit D header comments. |
--nofrom | Do not emit D // from: comments. |
--full-from | Generate complete // from: comments with a slower two-pass process. |
--nocom | Do not emit ANY D or BAF comments. |
--text | Emit string text with string references in comments. |
--transitive | Follow EXTERN links when making D files. See the tutorial on --transitive. |
--toplevel | Emit only top-level dialogue states -- that is, states with non-empty triggers. |
Translation Options | |
--traify X | Convert X (which should be a D or TP2 or BAF) so that it uses translation references instead of literal strings. Use --out Y to specify a name for the transformed version of X and its new TRA file. |
--traify# X | Use with --traify and --traify-tlk. Start the newly-created TRA file at translation string @X instead of @0. |
--traify-old-tra X | Assumes file X to contain strings already in .tra format. Use this over --traify# for merging new strings in an already traified D or TP2 or BAF file. |
--traify-comment | output @1 /* Hello */ rather than @1 when traifying |
--untraify-d X | convert .D file X to use hardcoded strings... |
--untraify-tra X | ...from TRA file X. Please note that the outcoming file
could not work properly (if baf code embedded in a d file contains @ references, or if a string contains
the~ character). |
--trans | Emit coupled D and TRA files when decompiling a DLG. |
--transref | Emit string reference numbers in TRA files when using --trans. |
--traify-tlk | Emit a TRA file for the loaded TLK file (see --tlkin, --out, --min and --traify#). |
--make-tlk X | Create a TLK file from TRA file X (cumulative, see --tlkout). |
--testtrans | Test all specified TRA translation files to see if any of them use text that is already in the loaded DIALOG.TLK. If they do you can save translation effort by using string references instead. |
--forceify X | Convert the given D file to use forced strrefs (see --out, SAY, Forced String Reference). |
Game Text Repository (TLK) Options | |
--string X | Display string reference #X (cumulative). If you also specify --min or --max, all string references between --min (or 0) and --max (or infinity) will be displayed. |
--strfind X | Display strings that contain X (cumulative, regexp allowed). |
--strapp X | Append string X to DIALOG.TLK (cumulative). |
Game Archive (BIFF) Options | |
--list-biffs | Enumerate all BIFF files in CHITIN.KEY. |
--list-files | Enumerate all resource files in CHITIN.KEY |
--biff X | Enumerate contents of BIFF file X (cumulative). |
--biff-get X | Extract resource X from game BIFFs (cumulative, regexp allowed). |
--biff-get-rest X Y ... | Every argument given on the command line after --biff-get-rest is treated as if it were preceded by --biff-get. Use this command to extract multiple different files (or regexps) at once. |
--biff-get-list X Y ... | Every argument given on the command line after --biff-get-rest is treated as if it were preceded by --biff-get, with the -list exception. Use this command to extract multiple different files (or regexps) at once. |
--biff-str X | Search all game BIFFs for files containing X (regexp allowed). |
--biff-value X | Search all game BIFFs for files containing value X at offset ADDR. Must be used with --biff-value-at. |
--biff-value-at ADDR | Gives the offset address for a --biff-value search. |
--biff-type X | Limit --biff-str or --biff-value searches to resources of type X (cumulative). |
--biff-name X | When a --biff-str or --biff-value search finds a matching file, assume it has a strref name at offset X and print that name out as well. |
--make-biff X | Create data/X.bif from all files in folder X and destructively update CHITIN.KEY. Do not use this feature. Use MAKE_BIFF instead. |
--remove-biff X | Remove references to BIFF X and all of its resources from CHITIN.KEY. Do not use this feature. |
Comparison Options (see --out) | |
--cmp-from X | Emit WRITE_BYTEs to turn this file ... |
--cmp-to X | ... into this one. |
--dcmp-fromX | Emit REPLACEs to turn this DLG file ... |
--dcmp-to X | ... into this one. |
--tcmp-fromX | Compare this TRA file (or directory of TRA files)... |
--tcmp-to X | ... with this one (or this directory). |
--tlkcmp-fromX | Emit STRING_SETs to convert this TLK file ... |
--tlkcmp-toX | ... into this one. |
--tlkcmp-use-strings | When using --tlkcmp-from, emit commands of the form STRING_SET "Hello" @1 instead of STRING_SET #1 @1. |
--bcmp-from X | Emit APPLY_BCS_PATCH to turn this BCS file ... |
--bcmp-to X | ... into this one. |
--bcmp-orig X | Original file X to apply ... |
--bcmp-patch X | ... this patch to. |
Range Options | |
--min X | Lower range for some commands. See --traify-tlk, --tlkcmp-from and --string. |
--max X | Upper range for some commands. |
Automatic Module Packaging Options | |
--automate X | Automatically create a TP2 file for resources in folder X. See --out. |
--automate-min X | Only --automate string references above X. If not specified, it is assumed as 62169 (that is, the number of strings in unmodded SoA). |
--extract-kits X | Extract all kits starting with kit #X and create TP2 actions to install those kits as part of a module. |
Logging Options | |
--log X | Log output and details to X. |
--autolog | Log output and details to WSETUP.DEBUG. |
--logapp | Append to log file instead of overwriting it. |
Finally, note that WeiDU will not add duplicate strings to DIALOG.TLK. If you instruct WeiDU to make use of the string “Imoen” (via SAY or --strapp or whatever) it will re-use any existing definition of “Imoen” instead. Two strings are equivalent in this sense only if they have the same text and the same associated sounds (normally strings have no associated sounds).
C:\Program Files\Black Isle\BGII - SoA\> weidu bodhi.dlg
[C:\Program Files\Black Isle\BGII - SoA\chitin.key] 182 BIFFs, 41793 resources [C:\Program Files\Black Isle\BGII - SoA\DIALOG.TLK] 84458 string entries [C:\Program Files\Black Isle\BGII - SoA\data\Dialog.bif] 2729 file entries [BODHI.DLG] loaded [.\BODHI.D] created from [BODHI.DLG]
This loads BODHI.DLG from the standard search path (i.e., the current directory, your override directory, then the game BIFFs) and creates BODHI.D from it.
C:\Program Files\Black Isle\BGII - SoA\> weidu bodhi.dlg --trans
... [.\BODHI.TRA] created as translation file [.\BODHI.D] created from [BODHI.DLG]
This creates BODHI.D as above and also the translation file BODHI.TRA (listing all of the strings in BODHI.D in an easy-to-translate or spell-check format). BODHI.D will be created with special references to those strings.
This is particularly useful if you are converting existing modifications you may have created with another tool, such as IDU, into WeiDU format. It allows you to both create the WeiDU D code and the translation-friendly string labels at the same time.
C:\Program Files\Black Isle\BGII - SoA\> weidu --nofrom bodhi.dlg --out foozle.d --text
... [.\foozle.d] created from [BODHI.DLG]
This creates foozle.d (instead of BODHI.D) and does not put any "// from:" comments in foozle.d. It will include states with SAYs of the form
SAY ~Hello~ /* #1 */instead of
SAY #1 /* ~Hello~ */
C:\Program Files\Black Isle\BGII - SoA\> weidu bodhi.dlg jaheira.dlg --out test
... [test\JAHEIRA.D] created from [JAHEIRA.DLG] [test\BODHI.D] created from [BODHI.DLG]
This loads BODHI.DLG and JAHEIRA.DLG and creates BODHI.D and JAHEIRA.D. The optional --out test argument instructs WeiDU to put the resulting D files in the test directory.
C:\Program Files\Black Isle\BGII - SoA\> weidu bodhi.d
... [bodhi.d] parsed [BODHI.DLG] saved 135 states, 259 trans, 16 strig, 66 ttrig, 54 actions
This loads and parses bodhi.d and then executed all instructions in it. This bodhi.d file just defines BODHI.DLG, which is created. If bodhi.d contains strings that do not occur in DIALOG.TLK, these strings will be added as new string references.
C:\Program Files\Black Isle\BGII - SoA\> weidu ppworker.d bodhi.d --out test
... [bodhi.d] parsed [ppworker.d] parsed [BODHI.DLG] saved 135 states, 259 trans, 16 strig, 66 ttrig, 54 actions [PPWORKER.DLG] saved 33 states, 81 trans, 4 strig, 12 ttrig, 10 actions
This creates test/BODHI.DLG and test/PPWORKER.DLG based on the instructions in bodhi.d and ppworker.d.
C:\Program Files\Black Isle\BGII - SoA\> weidu examples/sola/solae1.d
OR
C:\Program Files\Black Isle\BGII - SoA\> weidu examples\sola\solae1.d
... [examples/sola/solae1.d] parsed [SOLA.DLG] loaded [SOLA.DLG] saved 336 states, 401 trans, 64 strig, 18 ttrig, 125 actions [SOLAE1.DLG] saved 36 states, 49 trans, 1 strig, 11 ttrig, 1 actions [SOLAE2.DLG] saved 3 states, 3 trans, 0 strig, 0 ttrig, 0 actions [SOLAE3.DLG] saved 2 states, 2 trans, 0 strig, 0 ttrig, 0 actions [SOLAE4.DLG] saved 3 states, 3 trans, 1 strig, 0 ttrig, 0 actions [SOLAE5.DLG] saved 2 states, 2 trans, 0 strig, 0 ttrig, 0 actions [SOLAE6.DLG] saved 4 states, 5 trans, 0 strig, 2 ttrig, 0 actions
It just so happens that solae1.d APPENDs text to SOLA.DLG and creates SOLAE1.DLG, SOLAE1.DLG, SOLAE3.DLG, ..., SOLAE6.DLG. You could have put them all in the override directory with --out override. You may use the forward slash (/) or the backslash (\) for directories.
C:\Program Files\Black Isle\BGII - SoA\> weidu examples/sola/solafoe.d --transin examples/sola/solafoe.tra
OR
C:\Program Files\Black Isle\BGII - SoA\> weidu examples/sola/solafoe.d examples/sola/solafoe.tra
... [examples/sola/solafoe.tra] parsed (15 translation strings) [examples/sola/solafoe.d] parsed [SOLA.DLG] loaded [SOLA.DLG] saved 336 states, 401 trans, 65 strig, 18 ttrig, 124 actions [SOLAFOE.DLG] saved 11 states, 14 trans, 1 strig, 2 ttrig, 1 actions
It happens that solafoe.d uses 15 strings from a translation file, APPENDs to SOLA.DLG and creates SOLAFOE.DLG. You may use --transin to specify a translation file or (if it ends in TRA) just throw it on the command line. If you include multiple TRA files, the last one to define a particular string index wins for that string. They need not all cover the same set.
C:\Program Files\Black Isle\BGII - SoA\> weidu --string 123 --strfind understudy --strfind acid.*rows
... [C:\Program Files\Black Isle\BGII - SoA\chitin.key] 182 BIFFs, 41793 resources [C:\Program Files\Black Isle\BGII - SoA\DIALOG.TLK] 84458 string entries String #123 is ~Haer' Dalis, all of you, stop them!~ String #6763 is ~Acid Arrows~ String #11662 is ~Biff The Understudy~ ...
This displays string #123 and all strings that contain the string "understudy" and all strings that match the regular expression (regexp) "acid.*rows". Note that case does not matter.
C:\Program Files\Black Isle\BGII - SoA\> weidu --strapp ANewString --tlkout happy.tlk
[C:\Program Files\Black Isle\BGII - SoA\DIALOG.TLK] 84458 string entries [.\happy.tlk] created, 84459 string entries
Not much to say here. String reference #84459 in happy.tlk is now “ANewString”.
C:\Program Files\Black Isle\BGII - SoA\> weidu --biff data\dialog.bif
... [data\Dialog.bif] contains ABELA.DLG at index 0 [data\Dialog.bif] contains ACHEN.DLG at index 1 ...
This shows all of the resources (e.g., ACHEN.DLG is a resource) that are contained in data\Dialog.bif.
C:\Program Files\Black Isle\BGII - SoA\> weidu --biff-get dragred.cre
[C:\Program Files\Black Isle\BGII - SoA\chitin.key] 182 BIFFs, 41793 resources [C:\Program Files\Black Isle\BGII - SoA\DIALOG.TLK] 84458 string entries [C:\Program Files\Black Isle\BGII - SoA\data\Creature.bif] 3194 file entries [.\dragred.cre] 1776 bytes, created from [C:\Program Files\Black Isle\BGII - SoA\data\Creature.bif]
This grabs Firkraag’s dragon-form CRE creature file from the game BIFFs and saves it in the current directory.
C:\Program Files\Black Isle\BGII - SoA\> weidu --biff-get sper.*itm
[.\chitin.key] loaded, 590551 bytes [.\chitin.key] 182 BIFFs, 41793 resources [.\DIALOG.TLK] loaded, 10154904 bytes [.\DIALOG.TLK] 77666 string entries [.\data\Items.bif] loaded, 659688 bytes [.\data\Items.bif] 1990 file entries [.\SPER01.ITM] 266 bytes, created from [.\data\Items.bif] [.\SPER02.ITM] 314 bytes, created from [.\data\Items.bif] [.\SPER03.ITM] 362 bytes, created from [.\data\Items.bif] [.\SPER04.ITM] 322 bytes, created from [.\data\Items.bif] [.\SPER05.ITM] 266 bytes, created from [.\data\Items.bif] [.\SPER06.ITM] 266 bytes, created from [.\data\Items.bif] [.\SPER07.ITM] 554 bytes, created from [.\data\Items.bif] [.\SPER08.ITM] 314 bytes, created from [.\data\Items.bif] [.\SPER09.ITM] 314 bytes, created from [.\data\Items.bif] [.\SPER10.ITM] 362 bytes, created from [.\data\Items.bif] [.\data\25Items.bif] loaded, 222370 bytes [.\data\25Items.bif] 479 file entries [.\SPER11.ITM] 314 bytes, created from [.\data\25Items.bif] [.\SPER12.ITM] 1610 bytes, created from [.\data\25Items.bif] [.\SPERMEL.ITM] 890 bytes, created from [.\data\25Items.bif]
This one assumes that the game is in the current directory and asks for every spear item in the game. Note that --biff-get uses regular expressions (regexp), not DOS-style wildcards. Note also that --biff-get does not look in the override directory. Finally, if you are using a Mac (or otherwise running unix) you’ll want to put the regular expression in double quotes, like so: C:\Program Files\Black Isle\BGII - SoA\> weidu --biff-get "sper.*itm"
C:\Program Files\Black Isle\BGII - SoA\> weidu --biff-type CRE --biff-str SPWI911
... LICH01.CRE in [data\Creature.bif] matches HLKANG.CRE in [data\Creature.bif] matches ...
This finds all CRE files that contain the string "SPWI911", which is equivalent to finding all enemy mages that know the spell Meteor Swarm (which has resource name "SPWI911"). You could also try something like:
C:\Program Files\Black Isle\BGII - SoA\> weidu --biff-type BCS --biff-str Terminsel
... AR0300.BCS in [data\Scripts.bif] matches AR0308.BCS in [data\Scripts.bif] matches JAHEIRA.BCS in [data\Scripts.bif] matches ...
to find all of the game scripts that include a variable that includes the substring "Terminsel". As you would expect, Jaheira shows up. Note that these searches are moderately time-consuming (e.g., searching all scripts takes about 20 seconds).
C:\Program Files\Black Isle\BGII - SoA\> weidu --tlkcmp-from DIALOG.TLK --tlkcmp-to dialog-asc.tlk
... [DIALOG.TLK] loaded, 8692747 bytes [DIALOG.TLK] 74107 string entries [dialog-asc.tlk] loaded, 10211578 bytes [dialog-asc.tlk] 82805 string entries WARNING: DIALOG.TLK has 74107 entries, dialog-asc.tlk has 82805 entries STRING_SET 70866 ~Babau~ [] STRING_SET 70867 ~Babau~ []
This compares all strings in common between two DIALOG.TLK files and generates a list of STRING_SET TP2 entries to convert the TLK file named in --tlkcomp-from into the TLK file named in --tlkcomp-to. In this case, WeiDU indicates there are two differences in the strings shared between a standard ToB TLK file and an Ascension Classic TLK file: strings 70866 and 70867 were changed to "Babau". Also note that the Ascension Classic TLK file has more entries (82805 compared to 74107).
If you have made a large number of manual changes to a TLK file (such as grammar/spelling corrections, or other dialogue tweaks), this is a handy way to generate install-ready scripting to apply those changes to an end user’s version of BG2.
Note that the use of --out will be helpful for a long list.
C:\Program Files\Black Isle\BGII - SoA\> weidu --tlkcmp-from DIALOG.TLK --tlkcmp-to dialog-asc.tlk --out mylist.txt
This will make a new file mylist.txt file that contains the STRING_SET parts of the output, which can then be put into a TP2 file.
C:\Program Files\Black Isle\BGII - SoA\> weidu --automate MyMod/SomeFolder --append MyMod.tp2
WeiDU’s --automate feature can save you oodles of time, so you probably will want to learn how to use it. It’s rather simple when you get the hang of it (but everything is right?).
Suppose you have just created some items, some spells and some creatures and some areas for you mod and you want to distribute them to others using WeiDU. You could manually write out string patching code by hand for each resource. Or you can get WeiDU to do it automatically.
WeiDU will scan every item, spell and creature inside the given folder (in this example, the folder is MyMod/SomeFolder) and emit TP2 commands to COPY those resources from that folder into the override folder. In addition, each resource’s current strings (like item descriptions and monster names) will be loaded from your TLK file and used to patch that resource as it is copied.
For example, the output of --automate on a folder that contains a potion of extra healing looks like this:
COPY ~MyMod/SomeFolder/potn52.itm~ ~override/potn52.itm~ SAY NAME ~Potion~ SAY NAME2 ~Potion of Extra Healing~ SAY UNIDENTIFIED_DESC ~Potions are typically found in ceramic, crystal, glass, or metal flasks or vials. Flasks or other containers generally contain enough fluid to provide one person with one complete dose to achieve the effects of the potion.~ SAY DESC ~When wholly consumed, this potion restores 27 hit points to the person. The effect is instantaneous and the potion is destroyed in the process.~ SAY 0xde ~Gulp!~ [GULP]
And there you have it, apparently there is a substitute for hard work.
Some translators who are using WeiDU to translate non-WeiDU mods find it handy to be able to convert between TLK and TRA files.
First, let’s create a TRA file:
C:\Program Files\Black Isle\BGII - SoA\> weidu --traify-tlk --min 2000 --max 2002
... @2000 = ~Indeed! It's been quite tasty so far. Listen, we're not here to devour everything. In fact, we'd like to help a little girl named Jaella.~ @2001 = ~No, we haven't. We will devour you if you don't tell us what we need to know.~ @2002 = ~Let us stop this charade. I'm only here to ask you a few questions.~ ...
You may also extract only those strings matching a regexp:
C:\Program Files\Black Isle\BGII - SoA\> weidu --traify-tlk --strfind lawyer
... @36568 = ~Honor-bound and honor-branded, then, is it? Very well, lawyer, you have set me free and for that I thank you.~ ...
Finally, you may redirect the output to a file using --out and read from a different TLK file by adding it on the command line.
Once you have a TRA file with a few entries you can create a TLK file from it:
C:\Program Files\Black Isle\BGII - SoA\> weidu --make-tlk my.tra --tlkout new.tlk
[c:\src\weidu\weidu.exe] WeiDU version 109 [C:\Program Files\Black Isle\BGII - SoA/chitin.key] 182 BIFFs, 41793 resources [C:\Program Files\Black Isle\BGII - SoA/dialog.tlk] 82405 string entries [my.tra] parsed [my.tra] has 100 translation strings New TLK will have 200 entries [new.tlk] created, 200 string entries
String @1 in your TRA file will become string reference #1 in the TLK file. If your TRA file has “holes” the new TLK file will have blank entries. You may specify --make-tlk multiple times: the last TRA file to define a translation string determine that string reference.
C:\Program Files\Black Isle\BGII - SoA\> weidu --nocom --text --transitive banomen.dlg
The --transitive flag tells WeiDU to follow EXTERN references when emitting D files. So the resulting BANOMEN.D file has lines like this:
IF WEIGHT #31 ~InParty("Edwin") See("Edwin") Gender("Edwin",FEMALE) !StateCheck("Edwin",STATE_SLEEPING) Global("BAnomen1","LOCALS",0)~ THEN BEGIN 10 SAY ~Hey, Edwina! I shall be your champion at the next tournament that we come to if only you give me a piece of your robe, uh, that is, dress to adorn my shield.~ [ANOMEN49] IF ~~ THEN DO ~SetGlobal("BAnomen1","LOCALS",1)~ EXTERN ~BEDWIN~ 104 END IF ~~ THEN BEGIN BEDWIN 104 SAY ~(My condition draws fools like flies to honey). Silence, you idiot! You've a death wish that is larger than your swollen head.~ [EDWINW39] IF ~~ THEN GOTO 11 END IF ~~ THEN BEGIN 11 SAY ~Fair Edwina, I am truly bereft by your non-acceptance. It is tragic when a knight has no fair maiden to moon over. Heh he he...~ IF ~~ THEN EXIT END
Note that both lines from both Edwin and Anomen are presented. The resulting “D” file is not valid in that it cannot be fed back to WeiDU directly, but it should make it easier for you to read all of the jokes offline.
This section includes tutorials on specific parts of WeiDU. Many of them were contributed by users like you.
This tutorial was thoughtfully provided by Jason Compton.
Although a single SAY line can be of any length, for style purposes (particularly in BG2) it is considered good form to break up very large lines into smaller chunks.
One can easily create a series of simple SAY blocks, one doing GOTO to the next, but if there are no special conditions being checked or actions being taken, you can very easily string several lines together.
Let’s say you have a scenery NPC teaching a lesson about the Bill of Rights to the US Constitution.
BEGIN TEACHER IF ~NumTimesTalkedTo(0)~ THEN BEGIN constitution_1 SAY ~On September 25, 1789, the First Congress of the United States therefore proposed to the state legislatures 12 amendments to the Constitution that met arguments most frequently advanced against it. The first two proposed amendments, which concerned the number of constituents for each Representative and the compensation of Congressmen, were not ratified. Articles 3 to 12, however, ratified by three- fourths of the state legislatures, constitute the first 10 amendments of the Constitution, known as the Bill of Rights.~ IF ~~ THEN EXIT ENDThis is a perfectly valid block of dialogue, but it is extremely long, and would likely scroll out of the text window for players with lower resolution.
Rather than break each sentence up into a new explicit state, we can use Multisay and save a lot of typing. Multisay is invoked with the = (equals) sign, which tells WeiDU that, "the current speaker should say another line here."
Here’s how that D state would look with Multisay:
IF ~NumTimesTalkedTo(0)~ THEN BEGIN constitution_1 SAY ~On September 25, 1789, the First Congress of the United States therefore proposed to the state legislatures 12 amendments to the Constitution that met arguments most frequently advanced against it.~ = ~The first two proposed amendments, which concerned the number of constituents for each Representative and the compensation of Congressmen, were not ratified.~ = ~Articles 3 to 12, however, ratified by three-fourths of the state legislatures, constitute the first 10 amendments of the Constitution, known as the Bill of Rights.~ IF ~~ THEN EXIT ENDThis will create three dialogue states, separated by simple "click to continue" transitions.
And that’s Multisay in a nutshell. Note that (as always with WeiDU) the line break and spacing before and after the = are totally optional, and used here only for illustration.
SAY ~One~ = ~Two~ = ~Three~is perfectly valid as well.
You may Multisay inside almost any state, so you may use it within an APPEND D Action. This is valid:
APPEND J#KLSYJ IF ~~ THEN BEGIN Renal1_1 SAY ~All right, CHARNAME. I can accept that... you are right, there are bigger issues to consider.~ = ~But I hope you do understand why I said something, why it would be upsetting to have someone so close to me, in a role like that.~ IF ~~ THEN EXIT END // end of state Renal1_1 END // end of APPEND J#KLSYJHowever, you cannot use Multisay inside REPLACE, because the nature of REPLACE is to change (that is, replace) a single state, while Multisay’s nature is to create multiple states.
This tutorial was thoughtfully provided by Jason Compton.
CHAIN is an extension of the Multisay concept, simply with multiple participants. If you have two NPCs bantering back and forth for a prolonged period of time, and you do not need to do any special condition checks or actions as they babble, it can get very tedious to set up a separate IF/THEN/BEGIN/SAY block for each line.
Imagine a conversation like this:
If you wanted to add this witty little banter to your game, you could do it with 11 APPEND blocks for each line, or save a little time doubling up the back-to-back Imoen and Kelsey lines with Multisay inside their APPENDs, so you’d only need 9. In fact, you could get away with 2 APPEND blocks (remember that the states don’t have to appear in the order they are spoken, so you could mention all of Kelsey’s lines first and then all of Immy’s as long as the labels thread up the conversation correctly), one with 5 state declarations and one 4. But there’s an even better way, and that’s to use CHAIN. It works very much like Multisay. You use = to indicate that the current speaker should speak again, and == (that’s two consecutive equal signs) to indicate that a new speaker should take over.
Note that as of WeiDU 82, CHAIN can now define state triggers, perform DO actions, and end with an EXIT or COPY_TRANS. This means that for most simple NPC/NPC banters, where the PC does not have an opportunity to speak, you no longer need to use anything else.
Watch and see how this dialogue works using a CHAIN.
CHAIN IF ~Global("KelseyImoenPizza","LOCALS",0) InParty("Imoen2") See("Imoen2") !StateCheck("Imoen2",STATE_SLEEPING)~ THEN BJKLSY pizzachain ~Imoen, what do you like on your pizza?~ DO ~SetGlobal("KelseyImoenPizza","LOCALS",1)~ == IMOEN2J ~Oregano.~ = ~Oh, and maybe with a little basil mixed in.~ == BJKLSY ~Well, yeah, but anything else?~ == IMOEN2J ~Sauce is good.~ == BJKLSY ~(laughs) You're not being very helpful, Imoen.~ == IMOEN2J ~Crust. I like crust on my pizza. Cooked crust is better.~ == BJKLSY ~Do you want me to make you this pizza or not?~ = ~It WAS your idea.~ == IMOEN2J ~I can't decide. Never mind, I'm just gonna have yogurt.~ == BJKLSY ~(sigh)~ EXITNote how this dialogue works.
We use the CHAIN statement to define the state trigger (the starting conditions that must be true) and assign it to BJKLSY.DLG. The "pizzachain" label is mostly just for internal reference. Kelsey delivers the first line, then we use == IMOEN2J to allow her to answer, "Oregano." Then, we can use the single = to indicate that the current speaker (Imoen) has two consecutive lines.
Then it’s Kelsey’s turn to speak, so we use == BJKLSY to tell WeiDU to tell WeiDU to switch to the other speaker (which is Kelsey, since we specified BJKLSY). They banter back and forth for a while, and then when it is Kelsey’s turn to have back-to-back lines Do you want me to make you this pizza or not? and It WAS your idea., we separate with a single = to indicate that the current speaker (Kelsey) has two consecutive lines.
After Kelsey’s final, exasperated sigh, we use the EXIT command to terminate the CHAIN, and exit the dialogue.
And that’s all you need to know to use CHAIN. It saves a tremendous amount of time over setting up individual APPEND blocks, even Multisay blocks, for each NPC.
Advanced CHAINing:
You may include DO actions and conditionals inside chainText, as in:
CHAIN IF ~Global("KelseyImoenPizza","LOCALS",0) InParty("Imoen2") See("Imoen2") !StateCheck("Imoen2",STATE_SLEEPING)~ THEN BJKLSY pizzachain ~Imoen, what do you like on your pizza?~ DO ~SetGlobal("KelseyImoenPizza","LOCALS",1)~ == IMOEN2J ~Oregano.~ = ~Oh, and maybe with a little basil mixed in.~ == BJKLSY ~Well, yeah, but anything else?~ == IMOEN2J ~Sauce is good.~ == BJKLSY IF ~PartyHasItem("pepperoni")~ THEN ~Look, we HAVE pepperoni. Why don't I just use that? I'll eat it, anyway. If you don't like it, have yogurt instead.~ == IMOEN2J IF ~!PartyHasItem("pepperoni")~ THEN ~Crust. I like crust on my pizza. Cooked crust is better.~ == BJKLSY IF ~!PartyHasItem("pepperoni")~ THEN ~Do you want me to make you this pizza or not?~ = ~It WAS your idea.~ == IMOEN2J IF ~!PartyHasItem("pepperoni")~ THEN ~I can't decide. Never mind, I'm just gonna have yogurt.~ == BJKLSY IF ~!PartyHasItem("pepperoni")~ THEN ~(sigh)~ EXITIn this case, the dialogue changes if the party has the pepperoni item. If it does, Kelsey says the we HAVE pepperoni and then (sigh) and then the dialogue ends. If not, the dialogue works as before. The chainText lines with IFs in them are only spoken if their conditionals are true.
This tutorial was thoughtfully provided by Jason Compton.
There are some complex branching dialogues in Infinity Engine games that you, as a mod creator, may wish to add to. Consider Baldur’s Gate 2 and the "Arrival In Hell" dialogue. There is a brief internal dialogue as the protagonist comes to terms with the fact that he/she is now in Hell, and then all of the companions coded by Bioware have a chance to speak. (PLAYER1.DLG state 25. It will help the rest of this explanation if you go use WeiDU to decompile PLAYER1.DLG into PLAYER1.d, and/or open up PLAYER1.DLG in Near Infinity and look at state 25.)
After the PC’s internal voice says You doubt they will be pleased with their present circumstance, when you don’t even know why you are here yourself., every Bioware NPC has the opportunity to speak. If you are creating a new NPC and want it to have that full, rich Bioware flavor, you may wish to let your character speak here as well. For that, a simple EXTEND_BOTTOM will do the job.
Here’s the example of how Weimer does this with Solaufein:
EXTEND_BOTTOM PLAYER1 25 IF ~IsValidForPartyDialogue("Sola") Global("SolaWelcomeHell","GLOBAL",0)~ THEN DO ~SetGlobal("SolaWelcomeHell","GLOBAL",1)~ EXTERN SOLA inHell1 ENDThis puts a new transition at the bottom of PLAYER1 25 that tells the game to branch to Solaufein’s observation about your arrival in Hell, if he is present (IsValid) and if we have not already seen his comment once before (the check for SolaWelcomeHell=0, then setting it to 1. This ensures that this path can only happen once, which is important for a reason I will explain later.) SOLA inHell1, which we will define later, contains Solaufein’s comment.
Once Solaufein makes his comment, it would be very thoughtful of us to allow the other Bioware NPCs to have their say as well, as Bioware intended and as the experienced players out there expect. You could simply use the decompiled PLAYER1.D and copy and paste the transition list out. But there are some good reasons not to do that. On a trivial level, it’s a big waste of space in your D file.
The most important reason is this: If another mod NPC has come along and done their own EXTEND_BOTTOM, you would have no way of knowing that. By putting the Bioware stock transition list in, you would ensure that only your mod NPC got to have their say. The rest would be silent. So if you were the developer of Solaufein, and Solaufein were installed after Kelsey and Tashia in a game, Kelsey and Tashia would be skipped, because you only copied the Bioware transition list. That’s a heavy responsibility.
That’s why COPY_TRANS exists. COPY_TRANS pulls the entire transition list from a specified state and makes it the transition list for your new state.
To illustrate, look at SOLA inHell1 :
APPEND SOLA IF ~~ THEN BEGIN inHell1 SAY @2 = @3 // use strings @2 and @3 from translation file COPY_TRANS PLAYER1 25 END ENDInstead of copying and pasting that huge list of IF "" THEN EXTERN transitions from the PLAYER1.D, we let WeiDU do it for us. COPY_TRANS PLAYER1 25 tells WeiDU to grab the current list of transitions from PLAYER1 state 25, and use it as the transition list for SOLA inHell1. This ensures that Solaufein will be able to properly branch out to Imoen, Aerie, Minsc, and the rest of the gang, as well as grabbing the transitions that may have been added by other NPCs such as Kelsey, Valen, or Tashia.
COPY_TRANS can form all of your new state’s transition list, or only part of it. This would be valid, for example:
IF ~~ THEN BEGIN commentary SAY ~Hey, I think I might like to run the transition list from TOLGER 75... or I might want to do something else, if I'm in chapter six!~ COPY_TRANS TOLGER 75 IF ~Global("Chapter","GLOBAL",6)~ THEN GOTO chapter6commentary ENDThis would make the GOTO commentary2 transition show up at the bottom of the transition stack (below the list copied from TOLGER 75). Remember that transition triggers are read bottom to top, so it would be the first transition evaluated. If you want it to be evaluated after the list of transitions in the COPY_TRANS, put it above. Note, however, that Bioware usually structures its transition lists so that the topmost trigger will always be true (in fact, sometimes it is "True()") so it is somewhat unlikely you would ever want to put a new transition trigger above the COPY_TRANS.
Now, that explanation for why the SolaWelcomeHell variable check is important: if a user accidentally installs the same mod more than once, and it employs COPY_TRANS, the list the second time around will include our new trigger:
IF ~IsValidForPartyDialogue("Sola") Global("SolaWelcomeHell","GLOBAL",0)~ THEN DO ~SetGlobal("SolaWelcomeHell","GLOBAL",1)~ EXTERN SOLA inHell1If there was no flag being set to ensure that the transition could only run once, the user would get stuck in a loop. This can and has happened with mods in the wild. The end result would be Solaufein or Kelsey or whomever constantly offering their commentary, over and over again.
Important note: The WeiDU D compiler runs COPY_TRANS before other actions that you might take to affect a transition list within the same D file (like EXTEND_TOP and EXTEND_BOTTOM). This is a good thing.
This tutorial was thoughtfully provided by Jason Compton.
Interjections, the little comments party members make, are a great way to spice up a new NPC or a new quest you create. It shows that the characters are paying attention to their game world, and that they have an opinion about what goes on around them.
Through interjections, an NPC can advise a course of action, complain about a decision, force your hand... fun things.
The traditional way to do an interjection is to find a state of a dialogue where another NPC might comment, and use EXTEND_BOTTOM and APPEND in conjunction.
Here’s an old-school example:
EXTEND_BOTTOM SAHPR4 7 IF ~IsValidForPartyDialog("J#Kelsey")~ THEN EXTERN J#KLSYJ KelseySAHPR4 END APPEND J#KLSYJ IF ~~ THEN BEGIN KelseySAHPR4 SAY ~Urk. Who was the lucky donor?~ IF ~~ THEN EXTERN SAHPR2 10 END ENDThis works, but it’s also more work than it needs to be since the introduction of INTERJECT. (Incidentally, for you WeiDU historians out there, there are two main reasons advanced functions have been added to WeiDU since the first versions of Solaufein and the original CHAIN command: Either Weimer needed them for his own modding goals, or a mod project, usually Kelsey, requested it. INTERJECT came about when Westley finally decided that Solaufein and Valen should comment about quests. On the other hand, COPY_TRANS and INTERJECT_COPY_TRANS were my idea.)
INTERJECT simplifies this process considerably. To run an INTERJECT, you need to know the source state (the line after which you want one or more NPCs to interject), and the destination state (where you want the dialogue tree to go after the interjection.) Typically, but not necessarily, the destination state will be wherever the dialogue was originally planning to go, but if the NPC takes the conversation in a new direction, that may change. We’ll stick with the simpler cases for illustration.
INTERJECT is a specialized form of CHAIN. So if you’re familiar with CHAIN, this will look familiar.
Consider the dryads in the Irenicus start dungeon. Perhaps Minsc should say something to them. IDRYAD1.DLG state 1 offers a good opportunity.
IF ~~ THEN BEGIN 1 // from: SAY #11080 /* ~We are his possessions.~ */ IF ~~ THEN EXTERN ~IDRYAD2~ 1 ENDMinsc is outraged. Here’s how he can express it. The idea is that we want Minsc to comment, but for the dialogue to continue just as it would have if he was not there.
INTERJECT IDRYAD1 1 MinscDryad == MINSCJ IF ~IsValidForPartyDialog("Minsc")~ THEN ~Boo is outraged that the strange wizard would own these lovely ladies! Can Minsc and Boo help you nice girls?~ END IDRYAD2 1Here’s what’s going on here.
Invoking INTERJECT requires three arguments: the dialogue name and state we’re interjecting into, plus a unique global variable name. This variable will be set from 0 to 1 after the INTERJECT runs, to ensure that it can only happen once. (This is important in case players accidentally install your mod twice, as it could create a looping problem similar to the one described in COPY_TRANS.)
So INTERJECT IDRYAD1 1 MinscDryad tells WeiDU "Put this dialogue after IDRYAD1 state 1. This dialogue will run if MinscDryad is 0. After it runs, we will set MinscDryad to 1."
Then we need to define who is speaking. == (that’s two consecutive equal signs) is CHAIN-style notation for a new speaker, and MINSCJ is the proper dialogue to use for Minsc’s "joined-party" commentary. If Minsc is in the party and valid for dialogue, he will say his line. After that, we transition to IDRYAD2 state 1 (END IDRYAD2 1), which is where the dialogue was heading in the first place. We can make this more complicated and let the dryad reply to his interruption before proceeding.
INTERJECT IDRYAD1 1 MinscDryad == MINSCJ IF ~IsValidForPartyDialog("Minsc")~ THEN ~Boo is outraged that the strange wizard would own these lovely ladies! Can Minsc and Boo help you nice girls?~ == IDRYAD1 IF ~IsValidForPartyDialog("Minsc")~ THEN ~Large mortal, we are having a dramatic scene. Please do not interrupt.~ END IDRYAD2 1Note that we repeat the
IF ~IsValidForPartyDialog("Minsc")~ THENcheck for IDRYAD1. That is to ensure that she only says that line if Minsc is there, and by extension has already made his little comment. So now, Minsc interrupts, Dryad #1 scolds him, and then we proceed to the second dryad’s line.
One general piece of advice: while it’s not necessary to pick a state that has only a single transition to another state, unless you’re willing to experiment (or you’re intentionally trying to remove player choices from the equation, by making the NPC say something that forces immediate action, for instance), don’t INTERJECT into a state that has player options (REPLYs).
This tutorial was thoughtfully provided by Jason Compton.
INTERJECT is good for creating interjections where none already exist. However, many of the really good opportunities for interjections in game dialogue already have interjections in them. If you use standard INTERJECT, chances are you’ll skip right over them.
Consider TOLGER.DLG state 75. After Tolgerias lays down the law by saying This is a sensitive matter, and I cannot tell all to every curious soul. I must have your commitment that you agree to the task, four NPCs (Edwin, Jaheira, Yoshimo, Korgan) will tell you what they think of that rotten arrangement. What’s more, Bioware structured the dialogue transitions so that they all can get their comment in, if all four of them are in the party.
However, using a standard INTERJECT for a new NPC line would skip over those four comments, which is rather impolite. INTERJECT_COPY_TRANS works much like regular INTERJECT, but instead of defining a state to transition to after END, WeiDU will COPY_TRANS the transition list from the state you are INTERJECTing into.
This is not as confusing as it sounds. Watch as hypothetical new NPC Aqualung responds to Tolgerias’s terms:
INTERJECT_COPY_TRANS TOLGER 75 AquaTolger == AQUALUNJ IF ~IsValidForPartyDialogue("Aqualung")~ THEN ~Hey, that's a really crummy offer! Where did those little girls go? I could be sitting on a park bench, I don't need this aggravation! Who are you, anyway?~ == TOLGER IF ~IsValidForPartyDialogue("Aqualung")~ THEN ~You poor old sod, you see it's only me. Now, did anyone else have a smart remark they wanted to make?~ ENDSo, if Aqualung is around, we’ll hear from him and then Tolgerias will respond to him. After that, the game will look for the presence of Edwin, Jaheira, Yoshimo, and Korgan and we’ll get their responses to Tolgerias as well.
Hint: INTERJECT_COPY_TRANS is fine to use even if there were no other interjections in the source state, i.e. if there’s just a single IF "" THEN GOTO blah. That is, as long as you plan to proceed to the original destination. It saves you the trouble of having to look up and input the destination.
This tutorial was thoughtfully provided by Rastor.
There seems to be a great deal of confusion among the members of the modding community regarding the purpose and overall function of INTERJECT_COPY_TRANS2. Allow me to take a moment to dispel the rumors that you may have heard.
INTERJECT_COPY_TRANS2 is not intended as a replacement for INTERJECT_COPY_TRANS. INTERJECT_COPY_TRANS is a great function but it has a flaw in certain special cases. That’s what INTERJECT_COPY_TRANS2 is intended to remedy. This difference between the two is best illustrated with an example.
Let us suppose that you want to add an NPC response to PPSAEM2 8. Here is some of what that state looks like before any mods have been applied:
IF ~~ THEN BEGIN 8 // from: 7.0 SAY #44931 /* ~Blah blah blah~ */ IF ~!IsValidForPartyDialog("Jaheira") !IsValidForPartyDialog("Anomen") !IsValidForPartyDialog("Edwin") IsValidForPartyDialog("Viconia")~ THEN DO ~SetGlobal("WackoArmy","GLOBAL",1) OpenDoor("DOOR12") EscapeArea()~ UNSOLVED_JOURNAL #7045 EXTERN ~VICONIJ~ 129 END
Note that the DO actions include EscapeArea(). If you use INTERJECT_COPY_TRANS to add an interjection here, the DO actions will be performed by your interjector (usually a party member). The result is that after encountering this dialogue in the game the party member will leave the area (and Saemon will stay where he is!) promptly after performing the interjection. Instead, we want to keep the DO action associated with Saemon.
INTERJECT_COPY_TRANS2 allows modders to remedy this problem. INTERJECT_COPY_TRANS2 does exactly the same thing as INTERJECT_COPY_TRANS except that DO actions will be kept with their original actor and not transferred to the interjector.
To code your interjection using INTERJECT_COPY_TRANS2, you would do this:
INTERJECT_COPY_TRANS2 PPSAEM2 8 ~Blah blah blah~ END
The dialogue state that WeiDU will create from this INTERJECT_COPY_TRANS2 statement looks something like:
IF ~~ THEN BEGIN 133 // from: SAY #78199 /* ~Blah blah blah~ */ IF ~!IsValidForPartyDialog("Jaheira") !IsValidForPartyDialog("Anomen") !IsValidForPartyDialog("Edwin") IsValidForPartyDialog("Viconia")~ THEN UNSOLVED_JOURNAL #7045 EXTERN ~VICONIJ~ 129 END
Note that INTERJECT_COPY_TRANS2 is not intended as a universal replacement for INTERJECT_COPY_TRANS. This is most obvious when using the command to interject into a state that starts a cutscene. Here is an example of an interjection into PPIRENI2 27.
The original, unmodded state:
IF ~~ THEN BEGIN 27 // from: 28.0 26.0 SAY #44869 /* ~I bid you farewell, child of Bhaal. We shall not meet again.~ [IRENIC52] */ IF ~~ THEN DO ~EraseJournalEntry(7252) EraseJournalEntry(7253) EraseJournalEntry(22952) EraseJournalEntry(23306) SetGlobal("AsylumPlot","GLOBAL",40) StartCutSceneMode() StartCutScene("Cut41j")~ SOLVED_JOURNAL #7255 EXIT END
Using INTERJECT_COPY_TRANS2 to code your interjection into this state will cause the game to crash when your interjection plays (because the special StartCutSceneMode() action cannot occur in the middle of a dialogue, loosely). INTERJECT_COPY_TRANS will work properly, however.
stateTriggerStrings, the conditions that determine what state should be used for the beginning of a dialogue, may have WEIGHTs. These WEIGHTs are used by the Infinity Engine to choose which state to pick if multiple state triggers evaluate to "true". [ In reality, the WEIGHTs are just the offsets within the state trigger table in the DLG file, but this detail is not important unless you are writing your own tool. ] WEIGHTs only make sense for stateTriggerStrings that are not empty.
If multiple stateTriggerStrings evaluate to true, the Infinity Engine will pick the state with the lowest WEIGHT. Usually the weighting follows the order of state declaration in the D file. That is, the first state mentioned has the lowest weight (i.e., will be picked first in case of a tie) and the last state mentioned has the highest weight (i.e., will be picked last in case of a tie). However, you may include an explicit WEIGHT directive to change things around. For example, consider this D file:
BEGIN foozle IF ~True()~ THEN BEGIN a SAY ~Jason~ END IF ~True()~ THEN BEGIN b SAY ~yada~ END IF ~True()~ THEN BEGIN c SAY ~Compton~ END IF ~True()~ THEN BEGIN d SAY ~kelsey~ ENDIf you talk to foozle, it will always say Jason. However, you may explicitly alter the weights so that the third state is picked first, as in:
BEGIN foozle IF WEIGHT #10 ~True()~ THEN BEGIN a SAY ~Jason~ END IF ~True()~ THEN BEGIN b SAY ~yada~ END IF WEIGHT #2 ~True()~ THEN BEGIN c SAY ~Compton~ END IF ~True()~ THEN BEGIN d SAY ~kelsey~ ENDWith this D file, foozle will always say Compton. All states with WEIGHT directives come before all states without them. States without WEIGHT directives are ranked in order of appearance. So the state order for foozle.DLG is c-a-b-d. Yes, this is complicated.
Strong Style Suggestion: do not use the WEIGHT directive in your hand-made D files. Just use the implicit ordering.
The WEIGHT directive was introduced to facilitate handling of Bioware-created DLG files (e.g., BJAHEIR.DLG) that include tricky weighting. Only states with non-empty triggers are given implicit weights. If you create a D file from a DLG that features non-trivial weighting, WeiDU will emit comments like this:
IF WEIGHT #8 /* Triggers after states #: 11 12 24 25 26 36 58 even though they appear after this state */ ~True()~ THEN BEGIN 10 // from: SAY #52190 /* ~Please do not interrupt our thoughts. We must prepare carefully if we are to see a weakness in the illithid web. ~ */ IF ~~ THEN EXIT ENDto remind you that the order is not what you think.
All non-empty state triggers in DLG files are given weights counting up from 0 to the maximum number of state triggers in the DLG file. You may use any number you like (even a negative one): WeiDU will simply sort them. ADD_STATE_TRIGGERdoes not change the weight associated with that trigger. APPEND can be used to give a non-trivial weight to a state, as in:
APPEND BJAHEIR IF WEIGHT #-999 ~MyCondition()~ THEN BEGIN mystate SAY ~My Stuff~ END ENDSince BJAHEIR will have implicit WEIGHTs in the range from #0 to about #50, this causes mystate to have priority over all states that already exist in BJAHEIR. Without such drastic action, APPENDed states will have use the implicit ordering, and will thus have the lowest priority (because they appear at the end of the file). Multisay and CHAIN also append states, but since they always append states with empty stateTriggerStrings, WEIGHTs are not relevant.
Consider the following example:
BEGIN foozle IF WEIGHT #10 ~True()~ THEN BEGIN a SAY ~Jason~ END IF ~~ THEN BEGIN b SAY ~yada~ END IF WEIGHT #2 ~True()~ THEN BEGIN c SAY ~Compton~ END IF ~True()~ THEN BEGIN d SAY ~kelsey~ END ADD_STATE_TRIGGER foozle 1 /* state b */ ~MyCondition()~The resulting foozle dialogue will still have the c-a-b-d weighting order.
Here’s another example:
BEGIN foozle IF ~True()~ THEN BEGIN a SAY ~Jason~ END IF ~~ THEN BEGIN b SAY ~yada~ END IF ~True()~ THEN BEGIN c SAY ~Compton~ END IF ~True()~ THEN BEGIN d SAY ~kelsey~ END ADD_STATE_TRIGGER foozle 1 /* state b */ ~MyCondition()~The resulting foozle dialogue will have the (expected) a-b-c-d ordering.
However, consider this evil example:
////// // foozle.DLG contents, assume it has already been created and is // sitting on your hard drive somewhere // IF ~True()~ THEN BEGIN a SAY ~Jason~ END // IF ~~ THEN BEGIN b SAY ~yada~ END // IF ~True()~ THEN BEGIN c SAY ~Compton~ END // IF ~True()~ THEN BEGIN d SAY ~kelsey~ END ////// // new D file ADD_STATE_TRIGGER foozle 1 /* state b */ ~MyCondition()~This will update foozle and the resulting order will be a-c-d-b (because when foozle.DLG was loaded from the disk, a c and d were given weights but b was not (because it had an empty trigger)). Thus, you should avoid using ADD_STATE_TRIGGER on states with empty triggers unless you know what you are doing.
If you are writing a mod and you would like to make it easier to translate it into another language you can use "translation files" (much like BGII itself uses DIALOG.TLK) to separate your dialogue structure and content. A translation file basically lists the string texts in order. For example,
C:\Program Files\Black Isle\BGII - SoA\> WeiDU --trans SCSARLES.DLG
This creates scsarles.D and scsarles.tra. scsarles.D now contains:
IF ~NumTimesTalkedTo(0)~ THEN BEGIN 0 // from: SAY @1 /* ~Who is it? Might I ask why you have disturbed my meditations? My creative muse must be gently awakened, and your stomping about is simply not conducive to this.~ [SARLES02] #28655 */ IF ~~ THEN REPLY @2 /* ~My apologies. I will leave you to your thinking.~ #28656 */ GOTO 1 IF ~~ THEN REPLY @3 /* ~I apologize, but I have come to request your talent on a commissioned artwork.~ #28657 */ DO ~SetGlobal("TalkedToSarles","GLOBAL",1)~ GOTO 2 ENDNote that all of the strings have been replaced by @number and the texts have been put in comments.
The translation file scsarles.tra contains all of those strings:
// SCSARLES translation file @1 = ~Who is it? Might I ask why you have disturbed my meditations? My creative muse must be gently awakened, and your stomping about is simply not conducive to this.~ [SARLES02] @2 = ~My apologies. I will leave you to your thinking.~ @3 = ~I apologize, but I have come to request your talent on a commissioned artwork.~You may then ask someone who speaks another language to write a new translation file by translating every string in scsarles.tra. This prevents the string text and the structure from getting out of sync and simplifies translation; non-technical players can translate raw text files easily.
When compiling a D file that contains @number translation references you must supply (at least) one translation file. For example, you might say:
C:\Program Files\Black Isle\BGII - SoA\> WeiDU SCSARLES.D italian.tra
You may specify multiple translation files. The last one to define a string wins. This is useful if one language is more up to date than the others. In this example:
C:\Program Files\Black Isle\BGII - SoA\> WeiDU SCSARLES.D english.tra italian.tra
Strings will be taken from the italian translation whenever possible, but if they are not available it will fall back on the english versions.
You may use WeiDU to check and make sure that translations are up to date. WeiDU will automatically generate a text file listing all of the strings that are present in one translation (usually your native one) that are missing in another. You can then send this file to your translators so that they know what to do. This example command compares all of the TRA files in the american and french directories and creates a file called MISSING.
C:\Program Files\Black Isle\BGII - SoA\> weidu --tcmp-from american --tcmp-to french --out MISSING
This tutorial was thoughtfully provided by Jason Compton.
D and TP2 files allow programmers to describe text either literally:
// Greeting.d SAY ~Hello.~or with a companion TRA (translation) file that supports multiple languages:
// Greeting.d SAY @1 // Greeting.tra @1 = ~Hello.~The idea being that one can make a French version of Greeting.tra which contains
// French-Greeting.tra @1 = ~Bonjour.~However, some WeiDU users, for reasons of convenience or simply never anticipating the opportunity to translate, may have originally chosen the "hard-coded" approach but now regret that decision. --traify will break out all the text in SAY, REPLY, and JOURNAL entries into translation-ready format. --traify may also be used on TP2 files.
To turn the hard-coded D file FWKI.d into a new D/TRA combo, use --traify and --out to specify the input and output filenames, respectively:
C:\Program Files\Black Isle\BGII - SoA\> weidu --traify fwki.d --out fwki-new.d
After a brief pause, fwki-new.d and fwki-new.tra will be created.
The --traify process turns fwki.d’s
APPEND J#KLSYJ IF ~~ THEN BEGIN KelseySAHPR4 SAY ~Urk. Who was the lucky donor?~ IF ~~ THEN EXTERN SAHPR2 10 END ENDinto fwki-new.d’s
APPEND J#KLSYJ IF ~~ THEN BEGIN KelseySAHPR4 SAY @0 IF ~~ THEN EXTERN SAHPR2 10 END ENDand in the newly created fwki-new.tra, you will find
@0 = ~Urk. Who was the lucky donor?~and this dialogue file is now ready for translation.
--traify works from the top of the D down, starting at @0. It will NOT skip over any existing @x translation references it finds, so if your D contains any translation support at all, it is best to use --traify-old-tra as well.
Because standard --traify starts at @0 and is unaware of any any existing @x entries in the D, if you have begun to convert a D to a D/TRA pair by hand, you may have @x entries that clash with --traify’s results.
In other words, if you already have a state that says
IF ~~ THEN BEGIN blah SAY @0 IF ~~ THEN EXIT END--traify will not skip @0 automatically, you will have two locations where @0 is used but you intended to use two different strings, and this will be bad.
To avoid this problem, add the --traify-old-tra argument to specify the file containing the already traified strings:
C:\Program Files\Black Isle\BGII - SoA\> weidu --traify fwki.d --traify-old-tra fwki.tra --out fwki-new
will put the existing and new ones @x references in fwki-new.tra.
Finally, note that --traify works for BAF files as well.
Note: We were previously suggesting to use --traify# here. Unless you want for some reason to have new references starting at a given number, you should use --traify-old-tra instead.
In all cases, you might also find it beneficial to put the --traify-comment argument in your command line, so that you’ll also have the content of the @references in your file (which will make it easier to edit it later):
C:\Program Files\Black Isle\BGII - SoA\> weidu --traify fwki.d --traify-old-tra fwki.tra --out fwki-new --traify-comment
In particular, using --traify-comment and --traify-old-tra together will add the missing comments without doubling the existing ones.
This tutorial was thoughtfully provided by Japheth.
REPLACE_ACTION_TEXT can be used with regular expressions (regexp). So, some actions such as CreateCreature("blah",[0.0],1) won’t be matched by WeiDU because when you say [0.0] it is looking for a character set.
Here’s one dialogue that I’m fixing and how to do it correctly.
Arghai.dlg has this action trigger before REPLACE_ACTION_TEXTing it.
CreateCreature("OGREHA",[1351.1078])
There’s no point given, which makes WeiDU and NI angry. To fix it using REPLACE_ACTION_TEXT this is what you have to do:
REPLACE_ACTION_TEXT arghai ~CreateCreature("OGREHA",\[1351.1078\])~ ~CreateCreature("OGREHA",[1351.1078],0)~
So, all you have to remember to do is escape the square brackets with a backslash so WeiDU doesn’t confuse them with a regular expression.
This tutorial was thoughtfully provided by CamDawg.
ALTER_TRANS is a more compatibility-friendly way of changing transitions in an existing dialogue state. This is best illustrated with an example; let’s use state 9 from doghma.dlg in BG2:
IF ~~ THEN BEGIN 9 SAY #45751 IF ~~ THEN REPLY #45752 GOTO 2 IF ~GlobalLT("chapter","GLOBAL",4)~ THEN REPLY #45753 GOTO 5 IF ~~ THEN REPLY #45754 GOTO 4 IF ~Global("RevealUmar","GLOBAL",1) PartyHasItem("miscbl") PartyHasItem("miscbm") PartyHasItem("miscbn") PartyHasItem("miscbo")~ THEN REPLY #57922 GOTO 10 IF ~~ THEN REPLY #45755 GOTO 1 END
The problem lies in transition 3, which should have an OR(4) before those four PartyHasItem checks. Before ALTER_TRANS, the least destructive way to fix it would be
ADD_TRANS_TRIGGER DOGHMA 9 ~False()~ DO 3 EXTEND_BOTTOM DOGHMA 9 IF ~Global("RevealUmar","GLOBAL",1) OR(4) PartyHasItem("miscbl") PartyHasItem("miscbm") PartyHasItem("miscbn") PartyHasItem("miscbo")~ THEN REPLY #57922 GOTO 10 END
This presents some compatibility issues though. If another mod has altered this transition (for example, a new action via ADD_TRANS_ACTION) then those changes are lost via this method. It also reorders the replies as they’re displayed on-screen--not a big issue for transitions involving replies, but it is a big issue for non-reply transitions where they’re evaluated from the bottom-up. It’s also an issue for other mods that target this transition, as the False() essentially eliminates it. Enter ALTER_TRANS:
ALTER_TRANS DOGHMA // file name BEGIN 9 END // state number (can be more than one) BEGIN 3 END // transition number (can be more than one) BEGIN // list of changes, see below for flags "TRIGGER" ~Global("RevealUmar","GLOBAL",1) OR(4) PartyHasItem("miscbl") PartyHasItem("miscbm") PartyHasItem("miscbn") PartyHasItem("miscbo")~ END
There are eight flags you can use in the list of changes. If you do not use a particular flag, the previous value of the transition will be retained (i.e. not specifying an ACTION flag will preserve the current action of the transition).
"TRIGGER" ~Global("foo","GLOBAL",0)~
"ACTION" ~SetGlobal("foo","GLOBAL",1)~
"REPLY" ~#57922~
"JOURNAL" ~@100~
EPILOGUE" ~EXTERN BVICONI 0~
Let’s try one last example. This is bviconi.dlg, state 103:
IF WEIGHT #22 ~Global("LoveTalk","LOCALS",46)~ THEN BEGIN 103 SAY #10537 IF ~~ THEN REPLY #10538 GOTO 367 IF ~~ THEN REPLY #10539 EXTERN ~~ 0 END
As you can see the transition destination for the second transition is broken. In the olden days this would be fixed with
ADD_TRANS_TRIGGER BVICONI 103 ~False()~ DO 1 EXTEND_BOTTOM BVICONI 103 IF ~~ THEN REPLY #10539 GOTO 368 END
With ALTER_TRANS, it becomes much easier:
ALTER_TRANS BVICONI BEGIN 103 END BEGIN 1 END BEGIN "EPILOGUE" ~GOTO 368~ END
Addendum: please note that it’s important to wrap the target (for example, REPLY or EPILOGUE) in quotes, tildas, or percentage signs.
Further addendum: REPLY, JOURNAL and friends will remove the feature (rather than add an empty journal line or whatever) if the associated string is empty.
At some point you will be done with your mod (a collection of CRE, ITM, D, etc., files) and you will want to package it up so that other users can install it (and then perhaps uninstall it later) easily. WeiDU can handle this task for you (and you may freely distribute WeiDU.exe with your module).
A TP2 describes how to install components of your module. WeiDU will read the file, ask the user questions, and then perform the installation. Uninstallation and upgrading are also handled.
See the file examples/mymod.tp2 for a commented example of how this all works.
Here is the context-free grammar syntax for the TP2 file format:
TP2 File | A TP2 File is a text file that contains a number of mod Components. TP2 Files tell WeiDU how to install various parts of your mod on an end-user’s computer. | ||||||||||||||||||||||||||||||||||
is | BACKUP directoryName TP2 Flag list Language list Component list | A TP2 File is basically a prologue and then a list of Components. The BACKUP declaration tells WeiDU where to put backed-up versions of files that would be overwritten so that they can be uninstalled later. This directory will be created automatically if it is missing. The AUTHOR directive gives an email address for users to send bugs to if there are problems during the installation. You may use SUPPORT as an alias for AUTHOR. The TP2_AUTHOR variable is set to the “emailAddress” value. The TP2_FILE_NAME variable is set to the name of the tp2 file. The TP2_BASE_NAME variable is set to the name of the tp2, excluding any initial “setup-” and file extension. For “setup-mymod.tp2”, TP2_FILE_NAME is set to “setup-mymod.tp2”, while TP2_BASE_NAME is set to “mymod”. The MOD_FOLDER variable is set to the directory containing the TP2 file, if there is one, otherwise it is set to the root directory of directoryName. TP2 Flags set global options. Languages are the various languages in which your mod is available. The Finally, the Components make up the actual meat of your mod. Different Components can be installed or uninstalled separately, but all of the parts within a Component are treated as a unit. | |||||||||||||||||||||||||||||||||
TP2 Flag | A TP2 Flag declaration tells WeiDU to apply some global option to your TP2 file. | ||||||||||||||||||||||||||||||||||
is | AUTO_TRA path | The AUTO_TRA flag is used with the COMPILE TP2 Action. It automatically loads TRA files that match your D files. The TRA file loaded by AUTO_TRA takes precedence over all other TRA files. Variables are evaluated when the action is processed. | |||||||||||||||||||||||||||||||||
or | ALLOW_MISSING file list | ALLOW_MISSING directive allows you to specify files that can be missing (when you try to copy them or reference them from D files). Empty versions of those files will be created on demand. Try to use ACTION_IF instead of this. | |||||||||||||||||||||||||||||||||
or | ASK_EVERY_COMPONENT | This flag instructs WeiDU to ask about installing every component in this TP2 file individually, rather than asking questions like "Would you like to install them all?" | |||||||||||||||||||||||||||||||||
or | ALWAYS TP2 Action list END | This flag specified a TP2 Action that is executed at the beginning of each Component, before the component-specific TP2 Action. | |||||||||||||||||||||||||||||||||
or | README file list | The given file is displayed to the user after choosing the language, but before installing any component. If more than one file name is provided, they are checked for existence in the order they’re provided, and the first available one will be opened. If you wish to open multiple readme files, use multiple README statements. | |||||||||||||||||||||||||||||||||
or | UNINSTALL_ORDER String list | Do not use this without a real reason. Specifies the order in which uninstallation operations are handled. The operations are STRSET (uninstall STRING_SET and ALTER_TLK), MOVE (uninstall MOVE), AT (perform AT_UNINSTALL and AT_INTERACTIVE_UNINSTALL) and COPY (uninstall all various file operations). If not specified, the order is MOVE - STRSET - COPY - AT. If this is specified, then ALL operations must be specified (if any isn’t, a warning is printed). Please note that this means that your mod might start printing warnings three years down the line if a new uninstall action is coded. | |||||||||||||||||||||||||||||||||
or | MODDER String list | Enable additional debug info. By default, these messages are verbose but do not halt the installation. Some options are configurable, see the in-depth description. | |||||||||||||||||||||||||||||||||
or | VERSION String | "String" (variables evaluated) will be appended to every component’s name in the weidu.log file. The variable MOD_VERSION evaluates to the string set by VERSION or to the empty string if the VERSION flag is not present. | |||||||||||||||||||||||||||||||||
or | SCRIPT_STYLE style | This flag determines how WeiDU will read in BAF and BCS files and write out BAF and BCS files. Possible options for “style” include BG (the default), IWD1, IWD2, and PST. See the Scripting Styles tutorial. | |||||||||||||||||||||||||||||||||
or | NO_IF_EVAL_BUG | this action solves a long-standing bug with the IF_EVAL action. Since solving it is not directly possible without damaging backwards compatibility, you have to use this action (preferably in an ALWAYS statement) to solve the bug for yourself. IF_EVAL will not work with this action in the TP2 - use PATCH_IF and BUT_ONLY_IF_IT_CHANGES instead. | |||||||||||||||||||||||||||||||||
or | QUICK_MENU lots of parameters | Used to specify one or more groups of components that can be installed in a single step. See the QUICK_MENU tutorial for syntax, usage instructions, and caveats. | |||||||||||||||||||||||||||||||||
or | AUTO_EVAL_STRINGS | EVALUATE_BUFFER is used whenever variables are evaluated. In particular, you can now "forget" about using it in FUNCTIONs and arrays. Note, TP2 defines multiple instances of EVALUATE_BUFFER, like EVALUATE_BUFFER, but these are not affected by AUTO_EVAL_STRINGS. N.B. This adds an additional level of EVAL to OUTER_TEXT_SPRINT and TEXT_SPRINT, but not to OUTER_SPRINT or SPRINT. | |||||||||||||||||||||||||||||||||
Language | A Language declaration tells WeiDU where to find TRA files. | ||||||||||||||||||||||||||||||||||
is | LANGUAGE languageName languageDirectory defaultLanguageTRA list | The languageName is the name of the language as it is presented to the user. "American English" and "Italiano" are examples. The languageDirectory is the name of the subdirectory in which you have stored the TRA files for that language. Examples include "american" and "spanish". The variable named LANGUAGE is set to languageDirectory if the user selects this language.. Finally, all of the TRA files in the defaultLanguageTRA list are loaded as soon as the user selects a language. | |||||||||||||||||||||||||||||||||
Component | A Component is a contiguous group of files and actions that a user can install, uninstall or upgrade. | ||||||||||||||||||||||||||||||||||
is | BEGIN componentName Component Flag list TP2 Action list | Basically, if componentName is "Foo", the user will be asked: "Do you want to install Foo?". If so, all of the associated TP2 Actions are performed. If not, they are skipped. | |||||||||||||||||||||||||||||||||
Component Flag | A Component Flag determines how WeiDU treats a component. | ||||||||||||||||||||||||||||||||||
is | DEPRECATED String | Mark the given component as deprecated. If it is currently installed, it will be uninstalled and the given String will be displayed. The user will never be asked to install the given component -- it will be silently skipped in all listings. However, it will still take up a “component number”. | |||||||||||||||||||||||||||||||||
or | REQUIRE_COMPONENT modToUninstall modComponent String | Make this component so that it can only be installed if another component is installed. If that other component is not installed, the String will be displayed and the user will not get a chance to install this component. This is in some sense the opposite of the UNINSTALL TP2 Action. For example, REQUIRE_COMPONENT "setup-ease.tp2" "0" "You must have infinite stacking installed!" prevents a component from being installed unless the infinite stacking part of the Ease-of-Use mod is installed. | |||||||||||||||||||||||||||||||||
or | FORBID_COMPONENT modToUninstall modComponent String | Make this component so that it can only be installed if another component is not installed. This does the opposite of REQUIRE_COMPONENT. | |||||||||||||||||||||||||||||||||
or | REQUIRE_PREDICATE value String | This component can only be installed if the value evaluates to true (non-zero). | |||||||||||||||||||||||||||||||||
or | SUBCOMPONENT String [ value ] | At most one component of the given subcomponent group can be installed at any time. All subcomponents of the same group are listed together for the user. See the SUBCOMPONENT tutorial. | |||||||||||||||||||||||||||||||||
or | FORCED_SUBCOMPONENT String [ value ] | See the SUBCOMPONENT tutorial. | |||||||||||||||||||||||||||||||||
or | GROUP String [ value ] | Used for grouping components; the user will be asked to display these or not. See the GROUP tutorial. | |||||||||||||||||||||||||||||||||
or | INSTALL_BY_DEFAULT | If WeiDU would ask the user whether to install this component or not, and this component is not already installed, WeiDU will instead install it by default (without asking the user). If there is an error or the component is already installed, WeiDU will ask the user. The --uninstall command-line argument overrides this. See also REQUIRE_COMPONENT and ALWAYS. | |||||||||||||||||||||||||||||||||
or | DESIGNATED forcedNumber | Normally module components are numbered based on their order in the TP2 file (starting from 0). This flag sets the current component number to forcedNumber. The next component (if it lacks a DESIGNATED flag) will be forcedNumber+1. You can easily shoot yourself in the foot by setting forcedNumber too low (e.g., so that multiple components have the same number). | |||||||||||||||||||||||||||||||||
or | NO_LOG_RECORD | Normally all module components are recorded in WeiDU.log and can be uninstalled later. This component flag prevents this component from writing a log record when it is successfully installed. As a result it is “invisible” to WeiDU, can be installed multiple times, and cannot be uninstalled with WeiDU. Do not use this flag without a real reason. | |||||||||||||||||||||||||||||||||
or | LABEL String | Defines the LABEL, or unique textual identifier, of this module. Any component can have zero or more LABELs. Will print a warning if two components in the same tp2 share a LABEL. Do not alter a component’s LABEL once you publish that mod. | |||||||||||||||||||||||||||||||||
or | METADATA String | Associates a string of metadata with this component. WeiDU does not use the metadata for anything, but external programs might. METADATA may be used multiple times per component. | |||||||||||||||||||||||||||||||||
TP2 Action | A TP2 Action tells WeiDU how to install a component. This usually involves copying files and writing in new string references. | ||||||||||||||||||||||||||||||||||
is | COPY optNoBackup optGlob fromFile toFile ... patch list when list |
You may specify as many fromFile-toFile pairs as you like. Each
fromFile is copied to its associated toFile. If there are any
WeiDU variables inside explicit %s in toFile or fromFile,
they are replaced by their values. All of the patches
are applied. If there are any when conditions and any
of them are false, the copy does not happen. If fromFile is a
directory, toFile is also taken to be a directory and all files
(but not subdirectories) in fromFile are copied to toFile. A
typical example is COPY "mymod/sword.itm"
"override/m#sword.itm". COPY commands set the user-defined SOURCE_DIRECTORY, SOURCE_FILESPEC, SOURCE_FILE, SOURCE_RES, SOURCE_EXT, DEST_DIRECTORY, DEST_FILESPEC, DEST_FILE,DEST_RES and DEST_EXT variables based on fromFile and toFile as follows. If fromFile is mymod/cre/bigboss.cre, then SOURCE_DIRECTORY is mymod/cre, SOURCE_FILESPEC is mymod/cre/bigboss.cre, SOURCE_FILE is bigboss.cre, SOURCE_RES is bigboss and SOURCE_EXT is cre. The DEST_ variables are similarly based on toFile. In addition, SOURCE_SIZE is set to the size (in bytes) of the source file. This is generally only useful if you have enabled globbing. Any user-defined variables in toFile are replaced with their values. You may also reference these variables in patches. See the Module Distribution section for information about finding a good unique prefix for your mod-created resources. | |||||||||||||||||||||||||||||||||
or | COPY_EXISTING optNoBackup fromFile toFile ... | Behaves like COPY except that the fromFiles are drawn from the game BIFFs or override directory. This is useful for making changes to files that other mods may have changed as well. | |||||||||||||||||||||||||||||||||
or | COPY_EXISTING_REGEXP optNoBackup optGlob fromFileRegexp toDirOrFile ... patch list when list | Behaves like COPY_EXISTING except that fromFileRegexp may
contain regular expressions (regexps). Additionally, toDirOrFile
may reference matched groups in fromFileRegexp with \1,
\2, etc. If toDirOrFile is an existing directory or lacks a
file extension, it will be treated as a directory and all matching files
in the game BIFFs will be copied there. Otherwise, toDirOrFile
will be treated as a file. If GLOB is specified, matching files in override will also be patched and copied. If a file appears in both the BIFFs and the override folder, it will only be copied once. For example, if HARM.ITM is in the BIFFs and HARM2.ITM is in override, this code will copy and patch them both: COPY_EXISTING_REGEXP GLOB ~HARM.*.ITM~ ~override~ SAY // ... whatever | |||||||||||||||||||||||||||||||||
or | COPY_LARGE optNoBackup optGlob fromFile toFile ... | Behaves like COPY except that the fromFiles can be of arbitrary
size (the limit should be over a Gigabyte), but on the other side of
the coin you can’t apply patches to the copying. Like COPY, COPY_LARGE sets the variables SOURCE_DIRECTORY, SOURCE_FILESPEC, SOURCE_FILE, SOURCE_RES, SOURCE_EXT, SOURCE_SIZE, DEST_DIRECTORY, DEST_FILESPEC, DEST_FILE, DEST_RES and DEST_EXT. | |||||||||||||||||||||||||||||||||
or | COPY_RANDOM ( file1 list ) [ ( fileN list ) list ] patch list when list | This command works like COPY_EXISTING but the destination for any given source file in the file1-list is some other different file in the file1-list. Similarly, the destination for any file in the fileN-list is some other file in the fileN-list. This allows you to randomly shuffle categories of game resources. | |||||||||||||||||||||||||||||||||
or | COPY_ALL_GAM_FILES patch list when list | Copies Default.gam from the biff and the ones in the save/ and mpsave/ saved games, applying the list of patches. If there are any when conditions and any of them are false, the copy does not happen. NO BACKUP is applied to files in the save/ and mpsave/ directories. The savegame from the biffs is backed up as usual. | |||||||||||||||||||||||||||||||||
or | MOVE optNoBackup fromFile toFile ... ( directory-file-regexp ) toFile ... | If optNoBackup is specified, in addition to the usual effects, fromFile may be moved to toFile even if
one or both contain spaces in their names. Do not use this feature without a real reason. You may specify as many fromFile--toFile pairs as you like. Each fromFile is moved to its associated toFile. If fromFile is a file, toFile can be a file or a directory; if fromFile is a directory, toFile must be a directory; if fromFile doesn’t exist, an error is raised. If toFile already exists, MOVE is equivalent to COPY_LARGE. MOVE also supports the directory-file-regexp construct in place of fromFile; in this form, it must be wrapped in parenthesis, toFile must be a directory, and no error is raised if no file is matched. Examples: MOVE ~sourceFile~ ~destinationFile~ MOVE ~sourceFile~ ~destinationDirectory~ MOVE ~sourceDirectory~ ~destinationDirectory~ MOVE (~sourceDirectory~ ~^[A-M].*\.itm$~) ~destinationDirectory Safety notes: when uninstalling, MOVE is restored first, then all generic actions are restored, then AT_*_UNINSTALL is handled. As such, do your AT_NOW, then do your COPY, then do your MOVEs (exception: if you MOVE for biffing purposes, it is safe to call MAKE_BIFF after MOVE. Do not use the --make-biff command-line argument). | |||||||||||||||||||||||||||||||||
or | DELETE optNoBackup file ... | Removes one or more files from the file system. If file is a
directory, it will be recursively deleted together with anything
it contains. Do not use without a real reason. | |||||||||||||||||||||||||||||||||
or | DISABLE_FROM_KEY file ... | Removes one or more files from the chitin.key, while leaving the
containing bif file itself untouched. Do not use without a real reason. | |||||||||||||||||||||||||||||||||
or | CREATE type [ VERSION version ] resref [ patch list ] | Creates a new file of the specified type with the resource reference resref and a correct file extension for the file type. If there are any patches, they are applied to contents of the new file. Any existing file with the same name will be backed up, as normal. The file will be created structurally correct but will contain no non-zero data aside from the structural details. The file version will be native to the current game, or default to "V1.0", but can be overridden with the VERSION option, which takes a string specifying the desired version. CREATE supports the file types ARE, CRE, EFF, ITM, SPL and STO and all their known file versions (aside from EFF V1.0, which is never found as a file). | |||||||||||||||||||||||||||||||||
or | COMPILE [ EVALUATE_BUFFER ] sourceFile list [ patch list ] [ USING traFile list ] | This command compiles D and BAF source files. If
sourceFile is a directory, all D and BAF files within
that directory are processed individually. If there is EVALUATE_BUFFER,
all %variables% in the files are substituted with their values.After the special EVALUATE_BUFFER is executed, all other patches are executed. First, this loads all of the traFiles presented. If any of their paths contain %s, the %s is replaced with the languageDirectory of from the Language the user selected. If you specified AUTO_TRA mymod/%s above, WeiDU will also attempt to load mymod/languageDirectory/sourceFile.tra for every sourceFile in the list. Once all of the TRA files are loaded, the D and BAF files are compiled. Any DLGs or BCSs they create or modify are placed in the override directory. | |||||||||||||||||||||||||||||||||
or | CLEAR_MEMORY | removes all variables from the memory, then reloads the automatic ones (TP2_AUTHOR, TP2_FILE_NAME, TP2_BASE_NAME, MOD_FOLDER, LANGUAGE, WEIDU_ARCH, WEIDU_OS, COMPONENT_NUMBER, all numeric constants such as NAME1, the soundsets, or SCRIPT_OVERRIDE). INTERACTIVE is automatically set when you call the next component. Do not use this feature without a real reason. | |||||||||||||||||||||||||||||||||
or | CLEAR_ARRAYS | removes the names and contents of all arrays from the memory (while still keeping their values). | |||||||||||||||||||||||||||||||||
or | CLEAR_CODES | Removes all loaded functions and macros from the memory. | |||||||||||||||||||||||||||||||||
or | CLEAR_INLINED | Removes all inlined files from the memory. | |||||||||||||||||||||||||||||||||
or | CLEAR_EVERYTHING | Runs CLEAR_MEMORY, CLEAR_ARRAY, CLEAR_CODES and CLEAR_INLINED. | |||||||||||||||||||||||||||||||||
or | CLEAR_IDS_MAP | force WeiDU to remove all loaded IDSes in memory (otherwise, if you compile a script, append to an IDS and compile another script, WeiDU will not handle the new IDS entry). | |||||||||||||||||||||||||||||||||
or | ACTION_CLEAR_ARRAY String | ‘Forgets’ that the array string exists until its values are recalled. The actual variables’ values are kept, the only effect is that *PHP_EACH will not consider old values. | |||||||||||||||||||||||||||||||||
or | SILENT | makes WeiDU skip all messages; it’s reenabled once a component is installed (or fails to), or a PRINT, PATCH_PRINT, VERBOSE or PATCH_VERBOSE is found. | |||||||||||||||||||||||||||||||||
or | VERBOSE | undoes the latest SILENT command. | |||||||||||||||||||||||||||||||||
or | MKDIR dirName list | Instructs WeiDU to create all of the directories in the list. | |||||||||||||||||||||||||||||||||
or | RANDOM_SEED someInteger | Sets the random number generator seed to someInteger. This allows you to get reproducible results when using random functions. If you specify a string that is not a valid integer the system initializes itself (e.g., by using the current time). | |||||||||||||||||||||||||||||||||
or | ACTION_READLN variable | Waits for the user to provided an enter-terminated string and store it in variable. Said string will be stored and re-used during non-interactive reinstalls. See the READLN tutorial. | |||||||||||||||||||||||||||||||||
or | APPEND optNoBackup filename newText when list [ KEEP_CRLF ] | If there are no when conditions or they are all true, the
ASCII text newText is appended to the existing file filename (which
is read from the game BIFFs or the override folder).
Any variables in newText are replaced by their values. If KEEP_CRLF is not present, double newlines in the file (but not in newText) will be removed; otherwise, they’re kept. In any event, if the file ends with CRLF only newText and an ending CRLF will be written; otherwise, if the file does not end with CRLF, it will be written before newText. | |||||||||||||||||||||||||||||||||
or | APPEND_OUTER optNoBackup filename newText when list [ KEEP_CRLF ] | If there are no when conditions or they are all true, the
ASCII text newText is appended to the existing file filename (which
is treated as a path to a file).
Any variables in newText are replaced by their values. If KEEP_CRLF is not present, double newlines in the file (but not in newText) will be removed; otherwise, they’re kept. In any event, if the file ends with CRLF only newText and an ending CRLF will be written; otherwise, if the file does not end with CRLF, it will be written before newText. | |||||||||||||||||||||||||||||||||
or | APPEND_COL optNoBackup filename newText [ prepend ] when list | If there are no when conditions or they are all true, the
string newText is appended column-wise to the existing file filename (which
is read from the game BIFFs or the override folder).
If filename was:
A B C D E F X Y Z P Q Rand newText was "0 1 2 3", the result would be: A B C 0 D E F 1 X Y Z 2 P Q R 3You must have the same number of whitespace-separated words in newText as there are columns in filename. Prepend empty cells are assumed to be at the beginning of newText. A cell containing exactly $ is printed as empty. | |||||||||||||||||||||||||||||||||
or | APPEND_COL_OUTER optNoBackup filename newText [ prepend ] when list | If there are no when conditions or they are all true, the
string newText is appended column-wise to the existing file filename (which
is treated as a path to a file).
If filename was:
A B C D E F X Y Z P Q Rand newText was "0 1 2 3", the result would be: A B C 0 D E F 1 X Y Z 2 P Q R 3You must have the same number of whitespace-separated words in newText as there are columns in filename. Prepend empty cells are assumed to be at the beginning of newText. A cell containing exactly $ is printed as empty. | |||||||||||||||||||||||||||||||||
or | EXTEND_TOP existingBCS newFile patch list [ USING traName list ] | Loads existingFile (which may be BAF or BCS), prepends all of newBCS to the top of it, applies all of the patches, and then copies it to the override folder. User variables in the filenames existingFile and newFile are replaced by their values. Use EVALUATE_BUFFER if you want to evaluate variables inside the body of newFile before parsing it. | |||||||||||||||||||||||||||||||||
or | EXTEND_BOTTOM existingBCS newFile patch list [ USING traName list ] | As EXTEND_TOP, but the newFile file is put at the bottom of the existingBCS file. User variables in the filenames existingFile and newFile are replaced by their values. | |||||||||||||||||||||||||||||||||
or | EXTEND_TOP_REGEXP existingBCSregexp newFile patch list [ USING traName list ] | As EXTEND_TOP, but the newFile file is put at the top of the every BCS file that matches the regexp existingBCSregexp. | |||||||||||||||||||||||||||||||||
or | EXTEND_BOTTOM_REGEXP existingBCSregexp newFile patch list [ USING traName list ] | See EXTEND_TOP_REGEXP. | |||||||||||||||||||||||||||||||||
or | ACTION_IF value THEN BEGIN TP2 Action list END [ ELSE BEGIN TP2 Action list END ] | If value evaluates to true (non-zero), the TP2 Actions in the THEN-branch are executed. Otherwise, if an ELSE-branch is present, its commands are executed. Otherwise nothing happens. | |||||||||||||||||||||||||||||||||
or | ACTION_MATCH match-value WITH [ guard-value ... [ WHEN condition-value ] BEGIN TP2 Action list END ] [ ANY condition-value BEGIN TP2 Action list END ] ... DEFAULT TP2 Action list END |
The match-value is either a value or a literal expression
(strings or integers); values are evaluated, including for
variables and the array construct, and the result obtained is used
in the matching. Matching is done in order against guard-values for which any corresponding condition-value evaluates to true (not 0) and against guard-values that lack a condition-value. Guard-values are either values or literal expressions. Values are evaluated, including for variables and the array construct, and the result is case-insensitively matched as a regexp against the match-value. Condition-values must be values and must evaluate to integers. ANY matches any match-value for which the corresponding condition-value is true. If a match is found, the corresponding action list is executed and no further matching is done. If no match is found, the DEFAULT action list is executed. See the MATCH and TRY tutorial for additional information. | |||||||||||||||||||||||||||||||||
or | ACTION_TRY TP2 Action list WITH [ guard-value ... [ WHEN condition-value ] BEGIN TP2 Action list END ] [ ANY condition-value BEGIN TP2 Action list END ] ... DEFAULT TP2 Action list END | If evaluating the action list results in an error, the error is matched, as per ACTION_MATCH. N.B. TRY is generally not safe to use because many errors are intended to be fatal and if the mod installation were to proceed anyway, it might do so in an inconsistent state, with resource leaks or with other errors. ACTION_RERAISE mitigates or eliminates these risks, since the unsafe part is allowing the installation to continue and re-raising the error allows it to fail, like intended. See the MATCH and TRY tutorial for additional information. | |||||||||||||||||||||||||||||||||
or | ACTION_RERAISE | When used inside an ACTION_TRY, the matched error is re-raised. See the MATCH and TRY tutorial for additional information. | |||||||||||||||||||||||||||||||||
or | AT_EXIT commandToRun [ EXACT ] | Whenever this component attempts to be installed, commandToRun
is executed. Variables (e.g., %LANGUAGE%) in the string
commandToRun are replaced by their values. Note that the command is
executed even if the component does not install correctly, so
AT_EXIT should probably be the last command in a component.
/
and \ will instead be preserved as they are. The most common usage is:
AT_INTERACTIVE_EXIT ~VIEW mymod\README.txt~This causes your README file to be displayed using a system appropriate viewer. Here’s a more complicated example that pulls up a language-specific README if one is available: ACTION_IF FILE_EXISTS ~mymod\README.%LANGUAGE%.txt~ THEN BEGIN AT_INTERACTIVE_EXIT ~VIEW mymod\README.%LANGUAGE%.txt~ END ELSE BEGIN AT_INTERACTIVE_EXIT ~VIEW mymod\README.txt~ END | |||||||||||||||||||||||||||||||||
or | AT_INTERACTIVE_EXIT commandToRun [ EXACT ] | As AT_EXIT, but the command is only executed if the user specifically asked for the component to be installed or upgraded. | |||||||||||||||||||||||||||||||||
or | AT_UNINSTALL commandToRun [ EXACT ] | As AT_EXIT, but when this component is removed, commandToRun is executed. | |||||||||||||||||||||||||||||||||
or | AT_INTERACTIVE_UNINSTALL commandToRun [ EXACT ] | As AT_EXIT, but whenever the user specifically asks for this component to be removed, commandToRun is executed. Only the %LANGUAGE% variable is guaranteed to be replaced, so do not count on any others. | |||||||||||||||||||||||||||||||||
or | AT_UNINSTALL_EXIT commandToRun [ EXACT ] | As AT_UNINSTALL, but executed at the end of the WeiDU run (after printing ’SUCCESFULLY INSTALLED xyz’). | |||||||||||||||||||||||||||||||||
or | AT_INTERACTIVE_UNINSTALL_EXIT commandToRun [ EXACT ] | As AT_UNINSTALL_EXIT, but only whenever the user specifically asks for this component to be removed. | |||||||||||||||||||||||||||||||||
or | AT_NOW [ variable ] commandToRun [ EXACT ] | As AT_EXIT, but commandToRun is executed when found, rather than at the end of the installation. Use with oggdec, tisunpack, wav2acm... but not with readmes (you’ll create dozens of windows), nor with --make-biff (you’ll crash the installation). If the optional variable is provided, a TP2 variable is of that name is assigned the return value of commandToRun. | |||||||||||||||||||||||||||||||||
or | AT_INTERACTIVE_NOW [ variable ] commandToRun [ EXACT ] | As AT_NOW, but commandToRun is executed only if the installation is run interactively. | |||||||||||||||||||||||||||||||||
or | MAKE_BIFF name-of-biff BEGIN directory-file-regexp list END | Create a biff Data/name-of-biff.bif from the files that can be matched by directory-file-regexp. The chitin.key file is restored as a normal file, and reloaded whenever the component is installed or uninstalled (which means that its contents will be available exactly when the biff exists), while the .bif file itself is deleted or on uninstall as usual. | |||||||||||||||||||||||||||||||||
or | LOAD_TRA traName list | Loads all traName files (variable substitution is done; the file can be inlined) to be used in the following tp2 actions, as if you had declared them in LANGUAGE. | |||||||||||||||||||||||||||||||||
or | WITH_TRA traName list BEGIN TP2 Action list END | Creates a new TRA scope copied from the surrounding scope, loads all traName files (variable substitution is done; the file can be inlined) and evaluates the TP2 Actions. When WITH_TRA exits, the new TRA scope is discarded, that is, all TRA changes made by WITH_TRA or any of its enclosed actions are forgotten. | |||||||||||||||||||||||||||||||||
or | WITH_SCOPE BEGIN TP2 Action list END | Creates a new variable scope copied from the surrounding scope and evaluates the TP2 Actions inside the new scope. When WITH_SCOPE exits, the new scope is discarded, all new variables defined inside the scope and all changes to existing variables are forgotten. | |||||||||||||||||||||||||||||||||
or | UNINSTALL modToUninstall modComponent | If the given component of the given mod is currently installed, uninstall it before proceeding. Do not use this action without a real reason. This should only be used if you release a new version of a component under a new name. For example, many Tactics Mod components replace old Solaufein mod components. In order to prevent such a component from being installed twice, the Tactics version uninstalls the Solaufein version. | |||||||||||||||||||||||||||||||||
or | COPY_KIT oldKit newKit ( diffName diffValue list ) | Copies a copy of the oldKit kit called newKit. This copy is non-player
selectable, while all other fields can be edited by setting diffName to
the field to be changed and diffValue to the new value. Valid values for
diffName are:
clasweap weapprof abclasrq abclsmod abdcdsrq abdcscrq dualclas alignmnt clab lower mixed help luabbr 25stweap unusabilitiesCurrently, lower, mixed and help can only be numbers referring to a TLK string. Unusabilities requires to be both kit and class unusables, there isn’t a way to BAND or BOR them. | |||||||||||||||||||||||||||||||||
or | ADD_KIT internalKitName String1 String2 String3 String4 String6 String7 String8 String9 String10 String11 String12 String13 SAY text1 SAY text2 SAY text3 | This command allows you to add new kits to the BGII.
See the example file mymod.tp2 or the tutorial at
http://gibberlings3.net/forums/index.php?showtopic=584
for information on how to do this. ADD_KIT sets the variable %internalKitName% to the number your kit occupies in kitlist.2da. This value is exactly 0x4000 less than the number your kit is assigned in kit.ids. Also observe that the kit DWORD in the CRE file format uses a reversed WORD order, unlike all other fields in the CRE file format. Most arguments to ADD_KIT are strings to be appended verbatim to 2DA files, detailed as follows:
Strings that are appended verbatim need to include the kit name at the very start of the string. Strings that are not appended verbatim should not include the kit name, as it is added automatically. String9 is the path to the CLAB-style 2DA file of your kit. String10 is a list of which class and race combinations the kit should be available for, as per kittable.2da. String11 is the unusability information and parent class used in kitlist.2da. Note that if String12 denotes a custom HLA table, you need to copy the new 2DA file into the game manually. ADD_KIT merely appends to luabbr.2da. text1, text2 and text3 are, respectively, the lower name, mixed name and description of your kit. The BG2: ToB engine cannot handle more than 255 kits installed (if you
try to check | |||||||||||||||||||||||||||||||||
or | ADD_MUSIC internalMusicName newMUSFile |
newMUSFile is copied to the music directory and added to
songlist.2da under the name internalMusicName. The
variable %internalMusicName% is set to the number of this
new entry in songlist.2da. If newMUSFile already exists,
the variable %internalMusicName% is set to the existing
entry in songlist.2da and all other operations are
skipped. Variables are evaluated. Some versions of the Infinity Engine cannot handle more than 100 entries in songlist.2da. ADD_MUSIC will fail under those circumstances unless the appropriate versions of ToB Hacks or ToBEx are installed or the game is GemRB or BGEE. | |||||||||||||||||||||||||||||||||
or | ADD_SCHOOL schoolName removalString | Creates a new (guaranteed-unique) spell school (as per mschool.2da),
associate removalString (can be a tra reference) to its removal,
and sets the schoolName variable to the school identifier. If mschool.2da already contains schoolName, the variable is set to the school’s current identifier and no other operation is performed. | |||||||||||||||||||||||||||||||||
or | ADD_SECTYPE sectypeName removalString | Creates a new (guaranteed-unique) spell secondary type
(as per msectype.2da), associate removalString (can be a tra reference)
to its removal, and sets the sectypeName variable to the sectype
identifier. If msectype.2da already contains sectypeName, the variable is set to the sectype’s current identifier and no other operation is performed. | |||||||||||||||||||||||||||||||||
or | ADD_AREA_TYPE areaTypeName | Adds a new (guaranteed-unique) area type to areatype.ids, and sets
the areaTypeName variable to the index of the bit the type is associated
with: the type can be set with
WRITE_SHORT 0x48 THIS | (1 << areaTypeName) .If areatype.ids already contains areaTypeName, the variable is set to the type’s current identifier and no other operation is performed. Also note that only 8 area types can be added to an unmodded installation of BG2; as such, don’t add a new area type type unless you really need it (most likely, another modder is already using a type with a similar name, and you can share the type between your mods without issue). | |||||||||||||||||||||||||||||||||
or | ADD_PROJECTILE modpath/PROName.PRO | Appends an entry for PROName to PROJECTL.IDS and assigns it the next
available ProRef number. Then copies the file modpath/PROName.PRO
to the override folder. The new ProRef number can be accessed
through the variable %PROName% and used to updated the
Projectile type field of an ITM or SPL file’s Item Ability or
Spell Ability sub-structures. (The hexadecimal offsets for these
fields can be found using NearInfinity.) In the following example, a
new PRO file and an ITM file that will use it are added to the game:
ADD_PROJECTILE ~MyMod/MYDXP.PRO~ COPY ~MyMod/MYDART.ITM~ ~override/MYDART.ITM~ WRITE_SHORT 0x09c ~%MYDXP%~ | |||||||||||||||||||||||||||||||||
or | ADD_SPELL modpath/newSPLfile.SPL type level idsName patch list [ IF_EXISTING patch list END ] [ ON_DISABLE patch list END ] |
adds newSPLfile as a spell of type and level into the game (IE, SPPR102
or whatnot), in the first empty slot possible, appends the idsName
to spell.ids, and sets %idsName% to the 4-digit code
associated with idsName in Spell.ids. While you copy newSPLfile to
the override, you also apply the patches listed. If Spell.ids already contains a spell named idsName, of the same type and
level than the one you’re adding, then that spell is overwritten (rather
than adding a duplicate version). If you have the If Spell.ids already contains a spell named idsName, but of of different
type and/or level than the one you’re adding, then the current reference
is removed from spell.ids, and then the action proceeds as usual. In this
case, if there is the | |||||||||||||||||||||||||||||||||
or | ADD_JOURNAL [ EXISTING ] [ MANAGED ] [ TITLE ( text ) ] reference list [ USING traFile list ] |
If the game is not of an EE-type, this action does nothing. If
the game is of an EE-type, this action patches BGEE.SQL or
BGEE.LUA (depending on game version) with the provided quests and
journal entries, so they will work with the EE-type journal
system. For each of the provided references, which are taken to be journal entries, the first line of text, up to a newline character, is taken to be the title of the journal entry. Titles isolated in this way have any trailing whitespace and full stops trimmed. The title is added as a separate string to the TLK, together with the complete journal entry. The game files patched with the strrefs of the title and journal entry. Each unique title is given a unique quest id and journal entries that share titles end up sharing quest ids. If EXISTING is specified, the strref of the title is matched against the existing title strrefs. If a match is found, the journal entry is added under the quest id of the existing quest. Otherwise it is added as a new quest. Note that TITLE should be given a string reference in this case. ADD_JOURNAL cannot guarantee correct results if EXISTING is used without TITLE or if TITLE is given a string or tra reference instead of a string reference. If MANAGED is specified, all provided journal entries are given a unique, non-zero quest-group number. They share the same quest-group number regardless of whether they share the same title. If EXISTING is also specified, the quest-group numbers for all existing journal entries that match the provided titles are assigned a quest-group number, provided they do not already have one. This option does nothing unless the game version supports quest groups. If TITLE is specified, the provided text is used as the title for all provided journal entries. The titles of each individual journal entry remains unchanged, they are just all listed under the same title. For more information on ADD_JOURNAL and some usage examples, refer to the journal section of this tutorial. | |||||||||||||||||||||||||||||||||
or | STRING_SET indexOrString newValue list [ USING traFile ] | This command replaces each given string in the user’s TLK file with the associated newValue. If a traFile is given, that file is is read once before all of the replacements take place and its contents are forgotten after. | |||||||||||||||||||||||||||||||||
or | STRING_SET_EVALUATE value newValue list [ USING traFile ] | This command replaces the string at index value in the user’s TLK file with the associated newValue. If a traFile is given, that file is is read once before all of the replacements take place and its contents are forgotten after. If the index value equals the current number of strings in the TLK file (for example, through NEXT_STRREF), newValue will be added to the TLK file as a new string. This addition is made with no deduplication. Use with caution, as this will continue to grow the TLK file each time the mod is reinstalled. | |||||||||||||||||||||||||||||||||
or | STRING_SET_RANGE #min #max USING traFile | For every integer i between min and max (inclusive) we do STRING_SET i @i USING traFile (except that this command should be executed more rapidly). The command will fail if @i is not defined (either by traFile or by some other tra file in scope) for some i between min and max. | |||||||||||||||||||||||||||||||||
or | ALTER_TLK_RANGE value value BEGIN patch list END | Executes the given patch list on all the strings *currently* in dialog.tlk between the two given values (both inclusive). This means that strings added in the current mod installation will not be patched, and strings already altered via ALTER_TLK* and/or STRING_SET* will be patched from the pre-patch condition. | |||||||||||||||||||||||||||||||||
or | ALTER_TLK_LIST BEGIN value list END BEGIN patch list END | Executes the given patch list on all the strings *currently* in dialog.tlk from the given list. This means that strings added in the current mod installation will not be patched, and strings already altered via ALTER_TLK* and/or STRING_SET* will be patched from the pre-patch condition. | |||||||||||||||||||||||||||||||||
or | ALTER_TLK BEGIN patch list END | Executes the given patch list on all the strings *currently* in dialog.tlk. This means that strings added in the current mod installation will not be patched, and strings already altered via ALTER_TLK* and/or STRING_SET* will be patched from the pre-patch condition. Consider using ALTER_TLK_RANGE with the base game’s boundaries instead. | |||||||||||||||||||||||||||||||||
or | REQUIRE_FILE filename warningString | If filename does not exist (or the bif file is not referenced inside the chitin.key), warningString is displayed and this component cannot be installed. This is checked before any actions are executed. | |||||||||||||||||||||||||||||||||
or | FORBID_FILE filename warningString | If filename does exist (or the bif file is referenced inside the chitin.key), warningString is displayed and this component cannot be installed. This is checked before any actions are executed. | |||||||||||||||||||||||||||||||||
or | FAIL warningString | If this TP2 Action is executed, warningString is displayed and the component fails to install. | |||||||||||||||||||||||||||||||||
or | ABORT string | The string is displayed and installation of the component is undone. No error is implied. | |||||||||||||||||||||||||||||||||
or | WARN displayString | The string DisplayString is echoed to the user. Useful for debugging or status reports. If displayString contains %variable% references, their values will be displayed. Additionally, INSTALLED WITH WARNINGS is printed at the end of the install. | |||||||||||||||||||||||||||||||||
or | PRINT displayString | The string DisplayString is echoed to the user. Useful for debugging or status reports. If displayString contains %variable% references, their values will be displayed. | |||||||||||||||||||||||||||||||||
or | LOG displayString | The string DisplayString is echoed to setup-mymod.debug. If displayString contains %variable% references, their values will be displayed. | |||||||||||||||||||||||||||||||||
or | OUTER_TEXT_SPRINT variable stringWithVars | Any WeiDU variables (enclosed in %s) inside stringWithVars are replaced by their values and the resulting string (constructed at mod-installation time!) is assigned to the variable variable. | |||||||||||||||||||||||||||||||||
or | OUTER_SPRINT variable stringWithVars | Like OUTER_TEXT_SPRINT, but stringWithVars can be an @reference, and will break --traify. Use OUTER_TEXT_SPRINT instead. | |||||||||||||||||||||||||||||||||
or | OUTER_SNPRINT value variable stringWithVars | If value evaluates to zero or a positive number, N, the first
N characters of the evaluated contents of stringWithVars are
assigned to variable. If value evaluates to a
negative number, N, it’s instead the last N characters. Thus:
SPRINT author Jason SNPRINT 3 myvar "1:%author%"... assigns 1:J to myvar. | |||||||||||||||||||||||||||||||||
or | OUTER_SET variable = value | Update variable so that it is equal to value. | |||||||||||||||||||||||||||||||||
or | OUTER_SET variable += value | Equivalent to OUTER_SET variable = variable + value. | |||||||||||||||||||||||||||||||||
or | OUTER_SET variable -= value | Equivalent to OUTER_SET variable = variable - value. | |||||||||||||||||||||||||||||||||
or | OUTER_SET variable *= value | Equivalent to OUTER_SET variable = variable * value. | |||||||||||||||||||||||||||||||||
or | OUTER_SET variable /= value | Equivalent to OUTER_SET variable = variable / value. | |||||||||||||||||||||||||||||||||
or | OUTER_SET variable &= value | Equivalent to OUTER_SET variable = variable BAND value. | |||||||||||||||||||||||||||||||||
or | OUTER_SET variable |= value | Equivalent to OUTER_SET variable = variable BOR value. | |||||||||||||||||||||||||||||||||
or | OUTER_SET variable <<= value | Equivalent to OUTER_SET variable = variable BLSL value. | |||||||||||||||||||||||||||||||||
or | OUTER_SET variable >>= value | Equivalent to OUTER_SET variable = variable BLSR value. | |||||||||||||||||||||||||||||||||
or | OUTER_INNER_PATCH buffString BEGIN patch list END | Any WeiDU variables inside %s within buffString are replaced by their values. All of the patches given are evaluated as if the contents of the current file were buffString. Any modifications to buffString are thrown away. This is considered an action, not a patch. | |||||||||||||||||||||||||||||||||
or | OUTER_PATCH buffString BEGIN patch list END | Equal to OUTER_INNER_PATCH, if you feel that OUTER_INNER sounds wrong. | |||||||||||||||||||||||||||||||||
or | OUTER_INNER_PATCH_SAVE savevar buffString BEGIN patch list END | As with OUTER_INNER_PATCH, except that any modifications to buffString are stored inside savevar. | |||||||||||||||||||||||||||||||||
or | OUTER_PATCH_SAVE savevar buffString BEGIN patch list END | Equal to OUTER_INNER_PATCH_SAVE, if you feel that OUTER_INNER sounds wrong. | |||||||||||||||||||||||||||||||||
or | OUTER_WHILE value BEGIN TP2 Action list END | If value is non-zero, execute the given TP2 Action list and then repeat, re-evaluating the value. Be very careful when using this command. You can easily describe an infinite loop. See the WHILE loop tutorial for more information. | |||||||||||||||||||||||||||||||||
or | OUTER_FOR ( patch list ; value ; patch list ) BEGIN TP2 Action list END | The TP2 Action OUTER_FOR (init;pred;inc) BEGIN body END is equivalent to init WHILE pred BEGIN INNER_ACTION BEGIN body END inc END, except that it’s considered an action. Note that the predicate value cannot be empty. | |||||||||||||||||||||||||||||||||
or | ACTION_BASH_FOR directory-file-regexp BEGIN TP2 Action list END | for all files that match directory-file-regexp, sets a bunch of variables
and executes the actions for each file found. Assuming the file being copied
is somedir/yourfile.cre, the following variables will be set:
"%BASH_FOR_DIRECTORY%" = "somedir" "%BASH_FOR_FILESPEC%" = "somedir/yourfile.cre" "%BASH_FOR_FILE%" = "yourfile.cre" "%BASH_FOR_RES%" = "yourfile" "%BASH_FOR_EXT%" = "cre" "%BASH_FOR_SIZE%" = <size of somedir/yourfile.cre> | |||||||||||||||||||||||||||||||||
or | ACTION_DEFINE_ARRAY String1 BEGIN String2 list END | Sets the array $string1(0) ,$string1(1) etc. to the various elements in string2.
| |||||||||||||||||||||||||||||||||
or | ACTION_SORT_ARRAY_INDICES String [ ArrayIndicesSortType ] | Sorts the indices of the array given by String according to the order ArrayIndicesSortType. String is evaluated for variables and you can use the array construct. | |||||||||||||||||||||||||||||||||
or | GET_FILE_ARRAY String path regexp | The array $string(0) , $string{1} etc is set to the file names, including path, that match regexp in path. The path is relative to the installation directory (e.g. save/000000000-Auto-Save). | |||||||||||||||||||||||||||||||||
or | GET_DIRECTORY_ARRAY String path regexp | Like GET_FILE_ARRAY except regexp is matched against directories instead of files. | |||||||||||||||||||||||||||||||||
or | ACTION_DEFINE_ASSOCIATIVE_ARRAY String BEGIN key1 [ , key2 ... ] => result1 list END | Sets the array $string , indexed by the keys, to the corresponding results. The results can either be Strings or values.
Example:
ACTION_DEFINE_ASSOCIATIVE_ARRAY mix_with_blue BEGIN red => purple yellow => green END | |||||||||||||||||||||||||||||||||
or | ACTION_FOR_EACH String1 IN String list BEGIN TP2 Action list END | will set the string1 variable to each value in string list and process each action. | |||||||||||||||||||||||||||||||||
or | ACTION_PHP_EACH String1 AS String2 => String3 list BEGIN TP2 Action list END | For every value of the string1 array that has been created or read, sets
string2_0 to the first array parameter, string2_1 to the second array
parameter, etc. and string3 to the result of the array. string2 will also contain
the first array parameter (as a synonym of string2_0 ). then executes the
actions listed. More exhaustive documentation will be provided by SConrad.
| |||||||||||||||||||||||||||||||||
or | INCLUDE String list | loads the file string as if it were a list of tp2 actions and executes it (as if it had been typed inside your tp2). These files have by convenience tph as an extension, due to no reason other than randomness (you can use .tp1 or .xyzsucks if you’re so oriented). Please note that, to speed up inclusions, tph files are stored in memory, rather than re-read each time from the hard-disk. If you need automorphing code (IE, you want to edit your tph file), you can use REINCLUDE instead. | |||||||||||||||||||||||||||||||||
or | ACTION_INCLUDE String list | A synonym of INCLUDE. | |||||||||||||||||||||||||||||||||
or | REINCLUDE String list | Works like INCLUDE, but loads each time the tph file from the hard disk. This is slower, but required if you want to edit your tph file between an inclusion and another. | |||||||||||||||||||||||||||||||||
or | ACTION_REINCLUDE String list | A synonym of REINCLUDE. | |||||||||||||||||||||||||||||||||
or | <<<<<<<< fileName fileBody >>>>>>>> | (That’s eight (8) angle brackets on each side.)
For the purposes of copying and compiling files, WeiDU will pretend
that fileName is a real file with contents fileBody. This allows you to
define inlined files inside your TP2 file. Such definitions
must be executed before the inlined file is used. Other operations
on fileName (such as FILE_EXISTS, FILE_MD5 or
FILE_SIZE) are undefined. Inlined files will be skipped by
COPY_EXISTING_REGEXP and other “wildcard” approaches: you
must name them directly. Unlike most other WeiDU things,
spacing matters here. After the initial <<<<<<<< there can be
any number of spaces. The first non-space character after the
<<<<<<<< is the first character of fileName. All other
characters up to and excluding the newline on that line are part of
fileName (and thus fileName cannot start with a space or contain a
newline). All user variables (e.g., %foo%) in fileName will be
replaced by their values.
The fileBody contains all characters after that newline up to
and excluding the >>>>>>>>. Note that a single inlined filename
namespace is shared by WeiDU for all TP2 files it reads (it
might read other TP2 files in the process of installing yours
in order to uninstall or reinstall another mod’s components), so it is
critically important that you use some sort of unique prefix.
I also suggest that you avoid using your mod’s actual directory
structure (if any) in order to avoid confusion with real files.
Consider using .../yourmod-inlined/fileName. Here is a concrete
working example:
BEGIN ~MyMod Component 1~ <<<<<<<< .../mymod-inlined/myfile.baf IF True() THEN RESPONSE #100 Kill("Anomen") END >>>>>>>> COMPILE ~.../mymod-inlined/myfile.baf~This inclusion method is eight-bit clean, so if you are very careful you can inline a binary file (e.g., a CRE or BAM) with this method. Be careful to shave off the newline before the >>>>>>>> in such cases. Finally, note that inlined files have the same maximum size as other (non-BIFF) WeiDU files and strings (usually about 16 megs). | |||||||||||||||||||||||||||||||||
or | ACTION_TO_UPPER variable | Turns to upper-case the contents of %variable% and store the
results in variable. | |||||||||||||||||||||||||||||||||
or | ACTION_TO_LOWER variable | Turns to lower-case the contents of %variable% and store the
results in variable. | |||||||||||||||||||||||||||||||||
or | ACTION_GET_STRREF value variable | The string reference value is looked up in DIALOG.TLK and the (male) string (without any quotes) is stored in the variable. | |||||||||||||||||||||||||||||||||
or | ACTION_GET_STRREF_F value variable | The string reference value is looked up in DIALOGF.TLK and the female string (without any quotes) is stored in the variable. If there is no DIALOGF.TLK, the string from DIALOG.TLK is used instead. | |||||||||||||||||||||||||||||||||
or | ACTION_GET_STRREF_S value variable | The string reference value is looked up in DIALOG.TLK and the sound reference associated with the (male) string is stored in the variable. If the string has no sound, the empty string is used instead. | |||||||||||||||||||||||||||||||||
or | ACTION_GET_STRREF_FS value variable | The string reference value is looked up in DIALOGF.TLK and the sound reference associated with the female string is stored in the variable. If there is no DIALOGF.TLK, the sound reference associated with the string from DIALOG.TLK is used instead. If the string has no sound, the empty string is used instead. | |||||||||||||||||||||||||||||||||
or | DECOMPRESS_BIFF file ... | Decompresses one or more biff files from either "BIF V1.0" or "BIFCV1.0"
into "BIFFV1 ".
The biff file is looked for in /data and the CD paths specified in baldur.ini. You should
consequently not specify the biff path, merely the name. All matching biff files
are decompressed, even ones that are duplicated between CD paths. The biff file is backed up
to another file in the same directory before it is decompressed. Since multiple games
can share the same biffs, this action is not uninstalled together with the mod. WeiDU will raise a Failure exception if the biff is not found in any of the paths. Decompressing a biff file can be helpful in preventing crashes related to area files, commonly WAV ambient sounds or TIS files. Do not use this action without a real reason. | |||||||||||||||||||||||||||||||||
or | DEFINE_ACTION_MACRO String BEGIN TP2 Action list END | Define an action macro. Refer to the macros section for additional information. | |||||||||||||||||||||||||||||||||
or | DEFINE_PATCH_MACRO String BEGIN patch list END | Define a patch macro. Refer to the macros section for additional information. | |||||||||||||||||||||||||||||||||
or | DEFINE_ACTION_FUNCTION String [ INT_VAR variable = value ... ] [ STR_VAR variable = String ... ] [ RET variable ... ] [ RET_ARRAY variable ... ] BEGIN TP2 Action list END | Define an action function. Refer to the Functions section for additional information. | |||||||||||||||||||||||||||||||||
or | DEFINE_DIMORPHIC_FUNCTION String [ INT_VAR variable = value ... ] [ STR_VAR variable = String ... ] [ RET variable ... ] [ RET_ARRAY variable ... ] BEGIN TP2 Action list END | As DEFINE_ACTION_FUNCTION, except the function is additionally defined as a PATCH function. | |||||||||||||||||||||||||||||||||
or | DEFINE_PATCH_FUNCTION String [ INT_VAR variable = value ... ] [ STR_VAR variable = String ... ] [ RET variable ... ] [ RET_ARRAY variable ... ] BEGIN patch list END | Define a patch function. Refer to the Functions section for additional information. | |||||||||||||||||||||||||||||||||
or | LAUNCH_ACTION_MACRO String | Launch an action macro. Refer to the macros section for additional information. You may use LAM as a synonym for LAUNCH_ACTION_MACRO. | |||||||||||||||||||||||||||||||||
or | LAUNCH_ACTION_FUNCTION String [ INT_VAR variable [ = value ] ... ] [ STR_VAR variable [ = [ EVALUATE_BUFFER ] String ] ... ] [ RET variable [ = String ] ... ] [ RET_ARRAY variable [ = String ] ... ] END | Launch an action function. Refer to the Functions section for additional information. You may use LAF as a synonym for LAUNCH_ACTION_FUNCTION. | |||||||||||||||||||||||||||||||||
or | ACTION_TIME String BEGIN TP2 Action list END | Measure the time taken to execute the TP2 Actions and report it under the label String together with the other time measurements at the end of the mod’s debug file. | |||||||||||||||||||||||||||||||||
optNoBackup | A COPY, MOVE or DELETE command normally makes a backup copy of its target (if one exists) so that the mod can be uninstalled later by restoring the backup. | ||||||||||||||||||||||||||||||||||
is | If you don’t say anything here, WeiDU will make a backup copy of the file and the COPY, MOVE or DELETE will be undone if the mod is uninstalled. | ||||||||||||||||||||||||||||||||||
or | + | If you put a + here, WeiDU will not make a backup copy of the file and the COPY, MOVE or DELETE will not be undone if the mod is uninstalled. Do not use this feature without a real reason. | |||||||||||||||||||||||||||||||||
or | - | If you put a - here, WeiDU will not copy the file, but only store it as an inlined file (so you can patch a file and then use it for EXTEND_BOTTOM or whatever). This option is not available for COPY_LARGE, MOVE or DELETE. | |||||||||||||||||||||||||||||||||
directory-file-regexp | This is a directory path and a regexp string. It’s used by MAKE_BIFF, ACTION_BASH_FOR and PATCH_BASH_FOR for listing patterns to be matched. Please note that the directory / filename distinction must be enforced. | ||||||||||||||||||||||||||||||||||
is | directory regexp | get a list of all files in directory that match regexp. Case doesn’t matter. | |||||||||||||||||||||||||||||||||
or | directory EVALUATE_REGEXP regexp | get a list of all files in directory that match regexp. Case doesn’t matter. This is the same as the above item. | |||||||||||||||||||||||||||||||||
or | directory EXACT_MATCH name | get the file directory/name. Please note that the directory / filename distinction must be enforced. | |||||||||||||||||||||||||||||||||
optGlob | A COPY command may use globbing to expand filename wildcards with respect to files on the host filesystem. Unlike COPY_EXISTING_REGEXP, glob wildcards do not range over game resources. Instead, they range over physical files actually on the disk. | ||||||||||||||||||||||||||||||||||
is | GLOB | Use local filesystem globbing. Globbing is
generally architecture specific! Here is a concrete example. Imagine that CHITIN.KEY contains two files: C1.ITM and C2.ITM. The override contains C2.ITM and C_MOD.ITM. COPY_EXISTING_REGEXP "C.*.ITM" "override" // catches C1.ITM, C2.ITM COPY_EXISTING_REGEXP GLOB "C.*.ITM" "override" // catches C1.ITM, C2.ITM, C_MOD.ITM COPY "override" "override" // catches C2.ITM, C_MOD.ITM To put it another way: if you do not specify GLOB with COPY_EXISTING, WeiDU pretends that the override directory contains 0 files that are not in CHITIN.KEY. Finally, GLOB has no effect on wildcards in the "destination" part of the COPY: COPY GLOB "mymod/foo" "music/*" // this is illegal: DO NOT DO THIS | |||||||||||||||||||||||||||||||||
or | NOGLOB | Never use local file system globbing. | |||||||||||||||||||||||||||||||||
or | Use sane defaults: enable globbing with COPY_EXISTING_REGEXP, disable it with COPY. | ||||||||||||||||||||||||||||||||||
optcase | Allows you to decide whether regexp matching is case-sensitive or not. | ||||||||||||||||||||||||||||||||||
is | Uses the WeiDU default (which is undocumented and varies with the action). Please specify case-sensitiveness manually. | ||||||||||||||||||||||||||||||||||
or | CASE_SENSITIVE | matching is case-sensitive. | |||||||||||||||||||||||||||||||||
or | CASE_INSENSITIVE | matching is case-insensitive. | |||||||||||||||||||||||||||||||||
optexact | Allows you to decide whether regexp matching is exact. | ||||||||||||||||||||||||||||||||||
is | Uses the WeiDU default (which is undocumented and varies with the action). Please specify matching policy manually. | ||||||||||||||||||||||||||||||||||
or | EXACT_MATCH | only the given string is searched for. | |||||||||||||||||||||||||||||||||
or | EVALUATE_REGEXP | matching is evaluated following the usual regexp conventions. | |||||||||||||||||||||||||||||||||
ArrayIndicesSortType | Allows you to select how the indices in an array to be sorted are to be interpreted. | ||||||||||||||||||||||||||||||||||
is | The default sort is lexicographic, see LEXICOGRAPHICALLY. | ||||||||||||||||||||||||||||||||||
or | LEXICOGRAPHICALLY | The sorting is in ascending lexicographical order. This means each row of indices are interpreted as a string and that, for example, 10 is sorted before 2. | |||||||||||||||||||||||||||||||||
or | NUMERICALLY | The sorting is in ascending numerical order. This requires that the first column of indices can be interpreted as numbers; any strings will raise a warning and the sort order will be undefined. | |||||||||||||||||||||||||||||||||
patch | A patch tells WeiDU how to modify a file. | ||||||||||||||||||||||||||||||||||
is | SAY offset text | The string-ref associated with text is written at offset. This is commonly used to change the name or description of a spell or item. | |||||||||||||||||||||||||||||||||
or | PATCH_FAIL warningString | If this TP2 Action is execution, warningString is displayed and the component fails to install. | |||||||||||||||||||||||||||||||||
or | PATCH_ABORT string | The string is displayed and installation of the component is undone. No error is implied. | |||||||||||||||||||||||||||||||||
or | PATCH_WARN displayString | The string DisplayString is echoed to the user. Useful for debugging or status reports. If displayString contains %variable% references, their values will be displayed. Additionally, INSTALLED WITH WARNINGS is printed at the end of the install. | |||||||||||||||||||||||||||||||||
or | PATCH_PRINT displayString | The string DisplayString is echoed to the user. Useful for debugging or
status reports. If displayString contains %variable%
references, their values will be displayed. See also PRINT.
Example:
COPY_EXISTING_REGEXP ~.*\.CRE~ ~override~ READ_BYTE 0x272 race READ_BYTE 0x273 class PATCH_IF class = 3 THEN BEGIN PATCH_PRINT ~%SOURCE_FILE% is a cleric with race = %race%.~ END | |||||||||||||||||||||||||||||||||
or | PATCH_LOG displayString | The string DisplayString is echoed to setup-mymod.debug. If displayString contains %variable% references, their values will be displayed. | |||||||||||||||||||||||||||||||||
or | SAY_EVALUATED offset stringWithVars | Any WeiDU variables (enclosed in %s) inside stringWithVars are
replaced by their values and the resulting string (constructed at
mod-installation time!) is added to DIALOG.TLK and its string
reference it written to the offset. Example:
COPY_EXISTING_REGEXP ~RING.*.ITM~ ~override~ READ_LONG 0x38 cost SAY_EVALUATED IDENTIFIED_DESC ~I Am %SOURCE_RES%, I Cost %cost%~ | |||||||||||||||||||||||||||||||||
or | TO_UPPER variable | Turns to upper-case the contents of %variable% and store the
results in variable. | |||||||||||||||||||||||||||||||||
or | TO_LOWER variable | Turns to lower-case the contents of %variable% and store the
results in variable. | |||||||||||||||||||||||||||||||||
or | TEXT_SPRINT variable stringWithVars | Any WeiDU variables (enclosed in %s) inside stringWithVars are replaced by their values and the resulting string (constructed at mod-installation time!) is assigned to the variable variable. | |||||||||||||||||||||||||||||||||
or | SPRINT variable stringWithVars | Like TEXT_SPRINT, but stringWithVars can be an @reference, and will break --traify. Use TEXT_SPRINT instead. | |||||||||||||||||||||||||||||||||
or | SNPRINT value variable stringWithVars | If value evaluates to zero or a positive number, N, the first
N characters of the evaluated contents of stringWithVars are
assigned to variable. If value evaluates to a
negative number, N, it’s instead the last N characters. Thus:
SPRINT author Jason SNPRINT 3 myvar "1:%author%"... assigns 1:J to myvar. | |||||||||||||||||||||||||||||||||
or | SPRINTF variable stringWithVars ( variable or value list ) | In stringWithVars, any instance of %s, %d and %x will consume
one variable of the list of variables and format the contents of the variable
as a string, decimal number or hexadecimal number, respectively. Any normal text
in stringWithVars is reproduced verbatim. Variables enclosed in % are not
evaluated. The result is stored in the variable given as the first argument.
SET ten = 10 SPRINTF foo "%s %d %x quux" ("bar" 10 ten)will set the variable foo to the string ~bar 10 0xa quux~ | |||||||||||||||||||||||||||||||||
or | SOURCE_BIFF variable filename | If filename is in a biff (whether it is also in the override or not), sets
variable to the name of the containing biff (the name is printed as E.G.
data/items.bif ).
| |||||||||||||||||||||||||||||||||
or | SPACES variable stringWithVars | %variable% will be inited to a string the same length as stringWithVars,
but made entirely of spaces. SPACES var ~123~ will create %var% = " " .
| |||||||||||||||||||||||||||||||||
or | QUOTE variable stringWithVars | %variable% will be inited to a string that will match exactly stringWithVars.
QUOTE var ~some\thing^~ will create %var% = "some\\thing\^" .
| |||||||||||||||||||||||||||||||||
or | REPLACE optcase optexact regexp text | All occurrences of regexp in the file are replaced with the ASCII printing of the string reference for text. So if regexp is "FRED" and the text ends up being strref #1234, "FRED" will be replaced with "1234". This is usually used to replace string references in BCS files (where they are stored textually). Put a command like DisplayString(Myself,99999) in your BCS file and use something like REPLACE 99999 "Hello, World". | |||||||||||||||||||||||||||||||||
or | REPLACE_TEXTUALLY optcase optexact regexp string [ ( sizeValue ) ] | All occurrences of the given regexp in the file are replaced with the given string. variable substitution (e.g., kit and music names) is performed on both the string and the regexp. If you use the sizeValue field, optexact is automatically set to true (IE without regexp patterns). The regexp and the string are padded by zeros to be long exactly sizeValue bytes. | |||||||||||||||||||||||||||||||||
or | EVALUATE_BUFFER_SPECIAL string | String must be one character long. Any WeiDU variables (like XmyvarX)
inside the current file are replaced by their values, where X is to be intended
as string. Example:
<<<<<<<< .../mymod-inlined/script.baf IF See($myvar$) THEN RESPONSE #100 Kill(%myvar%) END >>>>>>>> EXTEND_TOP ~sola.bcs~ ~.../mymod-inlined/script.baf~ SPRINT myvar = ~"Anomen"~ EVALUATE_BUFFER_SPECIAL ~$~myvar will be substituted with Anomen, but %myvar% will be not. Do not use this without a real reason. | |||||||||||||||||||||||||||||||||
or | EVALUATE_BUFFER | Any WeiDU variables (like %myvar%) inside the current file (which
should probably be a plain text file in order for this to make much sense)
are replaced by their values. Example:
<<<<<<<< .../mymod-inlined/script.baf IF See(%myvar%) THEN RESPONSE #100 Kill(%myvar%) END >>>>>>>> EXTEND_TOP ~sola.bcs~ ~.../mymod-inlined/script.baf~ SPRINT myvar = ~"Anomen"~ EVALUATE_BUFFERThose two actions extend the top of sola.bcs with the script block IF See("Anomen") THEN RESPONSE #100 Kill("Anomen") END. You can also use EVALUATE_BUFFER in COMPILE actions or before strings in values. You may use EVAL as a synonym for EVALUATE_BUFFER. | |||||||||||||||||||||||||||||||||
or | APPLY_BCS_PATCH patchFile | Applies patchFile to the current file. See --bcmp-from and similar command-line arguments for constructing these patches. | |||||||||||||||||||||||||||||||||
or | APPLY_BCS_PATCH_OR_COPY patchFile copyFile | Applies patchFile to the current file, as APPLY_BCS_PATCH. However, if the patching fails the current file is replaced with copyFile instead. | |||||||||||||||||||||||||||||||||
or | WRITE_BYTE offset value | The first argument is the offset at which the second argument (an 8-bit
byte value) is written. Performs READ_BYTE offset THIS and
READ_SBYTE offset STHIS automatically (you can now write
WRITE_BYTE 100 THIS 2). | |||||||||||||||||||||||||||||||||
or | WRITE_SHORT offset value | The first argument is the offset at which the second argument (a 16-bit
short value) is written. Performs READ_SHORT offset THIS and
READ_SSHORT offset STHIS automatically. | |||||||||||||||||||||||||||||||||
or | WRITE_LONG offset value | The first argument is the offset at which the second argument (a 32-bit
long word value) is written. Performs READ_LONG offset THIS and
READ_SLONG offset STHIS automatically. | |||||||||||||||||||||||||||||||||
or | WRITE_ASCII offset ascString [ #requiredSize ] | The ASCII ascString is written to the file starting at offset. If you specify a requiredSize then exactly that many bytes are written (if ascString is smaller, it is padded with NULs; if ascString is larger, it is truncated). If you do not specify a requiredSize, the terminating NUL is not written. | |||||||||||||||||||||||||||||||||
or | WRITE_ASCII_LIST offset ascString list | The given ASCII strings are written at offset, offset + 8, offset + 16... Automatically evaluates variables. You may use WRITE_ASCIIL as a synonym for WRITE_ASCII_LIST. | |||||||||||||||||||||||||||||||||
or | WRITE_ASCII_TERMINATE offset ascString | The ASCII ascString is written to the file starting at offset. The terminating NUL is written. You may use WRITE_ASCIIT as a synonym for WRITE_ASCII_TERMINATE. | |||||||||||||||||||||||||||||||||
or | WRITE_EVALUATED_ASCII offset ascString [ #requiredSize ] | The ASCII ascString is evaluated (so %variable% is replaced by its value) and written to the file starting at offset (as in WRITE_ASCII). You may use WRITE_ASCIIE as a synonym for WRITE_EVALUATED_ASCII. | |||||||||||||||||||||||||||||||||
or | WRITE_FILE offset filename | The entire contents of “filename” (which may contain variables) are loaded and copied over the current file starting at offset offset. “filename” must be a literal filename like mymod/data/file.bam. If there is not enough room between offset and the end of the file for the contents of “filename” the patch will fail with an error message. | |||||||||||||||||||||||||||||||||
or | INSERT_FILE offset filename | Just like WRITE_FILE except that the entire contents of “filename” are inserted at offset, just as if you had done an INSERT_BYTES with the size of “filename” to that offset followed by a WRITE_FILE to that offset. | |||||||||||||||||||||||||||||||||
or | APPEND_FILE [ TEXT ] filename | Appends the contents of filename onto the current file. If TEXT is specified, the contents of filename and the current file is separated by a newline character (LF) unless the first character of filename or the last character of the current file is a newline character. | |||||||||||||||||||||||||||||||||
or | APPEND_FILE_EVALUATE [ TEXT ] filename |
Just like APPEND_FILE except that %variables% in the
contents of filename are evaluated before the appending. | |||||||||||||||||||||||||||||||||
or | REPLACE_BCS_BLOCK [ EVALUATE_BUFFER ] optcase oldFile newFile [ ON_MISMATCH patch list END ] | If the current file is a BCS file, the segment of it
corresponding to oldFile is replaced with the contents of newFile.
Multiple blocks can be replaced if they are contiguous and in order.
oldFile and newFile may be BCS or BAF files. If they
are BAF files they will not get the benefit of AUTO_TRA. If EVALUATE_BUFFER is given, variables inside oldFile and newFile are evaluated, but only they are BAF files. If the segment corresponding to oldFile is not found and the ON_MISMATCH section is present, those patches are run; if oldFile is not found and ON_MISMATCH is not present, a warning message is printed. You may use R_B_B as a synonym for REPLACE_BCS_BLOCK. | |||||||||||||||||||||||||||||||||
or | INSERT_BYTES offset value | The first argument is the offset, the second argument is the count. The file will be expanded at the given offset with count bytes worth of zeroes. | |||||||||||||||||||||||||||||||||
or | DELETE_BYTES offset value | The first argument is the offset, the second argument is the count. The file will shrink as count bytes are deleted starting at the given offset. | |||||||||||||||||||||||||||||||||
or | READ_BYTE offset variable [ ELSE value ] | An 8-bit value is read from the file at the given offset and is stored in the given variable. If offset is out-of-bounds and the ELSE is present, the ELSE-value is assigned to variable. If offset is out-of-bounds and the ELSE is not present, the patch fails with a visible error. | |||||||||||||||||||||||||||||||||
or | READ_SBYTE offset variable [ ELSE value ] | As READ_BYTE, but the value is interpreted as signed. | |||||||||||||||||||||||||||||||||
or | READ_SHORT offset variable [ ELSE value ] | A 16-bit value is read from the file at the given offset and is stored in the given variable. See READ_BYTE. | |||||||||||||||||||||||||||||||||
or | READ_SSHORT offset variable [ ELSE value ] | As READ_SHORT, but the value is interpreted as signed. | |||||||||||||||||||||||||||||||||
or | READ_LONG offset variable [ ELSE value ] | A 32-bit value is read from the file at the given offset and is stored in the given variable. See READ_BYTE. | |||||||||||||||||||||||||||||||||
or | READ_SLONG offset variable [ ELSE value ] | As READ_LONG, but the value is interpreted as signed. | |||||||||||||||||||||||||||||||||
or | READ_ASCII offset variable [ ELSE string ] [ ( value ) [ NULL ] ] | A nul-terminated string is read from the file at the given offset and is stored in the given variable. The terminating nul is not stored. The default read size is 8 bytes. If an explicit size value is specified then that many bytes are read into the variable, even if some of them are nuls, except if NULL is present, in which case a nul-terminated string is read, without storing the terminating null. See READ_BYTE. If the offset is out-of-bounds and the ELSE clause is present, the string is evaluated as in WRITE_EVALUATED_ASCII and then assigned into variable. | |||||||||||||||||||||||||||||||||
or | READ_STRREF offset variable [ ELSE string ] | A 32-bit Infinity Engine string reference is read from the file at the given offset. The string reference is looked up in DIALOG.TLK and the (male) string (without any quotes) is stored in the variable. In some sense this is the opposite of SAY_EVALUATED. | |||||||||||||||||||||||||||||||||
or | READ_STRREF_F offset variable [ ELSE string ] | A 32-bit Infinity Engine string reference is read from the file at the given offset. The string reference is looked up in DIALOGF.TLK and the female string (without any quotes) is stored in the variable. | |||||||||||||||||||||||||||||||||
or | READ_STRREF_S offset variable [ ELSE string ] | A 32-bit Infinity Engine string reference is read from the file at the given offset. The string reference is looked up in DIALOG.TLK and the sound reference associated with the (male) string is stored in the variable. If the string has no sound, the empty string is used instead. | |||||||||||||||||||||||||||||||||
or | READ_STRREF_FS offset variable [ ELSE string ] | A 32-bit Infinity Engine string reference is read from the file at the given offset. The string reference is looked up in DIALOGF.TLK and the sound reference associated with the female string is stored in the variable. If there is no DIALOGF.TLK, the sound reference associated with the string from DIALOG.TLK is used instead. If the string has no sound, the empty string is used instead. | |||||||||||||||||||||||||||||||||
or | GET_OFFSET_ARRAY String seven values | The seven values are:
1. Offset 2. Read length of "Offset" 3. Iterations 4. Read length of "Iterations" 5. Index 6. Read length of "Index" 7. Length between iterations. WeiDU comes with a number of pre-defined sets of values for GET_OFFSET_ARRAY. These sets are: ARE_V10_ACTORS (0x54 4 0x58 2 0 0 0x110) ARE_V10_REGIONS (0x5c 4 0x5a 2 0 0 0xc4) ARE_V10_SPAWN_POINTS (0x60 4 0x64 4 0 0 0xc8) ARE_V10_ENTRANCES (0x68 4 0x6c 4 0 0 0x68) ARE_V10_CONTAINERS (0x70 4 0x74 2 0 0 0xc0) ARE_V10_AMBIENTS (0x84 4 0x82 2 0 0 0xd4) ARE_V10_DOORS (0xa8 4 0xa4 4 0 0 0xc8) ARE_V10_ANIMATIONS (0xb0 4 0xac 4 0 0 0x4c) ARE_V91_ACTORS (0x64 4 0x68 2 0 0 0x110) CRE_V10_KNOWN_SPELLS (0x2a0 4 0x2a4 4 0 0 0xc) CRE_V10_SPELL_MEM_INFO (0x2a8 4 0x2ac 4 0 0 0x10) CRE_V10_EFFECTS (0x2c4 4 0x2c8 4 0 0 0x108) CRE_V10_ITEMS (0x2bc 4 0x2c0 4 0 0 0x14) ITM_V10_HEADERS (0x64 4 0x68 2 0 0 0x38) ITM_V10_GEN_EFFECTS (0x6a 4 0x70 2 0x6e 2 0x30) SPL_V10_HEADERS (0x64 4 0x68 2 0 0 0x28) SPL_V10_GEN_EFFECTS (0x6a 4 0x70 2 0x6e 2 0x30) STO_V10_ITEMS_PURCHASED (0x2c 4 0x30 4 0 0 0x4) STO_V10_ITEMS_SOLD (0x34 4 0x38 4 0 0 0x1c) STO_V10_DRINKS (0x4c 4 0x50 4 0 0 0x14) STO_V10_CURES (0x70 4 0x74 4 0 0 0xc) WMP_AREAS (0x34 4 0x30 4 0 0 0xf0) WMP_LINKS (0x38 4 0x3c 4 0 0 0xd8) GET_OFFSET_ARRAY performs "Iterations" number of reads and sets the array | |||||||||||||||||||||||||||||||||
or | GET_OFFSET_ARRAY2 String eight values | The eight values are:
1. Offset2 2. Offset 3. Read length of "Offset" 4. Iterations 5. Read length of "Iterations" 6. Index 7. Read length of "Index" 8. Length between iterations WeiDU comes with a number of pre-defined sets of values for GET_OFFSET_ARRAY2. These sets are: ARE_V10_ITEMS (0x78 4 0x44 4 0x40 4 0x14) ARE_V10_REGION_VERTICES (0x7c 4 0x2a 2 0x2c 4 0x4) ARE_V10_CONTAINER_VERTICES (0x7c 4 0x54 2 0x50 4 0x4) ARE_V10_DOOR_OPEN_OUTLINE_VERTICES (0x7c 4 0x30 2 0x2c 4 0x4) ARE_V10_DOOR_CLOSED_OUTLINE_VERTICES (0x7c 4 0x32 2 0x34 4 0x4) ARE_V10_DOOR_OPEN_CELL_VERTICES (0x7c 4 0x4c 2 0x48 4 0x4) ARE_V10_DOOR_CLOSED_CELL_VERTICES (0x7c 4 0x4e 2 0x50 4 0x4) CRE_V10_SPELL_MEM (0x2b0 4 0xc 4 0x8 4 0xc) ITM_V10_HEAD_EFFECTS (0x6a 4 0x1e 2 0x20 2 0x30) SPL_V10_HEAD_EFFECTS (0x6a 4 0x1e 2 0x20 2 0x30) WMP_NORTH_LINKS (0x38 4 0x54 4 0x50 4 0xd8) WMP_WEST_LINKS (0x38 4 0x5c 4 0x58 4 0xd8) WMP_SOUTH_LINKS (0x38 4 0x64 4 0x60 4 0xd8) WMP_EAST_LINKS (0x38 4 0x6c 4 0x68 4 0xd8) As you can see, the value for Offset2 isn’t included in the sets, since it varies. "Offset2" corresponds to the result from GET_OFFSET_ARRAY. "Offset" is read from the start of the file. "Iterations" and "Index" are read from Offset2 + value. Apart from that GET_OFFSET_ARRAY2 functions like GET_OFFSET_ARRAY. A more detailed explanation can be had in the GET_OFFSET_ARRAY and GET_OFFSET_ARRAY2 tutorial. | |||||||||||||||||||||||||||||||||
or | DEFINE_ASSOCIATIVE_ARRAY String BEGIN key1 [ , key2 ... ] => result1 list END | Sets the array $string , indexed by the keys, to the corresponding results. The results can either be Strings or values.
Example:
DEFINE_ASSOCIATIVE_ARRAY mix_with_blue BEGIN red => purple yellow => green ENDYou may use PATCH_DEFINE_ASSOCIATIVE_ARRAY as a synonym for DEFINE_ASSOCIATIVE_ARRAY. | |||||||||||||||||||||||||||||||||
or | GET_STRREF value variable | The string reference value is looked up in DIALOG.TLK and the (male) string (without any quotes) is stored in the variable. | |||||||||||||||||||||||||||||||||
or | GET_STRREF_F value variable | The string reference value is looked up in DIALOGF.TLK and the female string (without any quotes) is stored in the variable. If there is no DIALOGF.TLK, the string from DIALOG.TLK is used instead. | |||||||||||||||||||||||||||||||||
or | GET_STRREF_S value variable | The string reference value is looked up in DIALOG.TLK and the sound reference associated with the (male) string is stored in the variable. If the string has no sound, the empty string is used instead. | |||||||||||||||||||||||||||||||||
or | GET_STRREF_FS value variable | The string reference value is looked up in DIALOGF.TLK and the sound reference associated with the female string is stored in the variable. If there is not DIALOGF.TLK, the sound reference associated with the string from DIALOG.TLK is used instead. If the string has no sound, the empty string is used instead. | |||||||||||||||||||||||||||||||||
or | SET variable = value | Update variable so that it is equal to value. | |||||||||||||||||||||||||||||||||
or | variable = value | Update variable so that it is equal to value. | |||||||||||||||||||||||||||||||||
or | [ SET ] variable += value | Equivalent to SET variable = variable + value. | |||||||||||||||||||||||||||||||||
or | [ SET ] variable -= value | Equivalent to SET variable = variable - value. | |||||||||||||||||||||||||||||||||
or | [ SET ] variable *= value | Equivalent to SET variable = variable * value. | |||||||||||||||||||||||||||||||||
or | [ SET ] variable /= value | Equivalent to SET variable = variable / value. | |||||||||||||||||||||||||||||||||
or | [ SET ] variable &= value | Equivalent to SET variable = variable BAND value. | |||||||||||||||||||||||||||||||||
or | [ SET ] variable |= value | Equivalent to SET variable = variable BOR value. | |||||||||||||||||||||||||||||||||
or | [ SET ] variable <<= value | Equivalent to SET variable = variable BLSL value. | |||||||||||||||||||||||||||||||||
or | [ SET ] variable >>= value | Equivalent to SET variable = variable BLSR value. | |||||||||||||||||||||||||||||||||
or | WHILE value BEGIN patch list END | If value is non-zero, execute the given patch list and then repeat, re-evaluating the value. Be very careful when using this command. You can easily describe an infinite loop. See the WHILE loop tutorial for more information. | |||||||||||||||||||||||||||||||||
or | FOR ( patch list ; value ; patch list ) BEGIN patch list END | The patch FOR (init;pred;inc) BEGIN body END is equivalent to init WHILE pred BEGIN body inc END. Note that the predicate value cannot be empty. | |||||||||||||||||||||||||||||||||
or | PATCH_BASH_FOR directory-file-regexp BEGIN patch list END | for all files that match directory-file-regexp, sets a bunch of variables
and executes the patches for each file found. Assuming the file being copied
is somedir/yourfile.cre, the following variables will be set:
"%BASH_FOR_DIRECTORY%" = "somedir" "%BASH_FOR_FILESPEC%" = "somedir/yourfile.cre" "%BASH_FOR_FILE%" = "yourfile.cre" "%BASH_FOR_RES%" = "yourfile" "%BASH_FOR_EXT%" = "cre" "%BASH_FOR_SIZE%" = <size of somedir/yourfile.cre> | |||||||||||||||||||||||||||||||||
or | DEFINE_ARRAY String1 BEGIN String2 list END | Sets the array $string1(0) ,$string1(1) etc. to the various elements in string2.
| |||||||||||||||||||||||||||||||||
or | PATCH_DEFINE_ARRAY String1 BEGIN String2 list END | Same as DEFINE_ARRAY. | |||||||||||||||||||||||||||||||||
or | SORT_ARRAY_INDICES String [ ArrayIndicesSortType ] | Semantically identical to ACTION_SORT_ARRAY_INDICES | |||||||||||||||||||||||||||||||||
or | PATCH_FOR_EACH String1 IN String list BEGIN patch list END | will set the string1 variable to each value in string list and process each patch. | |||||||||||||||||||||||||||||||||
or | PHP_EACH String1 AS String2 => String3 list BEGIN patch list END | For every value of the string1 array that has been created or read, sets
string2_0 to the first array parameter, string2_1 to the second array
parameter, etc. and string3 to the result of the array. string2 will also contain
the first array parameter (as a synonym of string2_0 ). then executes the
patches listed. More exhaustive documentation will be provided by SConrad.
| |||||||||||||||||||||||||||||||||
or | PATCH_PHP_EACH String1 AS String2 => String3 list BEGIN patch list END | A synonym of PHP_EACH. | |||||||||||||||||||||||||||||||||
or | CLEAR_ARRAY String | ‘Forgets’ that the array string exists until its values are recalled. The actual variables’ values are kept, the only effect is that *PHP_EACH will not consider old values. | |||||||||||||||||||||||||||||||||
or | PATCH_CLEAR_ARRAY String | A synonym of CLEAR_ARRAY. | |||||||||||||||||||||||||||||||||
or | PATCH_IF value [ THEN ] BEGIN patch list END [ ELSE BEGIN patch list END ] | If value is non-zero, execute the first patch list once. Otherwise, execute the second patch list (if any). As a convenient shorthand, you may omit the BEGIN-END in the ELSE branch if the ELSE branch contains exactly one patch. | |||||||||||||||||||||||||||||||||
or | PATCH_MATCH match-value WITH [ guard-value ... [ WHEN condition-value ] BEGIN patch list END ] [ ANY condition-value BEGIN patch list END ] ... DEFAULT patch list END |
The match-value is either a value or a literal expression
(strings or integers); values are evaluated, including for
variables and the array construct, and the result obtained is used
in the matching. Matching is done in order against guard-values for which any corresponding condition-value evaluates to true (not 0) and against guard-values that lack a condition-value. Guard-values are either values or literal expressions. Values are evaluated, including for variables and the array construct, and the result is case-insensitively matched as a regexp against the match-value. Condition-values must be values and must evaluate to integers. ANY matches any match-value for which the corresponding condition-value is true. If a match is found, the corresponding patch list is executed and no further matching is done. If no match is found, the DEFAULT patch list is executed. See the MATCH and TRY tutorial for additional information. | |||||||||||||||||||||||||||||||||
or | PATCH_TRY patch list WITH [ guard-value ... [ WHEN condition-value ] BEGIN patch list END ] [ ANY condition-value BEGIN patch list END ... DEFAULT patch list END | If evaluating the patch list results in an error, the error is matched, as per PATCH_MATCH. N.B. TRY is generally not safe to use because many errors are intended to be fatal and if the mod installation were to proceed anyway, it might do so in an inconsistent state, with resource leaks or with other errors. PATCH_RERAISE mitigates or eliminates these risks, since the unsafe part is allowing the installation to continue and re-raising the error allows it to fail, like intended. See the MATCH and TRY tutorial for additional information. | |||||||||||||||||||||||||||||||||
or | PATCH_RERAISE | When used inside a PATCH_TRY, the matched error is re-raised. See the MATCH and TRY tutorial for additional information. | |||||||||||||||||||||||||||||||||
or | PATCH_INCLUDE String list | loads the file string as if it were a list of tp2 patches and executes it (as if it had been typed inside your tp2). These files have by convention tpp as an extension, due to no reason other than randomness (you can use .tp1 or .xyzsucks if you’re so oriented). Please note that, to speed up inclusions, tpp files are stored in memory, rather than re-read each time from the hard-disk. If you need automorphing code (IE, you want to edit your tpp file), you can use PATCH_REINCLUDE instead. | |||||||||||||||||||||||||||||||||
or | PATCH_REINCLUDE String list | Works like PATCH_INCLUDE, but loads each time the tph file from the hard disk. This is slower, but required if you want to edit your tpp file between an inclusion and another. | |||||||||||||||||||||||||||||||||
or | PATCH_WITH_TRA traName list BEGIN patch list END | Creates a new TRA scope copied from the surrounding scope, loads all traName files (variable substitution is done; the file can be inlined) and evaluates the patches. When PATCH_WITH_TRA exits, the new TRA scope is discarded, that is, all TRA changes made by PATCH_WITH_TRA or any of its enclosed patches are forgotten. | |||||||||||||||||||||||||||||||||
or | PATCH_WITH_SCOPE BEGIN patch list END | Creates a new variable scope copied from the surrounding scope and evaluates the patches inside the new scope. When PATCH_WITH_SCOPE exits, the new scope is discarded, all new variables defined inside the scope and all changes to existing variables are forgotten. | |||||||||||||||||||||||||||||||||
or | SET_2DA_ENTRY value value value value | The first value is the row, the second is the column and the third is the required column count. The entry on the given column of the given row is set to the fourth value, but only rows with at least as many columns as the required column count are considered. The fourth value, the new entry, is evaluated specially: if it can be evaluated like a value (e.g., ‘‘3+4’’) it will be evaluated and its integer result will be written as an ASCII string. Otherwise if it is a single string (that is not a variable in scope) that string will be written at the new value. See the SET_2DA_ENTRY tutorial for more information. | |||||||||||||||||||||||||||||||||
or | SET_2DA_ENTRY_LATER string value value value | Defines an change to a 2DA file to be made at some point in the future. The change is stored in variables derived from the first parameter, string. The first two values are the row and column of the 2DA table to be changed and the third value the new value of the 2DA entry. Use SET_2DA_ENTRIES_NOW to flush the queued changes and update the 2DA table. If you use SET_2DA_ENTRY_LATER together with READ_2DA_ENTRY_FORMER you must use different string variables for the two. See the SET_2DA_ENTRY_LATER tutorial. | |||||||||||||||||||||||||||||||||
or | SET_2DA_ENTRIES_NOW string value | Flush the changes to the 2DA table that have been queued by SET_2DA_ENTRY_LATER. The first parameter, string, should be the same string you used for SET_2DA_ENTRY_LATER. The second value is the required column count. Rows with fewer columns are disregarded. See the SET_2DA_ENTRIES_NOW tutorial. | |||||||||||||||||||||||||||||||||
or | PRETTY_PRINT_2DA | Indents the 3rd line in the current file (which should be a 2da file). | |||||||||||||||||||||||||||||||||
or | PRETTY_PRINT_2DA value | Indents the value’th line (counting from 0) in the current file (which should be a 2da file). | |||||||||||||||||||||||||||||||||
or | INSERT_2DA_ROW value1 value2 String | String (after variable substitution) will be inserted as a new line in the table.
Value1 is the row count, value2 is the required column count. If row count is
the same as the number of long enough lines in the file, then the line will be inserted
at the end; if row count is lesser than the number of lines, it will be inserted just before
the nth line in the original file (starting count from 0); finally, if row count is greater
than the number of lines, the component will fail to install.
<<<<<<<< ~.../mymod-inlined/2dafile~ asd foo a b c d e f g h i >>>>>>>> COPY ~.../mymod-inlined/2dafile~ ~.../mymod-inlined/2dafile~ INSERT_2DA_ROW 3 3 ~4 4 4~ INSERT_2DA_ROW 2 3 ~3 3 3~ INSERT_2DA_ROW 1 3 ~2 2 2~ INSERT_2DA_ROW 0 3 ~1 1 1~will result in asd foo 1 1 1 a b c 2 2 2 d e f 3 3 3 g h i 4 4 4 | |||||||||||||||||||||||||||||||||
or | REMOVE_2DA_ROW value1 value2 | the Value1-th row from the 2da file containing at least value2 items will be removed. | |||||||||||||||||||||||||||||||||
or | PATCH_READLN variable | Waits for the user to provided an enter-terminated string and store it in variable. Said string will be stored and re-used during non-interactive reinstalls. See the READLN tutorial. | |||||||||||||||||||||||||||||||||
or | PATCH_RANDOM_SEED value | See RANDOM_SEED. | |||||||||||||||||||||||||||||||||
or | ADD_STORE_ITEM [ + ] itemName [ position ] charge1 charge2 charge3 flags stack [ unlimited ] | Add the item itemName to the current STO file. An optional + signifies that the new instance of the item should overwrite any existing instance of the same item. The item’s number of charges are given by the respective charge argument, which must take the form of #integer or ( value ). The optional position argument must be one of AFTER String, BEFORE String, LAST, FIRST or AT value. AFTER will place the new item behind the item given by String. BEFORE will place the item before the item provided by String. LAST will place the new item after all existing items. FIRST will place the new item as the first item in the store. AT will place the new item at the position given by value, with the first item having position 0. If no position argument is given, ADD_STORE_ITEM defaults to FIRST. The argument flags must be a string consisting of one of none, identified, unstealable, stolen, identified&stolen or identified&unstealable. The argument stack sets the number of item the store carries in the stack and must take the form of #integer or ( value ). The optional argument unlimited should be one of the strings limited or unlimited and controls whether the store should carry an inexhaustible stack of the new item, or not. limited is the default behaviour. See the ADD_STORE_ITEM tutorial for more information. Note: The patch function ADD_STORE_ITEM_EX offers similar functionality. | |||||||||||||||||||||||||||||||||
or | REMOVE_STORE_ITEM itmName list | Remove the listed items from the current store. Does nothing if an item is not for sale. Multiple instances are removed. Note: The patch function REMOVE_STORE_ITEM_EX offers similar functionality. | |||||||||||||||||||||||||||||||||
or | READ_2DA_ENTRY value value value variable | The first value is the row, the second is the column and the third is the required column count. The variable specified is set to the entry on the given column of the given row, but only column with at least as many columns as the required column count are considered. This is the reverse of SET_2DA_ENTRY. | |||||||||||||||||||||||||||||||||
or | READ_2DA_ENTRIES_NOW string value | The contents of the 2DA table is read in one go into an array derived from the first parameter, string. The second parameter, value, is the required column count. Rows with fewer columns are disregarded. The parameter string is also used to set a variable named after string that is set to the number of rows in the 2DA table with the required number of columns. Retrieve values from the constructed array with READ_2DA_ENTRY_FORMER. If you use READ_2DA_ENTRIES_NOW together with SET_2DA_ENTRY_LATER you must use different string variables for the two. See the READ_2DA_ENTRIES_NOW tutorial. | |||||||||||||||||||||||||||||||||
or | READ_2DA_ENTRY_FORMER string value value variable | Retrieves values from an array constructed by READ_2DA_ENTRIES_NOW. The first parameter, string, is the name of the array. The following two values are the 2DA row and column from which to read the entry. The value of the 2DA entry is stored in the last parameter, variable. See the READ_2DA_ENTRY_FORMER tutorial. | |||||||||||||||||||||||||||||||||
or | COUNT_2DA_ROWS value variable | The first value is the required column count. This command counts the number of rows in the current file (which should be a 2DA file) that have at least as many columns as the required column count and stores the result in the variable. | |||||||||||||||||||||||||||||||||
or | COUNT_2DA_COLS variable | This command counts the number of columns in the current file (which should be a 2DA file) and stores the result in the variable. | |||||||||||||||||||||||||||||||||
or | COUNT_REGEXP_INSTANCES optcase optexact regexp variable | This command counts the number of times regexp appears in the current file
and stores the result in the variable. Variable substitution is performed
on regexp prior to regexp handling (EG, if %var% = ~abc~ ,
~[%var%]~ is evaluated as ~[abc]~ ). | |||||||||||||||||||||||||||||||||
or | LOOKUP_IDS_SYMBOL_OF_INT variable idsFile value | The symbolic constant associated with value in idsFile (which may
contain user variables) is stored
in variable. If that doesn’t work, value is stored in variable.
Example:
LOOKUP_IDS_SYMBOL_OF_INT foo ~spell~ 1101 SPRINT myfile "SPELL" LOOKUP_IDS_SYMBOL_OF_INT bar ~%myfile%~ (0x44c + 1) LOOKUP_IDS_SYMBOL_OF_INT baz ~spell~ 77777Both foo and bar are CLERIC_BLESS while baz is 777777. | |||||||||||||||||||||||||||||||||
or | COMPILE_BAF_TO_BCS | Consider using DECOMPILE_AND_PATCH instead. The current file, which must be a valid BAF script, is compiled to a BCS. In general you should use the COMPILE TP2 Action instead, unless you are using other patch commands to modify the file under consideration. | |||||||||||||||||||||||||||||||||
or | DECOMPILE_BCS_TO_BAF | Consider using DECOMPILE_AND_PATCH instead. The current file, which must be a valid BCS script, is decompiled to a BAF. | |||||||||||||||||||||||||||||||||
or | DECOMPILE_DLG_TO_D | Consider using DECOMPILE_AND_PATCH instead. The current file, which must be a valid DLG file, is decompile to a textual D file (with string refs and no comments). Once you have a D file you can use other patch commands to change the actions and triggers around. You should use D actions (like REPLACE_ACTION_TEXT) instead whenever possible. | |||||||||||||||||||||||||||||||||
or | COMPILE_D_TO_DLG | Consider using DECOMPILE_AND_PATCH instead. The current file, which must be a valid D file that defines a single DLG file (via an obvious BEGIN action) is compiled to a DLG. Typically this is only used after a DECOMPILE_DLG_TO_D. | |||||||||||||||||||||||||||||||||
or | DECOMPILE_AND_PATCH BEGIN patch list END | Acts like
DECOMPILE_BCS_TO_BAF // or DLG_TO_D patch list COMPILE_BAF_TO_BCS // or D_TO_DLGexcept it enforces that the DECOMPILE is followed by the compile, can indiscriminately patch BCS and DLG files, and is guaranteed not to leave a decompiled file in the override (if decompiling, patching or recompiling fails, a warning is printed and the original version of the file is left in the override). | |||||||||||||||||||||||||||||||||
or | REFACTOR_TRIGGER optcase optexact String String | Works like REPLACE_TEXTUALLY, except that it only works on triggers
(for both D and BAF files)
and applies Boolean Algebra to ensure that the structure of the trigger is not
broken (like REPLACE_TEXTUALLY would do if you are substituting one trigger with
several and the matching trigger is inside an OR() block and/or negated). Caveats:
| |||||||||||||||||||||||||||||||||
or | REFACTOR_BAF_TRIGGER optcase optexact String String | Synonym for REFACTOR_TRIGGER. | |||||||||||||||||||||||||||||||||
or | REFACTOR_D_TRIGGER optcase optexact String String | Synonym for REFACTOR_TRIGGER. | |||||||||||||||||||||||||||||||||
or | REPLACE_EVALUATE optcase findRegexp BEGIN patch list END replaceRegexp |
For every instance of the regexp findRegexp found, the
patch list is evaluated (with the variable MATCHi set
to the ith matched group in findRegexp), variable substitution is
performed on replaceRegexp, and then findRegexp is replaced by
replaceRegexp. Note that REPLACE_EVALUATE defines up to 200
MATCHi variables, but that MATCH1 is normally the variable
matching the first group in findRegexp. Due to details of the
implementation, MATCH0 normally evaluates to the string matched
by the whole of findRegexp. Any writes done by the patch
list (e.g., SAY or WRITE_ASCII) are ignored:
SET should be the main component of the patch
list. For example:
COPY ~nice.baf~ ~mean.baf~ REPLACE_EVALUATE ~Give(\([0-9]+\),\([0-9]+\))~ BEGIN SET result = (MATCH1 + MATCH2) / 2 END ~Take(%result%)~This COPY TP2 Action would replace Give(10,20) with Take(15). optcase allows you to decide if the matching is case-sensitive or not. Matching is case-sensitive by default. | |||||||||||||||||||||||||||||||||
or | ADD_GAM_NPC npcCRE npcARE xCoord yCoord | See the ADD_GAM_NPC tutorial for more information about this action, which is used when adding NPCs to Baldur’s Gate 1. BG2 mods should not use this command. | |||||||||||||||||||||||||||||||||
or | ADD_MAP_NOTE xCoord yCoord color String | If the file currently being patched is an ARE area file, this
patch command adds a map note to it. Valid colors include: gray,
violent, green, orange, red, blue, darkblue, lightgray.
Example:
COPY_EXISTING ~ar0202.are~ ~override/ar0202.are~ ADD_MAP_NOTE #123 #777 ~violet~ ~This is my new map note! Yippee!~Special thanks to Japh for coding this feature. | |||||||||||||||||||||||||||||||||
or | ADD_KNOWN_SPELL splName spellLevel spellType | When applied to a CRE file, this patch causes the given spell to
be known. Note that spellLevel counts from 0 (e.g., you should say
#2 for a third-level Fireball). Possible values for spellType are
priest, innate and wizard. Example:
COPY_EXISTING ~some.cre~ ~override/some.cre~ ADD_KNOWN_SPELL ~SPPR314~ #2 ~priest~ // Unholy Blight is now known as a 3rd level priest spellSpecial thanks to Japh for coding this feature. | |||||||||||||||||||||||||||||||||
or | ADD_MEMORIZED_SPELL splName spellLevel spellType [ ( value ) ] |
When applied to a CRE file, this patch causes the given
spell to be memorized one more time (or value times if
specified). The spell is additionally added to the creature’s list
of known spells. Note that spellLevel counts from 0 (e.g., you
should say #2 for a third-level Fireball). Possible values for
spellType are priest, innate and wizard. Example:COPY_EXISTING ~some.cre~ ~override/some.cre~ ADD_MEMORIZED_SPELL ~SPPR314~ #2 ~priest~ ( 5 ) // Unholy Blight is now memorized five times as 3rd priest | |||||||||||||||||||||||||||||||||
or | REMOVE_KNOWN_SPELL splName list | When applied to a CRE file, this patch causes all of the
listed spells to be removed. Example:
COPY_EXISTING ~aerie.cre~ ~override/aerie.cre~ REMOVE_KNOWN_SPELL ~sppr101~ ~sppr102~Special thanks to Japh for coding this feature. | |||||||||||||||||||||||||||||||||
or | REMOVE_MEMORIZED_SPELL splName list | When applied to a CRE file, this patch causes all of the
listed spells to be removed from their memory. Example:
COPY_EXISTING ~aerie.cre~ ~override/aerie.cre~ REMOVE_MEMORIZED_SPELL ~sppr101~ ~sppr102~ | |||||||||||||||||||||||||||||||||
or | SET_BG2_PROFICIENCY String value | Removes all effects from the current CRE file that alter the proficiency specified by String (relative to Stats.ids). After that, if value is different from zero, add an effect to set said proficiency to value. | |||||||||||||||||||||||||||||||||
or | ADD_CRE_ITEM itmName charge1 charge2 charge3 flags slot [ EQUIP ] [ TWOHANDED ] [ NOMOVE ] | Add the item itmName to the current CRE file, with the number of charges determined by charge1, charge2 and charge3, respectively. Each charge should be an integer, either in the form of #integer or ( value ). The argument flags must be a string consisting of one of none, identified, unstealable, stolen, undroppable, identified&stolen, identified&unstealable, identified&undroppable, unstealable&undroppable, stolen&undroppable, identified&stolen&undroppable, identified&unstealable&undroppable. The argument slot should be a whitespace-separated list of entries chosen from helmet, armor, shield, gloves, lring, rring, amulet, belt, boots, weapon1, weapon2, weapon3, weapon4, quiver1, quiver2, quiver3, quiver4, cloak, qitem1, qitem2, qitem3, inv1, inv2, inv3, inv4, inv5, inv6, inv7, inv8, inv9, inv10, inv11, inv12, inv13, inv14, inv15, inv16. On PST, there are the additional slots tattoo1, tattoo2, tattoo3, earring1, earring2, inv17, inv18, inv19 and inv20. On IWD2, there are the additional slots shield1, shield2, shield3, shield4, inv17, inv18, inv19, inv20, inv21, inv22, inv23 and inv24. ADD_CRE_ITEM will add the item to the first empty slot in the list. If no slot is empty, the item will be placed in the first slot in the list and the item that previously occupied the slot will be moved to the general inventory. If the general inventory is full, the moved item will be discarded. Additionally, slot may contain one or more entries chosen from inv, qitem, quiver, weapon, ring. These entries unfold to the full range of corresponding slots. For example, qitem unfolds to qitem1, qitem2 and qitem3. On PST, you may additionally use tattoo and earring. On IWD2, you may additionally use shield. The optional argument EQUIP only applies to the weapon slots only and will make the creature have the weapon selected. The optional argument TWOHANDED also only applies to weapon slots and will cause items in the shield slot to be unequipped. The optional argument NOMOVE will cause the new item to be added to the specified slot(s) only if there is an empty slot, otherwise the new item is placed in the inventory. See ADD_CRE_ITEM tutorial. | |||||||||||||||||||||||||||||||||
or | REPLACE_CRE_ITEM itmName #charge1 #charge2 #charge3 flags slot [ EQUIP ] [ TWOHANDED ] | If there’s an item in the slot position, replace it; otherwise, process ADD_CRE_ITEM | |||||||||||||||||||||||||||||||||
or | REMOVE_CRE_ITEM itmName list | Remove the listed items from the current creature. Does nothing if an item is not possessed. Multiple instances are removed. | |||||||||||||||||||||||||||||||||
or | REMOVE_CRE_ITEMS | Remove all items from the current creature. | |||||||||||||||||||||||||||||||||
or | REMOVE_CRE_EFFECTS | Remove all effects from the current creature. | |||||||||||||||||||||||||||||||||
or | REMOVE_KNOWN_SPELLS | Remove all known spells from the current creature. | |||||||||||||||||||||||||||||||||
or | REMOVE_MEMORIZED_SPELLS | Remove all memorized spells from the current creature. | |||||||||||||||||||||||||||||||||
or | PATCH_SILENT | makes WeiDU skip all messages; it’s reenabled once a component is installed (or fails to), or a PRINT, PATCH_PRINT, VERBOSE or PATCH_VERBOSE is found. | |||||||||||||||||||||||||||||||||
or | PATCH_VERBOSE | undoes the latest SILENT or PATCH_SILENT command. | |||||||||||||||||||||||||||||||||
or | INNER_PATCH buffString BEGIN patch list END | Any WeiDU variables inside %s within buffString are replaced by
their values. All of the patches given are evaluated as if the
contents of the current file were buffString. Any modifications to
buffString are thrown away (making this mostly useful for reads).
Example:
INNER_PATCH "ABC" BEGIN READ_BYTE 0x2 "myvar" END PATCH_PRINT "myvar is %myvar%"This sequence will always print myvar is 67 (since 67 is the ASCII code for C). | |||||||||||||||||||||||||||||||||
or | INNER_PATCH_SAVE savevar buffString BEGIN patch list END | As with INNER_PATCH, except that any modifications to buffString are stored inside
savevar.
Example:
SPRINT foo "ABC" INNER_PATCH_SAVE bar "%foo%" BEGIN WRITE_BYTE 0x2 65 END PATCH_PRINT "foo is %foo%, bar is %bar%"This sequence will always print foo is ABC, bar is ABA (since 65 is the ASCII code for A). | |||||||||||||||||||||||||||||||||
or | INNER_PATCH_FILE resource BEGIN patch list END | Any WeiDU variables inside %s within resource are replaced by
their values. If the resulting resource is present in the game or in the
override folder, the patches given are evaluated as if the
current file were that resource. If not, nothing happens. Any
modifications to that resource are thrown away (making this mostly useful
for reads).
Example:
INNER_PATCH_FILE "SW1H01.ITM" BEGIN READ_BYTE 0x1 "myvar" END PATCH_PRINT "myvar is %myvar%"This sequence will always print myvar is 84 (since 84 is the ASCII code for T and SW1H01.ITM starts with ITM). | |||||||||||||||||||||||||||||||||
or | INNER_ACTION BEGIN TP2 Action list END | See the INNER_ACTION tutorial, but loosely the current COPY is paused, the given TP2 Actions are executed, and then the current COPY is resumed. Note that an INNER_ACTION should never modify a file that is being modified by the current action. For example, never put APPEND foo.2da inside of COPY_EXISTING foo.2da . More formally, if the inner action and the outer action both modify the same file, the results are undefined. | |||||||||||||||||||||||||||||||||
or | EDIT_SAV_FILE level [ ADD_IF_MISSING ] [ filename list ] BEGIN TP2 Patch list END |
The current file should be a SAV file. EDIT_SAV_FILE
will iterate over the files contained within the SAV file, set
the variable SAV_FILE to the name of the current file and
evaluate the patch list against the decompressed contents of
SAV_FILE. The file contents are then recompressed into the
SAV file using the specified compression level. level can be 0
to 9, where 0 means no compression, 1 is the fastest compression and
9 is the best compression. If the list of filenames is non-empty, only files whose name appear in that list are patched (the comparison is case-insensitive, variables and arrays are parsed if using EVALUATE_BUFFER or AUTO_EVAL_STRINGS). If the optional ADD_IF_MISSING is provided, the file will be loaded if it exists (determined with FILE_EXISTS_IN_GAME) and the patches are applied. If the file does not exist, the patches will instead be applied to the empty buffer. The resulting file is added to the SAV itself. | |||||||||||||||||||||||||||||||||
or | DECOMPRESS_REPLACE_FILE start length uncompressedlength | Decompress (ZLIB) the start...start+length-1 portion of the current
file (requires to specify the uncompressed length as well). The result overwrites
the current file. It’s equivalent to
DECOMPRESS_INTO_FILE start length uncompressedlength 0 BUFFER_LENGTH
| |||||||||||||||||||||||||||||||||
or | DECOMPRESS_INTO_FILE start length uncompressedlength overwritefrom overwriteto | Decompress (ZLIB) the start...start+length-1 portion of the current
file (requires to specify the uncompressed length as well). The result overwrites
the overwritefrom...overwriteto-1 portion of the current file.
| |||||||||||||||||||||||||||||||||
or | DECOMPRESS_INTO_VAR start length uncompressedlength varName | Decompress (ZLIB) the start...start+length-1 portion of the current
file (requires to specify the uncompressed length as well). The result is stored
into varName.
| |||||||||||||||||||||||||||||||||
or | COMPRESS_REPLACE_FILE start length level | Compress (ZLIB) the start...start+length-1 portion of the current
file (level is 0 to 9; 0 means don’t compress, 1 is fastest compression, 9 is best
compression). The result overwrites
the current file. It’s equivalent to
DECOMPRESS_INTO_FILE start length uncompressedlength 0 BUFFER_LENGTH
| |||||||||||||||||||||||||||||||||
or | COMPRESS_INTO_FILE start length level overwritefrom overwriteto | Compress (ZLIB) the start...start+length-1 portion of the current
file (level is 0 to 9; 0 means don’t compress, 1 is fastest compression, 9 is best
compression). The result overwrites
the overwritefrom...overwriteto-1 portion of the current file.
| |||||||||||||||||||||||||||||||||
or | COMPRESS_INTO_VAR start length level varName | Compress (ZLIB) the start...start+length-1 portion of the current
file (level is 0 to 9; 0 means don’t compress, 1 is fastest compression, 9 is best
compression). The result is stored into varName.
| |||||||||||||||||||||||||||||||||
or | LAUNCH_PATCH_MACRO String | Launch a patch macro. Refer to the macros section for additional information. You may use LPM as a synonym for LAUNCH_PATCH_MACRO. | |||||||||||||||||||||||||||||||||
or | LAUNCH_PATCH_FUNCTION String [ INT_VAR variable [ = value ] ... ] [ STR_VAR variable [ = [ EVALUATE_BUFFER ] String ] ... ] [ RET variable [ = String ] ... ] [ RET_ARRAY variable [ = String ] ... ] END | Launch a patch function. Refer to the Functions section for additional information. You may use LPF as a synonym for LAUNCH_PATCH_FUNCTION. | |||||||||||||||||||||||||||||||||
or | PATCH_TIME String BEGIN patch list END | Measure the time taken to execute the patches and report it under the label String together with the other time measurements at the end of the mod’s debug file. | |||||||||||||||||||||||||||||||||
when | A when clause gives you local control over when a COPY, COPY_EXISTING or APPEND_COL happens. If the COPY or COPY_EXISTING contains multiple files, each one is checked against the when clauses separately. | ||||||||||||||||||||||||||||||||||
is | IF_SIZE_IS fileSize | True if the input file size is fileSize. You may use I_S_I as a synonym for IF_SIZE_IS. | |||||||||||||||||||||||||||||||||
or | IF regexp | True if the input file contains regexp. | |||||||||||||||||||||||||||||||||
or | UNLESS regexp | False if the input file contains regexp. | |||||||||||||||||||||||||||||||||
or | BUT_ONLY_IF_IT_CHANGES | True only if the file is actually changed by patching actions. Unlike all other when clauses, this one is evaluated just before the result would be written out to the disk. You may use BUT_ONLY as a synonym for BUT_ONLY_IF_IT_CHANGES. | |||||||||||||||||||||||||||||||||
or | IF_EXISTS | True only if the file exists. For COPY, this check is done with FILE_EXISTS. For COPY_EXISTING, COPY_EXISTING_REGEXP, COPY_RANDOM, APPEND and APPEND_COL this check is done with FILE_EXISTS_IN_GAME. For COPY_ALL_GAM_FILES, this when clause is redundant. | |||||||||||||||||||||||||||||||||
offset | A offset is a normal value. | ||||||||||||||||||||||||||||||||||
value | An expression that evaluates to an integer. See --debug-value. | ||||||||||||||||||||||||||||||||||
is | integer | An absolute location or amount. You may format your numbers in decimal, hex, octal or binary. Use 0x for hex, 0o for octal and 0b for binary. | |||||||||||||||||||||||||||||||||
or | ( value ) | ||||||||||||||||||||||||||||||||||
or | value + value | Addition. | |||||||||||||||||||||||||||||||||
or | value - value | Subtraction. | |||||||||||||||||||||||||||||||||
or | value * value | Multiplication. | |||||||||||||||||||||||||||||||||
or | value / value | Division. Division by zero yields the value zero. Briefly, fractions are dropped and the result is an integer (so 11 / 6 = 1). More technically, “this division rounds the real quotient of its arguments towards zero” -- see http://caml.inria.fr/pub/docs/manual-ocaml/libref/Pervasives.html for more information. | |||||||||||||||||||||||||||||||||
or | value MODULO value | An operation that returns the remainder of a division between two numbers. If the second value is 0, the returned value is 0. You may use REM as a synonym. | |||||||||||||||||||||||||||||||||
or | BYTE_AT offset | The 8-bit value from the file at the given offset. | |||||||||||||||||||||||||||||||||
or | SBYTE_AT offset | The 8-bit signed value from the file at the given offset. | |||||||||||||||||||||||||||||||||
or | SHORT_AT offset | The 16-bit value from the file at the given offset. | |||||||||||||||||||||||||||||||||
or | SSHORT_AT offset | The 16-bit signed value from the file at the given offset. | |||||||||||||||||||||||||||||||||
or | LONG_AT offset | The 32-bit value from the file at the given offset. | |||||||||||||||||||||||||||||||||
or | SLONG_AT offset | The 32-bit signed value from the file at the given offset. | |||||||||||||||||||||||||||||||||
or | value ** value | Exponentiation. The first value is raised to the power of the second. This is done by converting both to floating point numbers, calculating the exponent, and converting the answer back to a 32-bit integer. If anything goes wrong the value zero is returned or the result is undefined. | |||||||||||||||||||||||||||||||||
or | value ** ( value value ) | Fractional exponentiation. a ** (b c) yields a raised to the
power of (b/c). All of the internal operations are done in floating
point. If anything goes wrong, zero is returned or the result is
undefined. Example:
SET x = 100 ** 2 SET y = 100 ** (1 2) // square root of 100 = 10 SET z = 100 ** (1 3) // cube root of 100 = 4.64 PATCH_PRINT "w = %w%, x = %x%, y = %y%, z = %z%"yields x = 10000, y = 10, z = 4. | |||||||||||||||||||||||||||||||||
or | value = value | Integer Equality. If the two values evaluate to equal integers, the result is 1. Otherwise, the result is 0. See STRING_COMPARE for comparing Strings. Synonym: ==. | |||||||||||||||||||||||||||||||||
or | NOT value | Negation. If the value is 0, the result is 1. Otherwise the result is 0. | |||||||||||||||||||||||||||||||||
or | ABS value | Absolute value. If the value is >= 0, the result is x. Otherwise the result is -x. | |||||||||||||||||||||||||||||||||
or | value != value | Integer Inequality. If the two values evaluate to equal integers, the result is 0. Otherwise the result is 1. See STRING_COMPARE for comparing Strings. | |||||||||||||||||||||||||||||||||
or | value OR value | Disjunction. If either value is non-zero, the result is 1. Otherwise, the result is 0. Synonym: ||. | |||||||||||||||||||||||||||||||||
or | value AND value | Conjunction. If both values are non-zero, the result is 1. Otherwise, the result is 0. Synonym: &&. | |||||||||||||||||||||||||||||||||
or | value BAND value | Bitwise And. 0b101 BAND 0b110 = 0b100. Synonym: &. | |||||||||||||||||||||||||||||||||
or | value BOR value | Bitwise Or. 0b101 BOR 0b110 = 0b111. Synonym: |. | |||||||||||||||||||||||||||||||||
or | value BXOR value | Bitwise Exclusive Or. 0b101 BXOR 0b110 = 0b011. Synonym: ^^. | |||||||||||||||||||||||||||||||||
or | BNOT value | Bitwise Not.
BNOT 0b111 = 0b1111111111111111111111111111000. Synonym: ` . | |||||||||||||||||||||||||||||||||
or | value BLSL value | Bitwise Logical Shift Left. 0b101 BLSL 2 = 0b10100. Synonym: <<. | |||||||||||||||||||||||||||||||||
or | value BLSR value | Bitwise Logical Shift Right. 0b101 BLSR 2 = 0b1. Synonym: >>. | |||||||||||||||||||||||||||||||||
or | value BASR value | Bitwise Arithmetic Shift Right. This is an arithmetic shift: the sign bit of the first value is replicated and inserted in the vacated bits. 0b101 BASR 2 = 0b1. | |||||||||||||||||||||||||||||||||
or | value > value | If the first value is greater than the second, the result is 1. Otherwise, the result is 0. | |||||||||||||||||||||||||||||||||
or | value >= value | If the first value is greater than or equal to the second, the result is 1. Otherwise, the result is 0. | |||||||||||||||||||||||||||||||||
or | value < value | If the first value is less than the second, the result is 1. Otherwise, the result is 0. | |||||||||||||||||||||||||||||||||
or | value <= value | If the first value is less than or equal to the second, the result is 1. Otherwise, the result is 0. | |||||||||||||||||||||||||||||||||
or | value ? value : value | An expression-valued conditional. If the first value is not 0 then the second value is evaluated and returned, otherwise the third value is evaluated and returned. | |||||||||||||||||||||||||||||||||
or | String STRING_COMPARE String | This is legacy syntax for STRING_EQUAL: This expression evaluates to 0 if and only if its two string arguments are equal (have the same length and the same contents). Otherwise it will evaluate to a negative or positive integer, depending on whether the first string argument would sort lexicographically before or after the second string argument. variables within the strings (e.g., “%mykit%”) are replaced by their values. Note that variables that you want expanded must be put in %’s, otherwise the raw text will be used. You may use STR_CMP as a synonym for STRING_COMPARE. This function works just like C’s strcmp. Note also that STRING_EQUAL and STRING_COMPARE are similar, but STRING_EQUAL has more intuitive return values. | |||||||||||||||||||||||||||||||||
or | String STRING_COMPARE_CASE String | This is legacy syntax for STRING_EQUAL_CASE: As STRING_COMPARE, but the comparison ignores case. That is, "ANOMEN" and "aNoMeN" are considered equal. | |||||||||||||||||||||||||||||||||
or | String STRING_EQUAL String | This expression evaluates to 1 if and only if its two string arguments are equal (have the same length and the same contents), otherwise it values to 0. variables within the strings (e.g., “%mykit%”) are replaced by their values. Note that variables that you want expanded must be put in %’s, otherwise the raw text will be used. Note also that STRING_EQUAL and STRING_COMPARE are similar, but STRING_EQUAL has more intuitive return values. | |||||||||||||||||||||||||||||||||
or | String STRING_EQUAL_CASE String | As STRING_EQUAL, but the comparison ignores case. That is, "ANOMEN" and "aNoMeN" are considered equal. You may use STR_EQ as a synonym for STRING_EQUAL_CASE. | |||||||||||||||||||||||||||||||||
or | String STRING_MATCHES_REGEXP String | As STRING_COMPARE_CASE, but the second string is treated as a regexp. Thus "AR1005" STRING_MATCHES_REGEXP "AR[0-9]+" evaluates to 0 (“no difference”). You may use STRING_COMPARE_REGEXP as a synonym. | |||||||||||||||||||||||||||||||||
or | String STRING_CONTAINS_REGEXP String | As STRING_MATCHES_REGEXP, but it evaluates to 0 if the first string
contains the second regexp. Thus "AR1005" STRING_CONTAINS_REGEXP
"[w-z]" evaluates to 1 (“mismatch”). Be warned that this does the exact opposite of what the name suggests. | |||||||||||||||||||||||||||||||||
or | GAME_IS String | Returns true if the IE game variant
is one of the entries in String, otherwise it returns false. String is a list of
whitespace-separated entries, chosen from (case doesn’t matter)
bg2=soa, tob, iwd2, pst, bg1, totsc, iwd=iwd1, how, totlm, tutu, tutu_totsc, bgt, ca, iwd_in_bg2, bgee, bg2ee, eet, iwdee, pstee .
In the list, bg2=soa means that bg2 and soa are synonyms,
and you can use whichever one you like. Please note that the items in the list are
defined to be mutually exclusive (except tob will detect both plain ToB
and ToB with BGT installed, for legacy purposes), so bg2 will detect a
SoA-only game, without either ToB or Tutu installed. As such, a mod which is
usable with any flavor of BG2 and any flavor of Tutu or BG: EE would contain
REQUIRE_PREDICATE GAME_IS ~bg2 tob tutu tutu_totsc bgee bg2ee~ . | |||||||||||||||||||||||||||||||||
or | ENGINE_IS String | Functions like GAME_IS
except bg2=soa will detect SoA-based Tutu games in addition to SoA games,
and tob will detect ToB-based Tutu games in addition to ToB and BGT games.
As such, a mod that requires Throne of Bhaal’s engine enhancements could use
REQUIRE_PREDICATE ENGINE_IS ~tob~ . | |||||||||||||||||||||||||||||||||
or | GAME_INCLUDES String |
Returns true if the IE game variant includes the game content
specified by String. String is a single game chosen from
bg1, totsc, bg2=soa, tob, pst, iwd=iwd1, how, totlm, iwd2, ca, sod . In
the list, bg2=soa means that bg2 and soa are
synonyms, and you can use whichever one you like. Please note that
unlike GAME_IS you only specify a single game, rather than a
list of games. As an example, GAME_INCLUDES ~tob~ will return
true for any game which includes the ToB content, such as ToB itself,
BGT or BG2EE. | |||||||||||||||||||||||||||||||||
or | IDS_OF_SYMBOL ( File String ) | Will return the number associated with String in File.ids, or -1 if String is not associated in File.ids. | |||||||||||||||||||||||||||||||||
or | VARIABLE_IS_SET String | Returns true if the variable String is set (there’s a variable called either
String or %String% , regardless of whether it is a string or an
integer). | |||||||||||||||||||||||||||||||||
or | IS_AN_INT String | Returns true if the variable String is set to an integer (there’s a variable called either
String or %String% with integer value). | |||||||||||||||||||||||||||||||||
or | TRA_ENTRY_EXISTS ( String String list ) | returns true if the variable String maps to a valid TRA entry. If the String list is empty the tra entry is looked for into the loaded TRAs, otherwise it’s looked for into the listed TRA files. | |||||||||||||||||||||||||||||||||
or | IS_SILENT | Returns true if the output is currently silenced, false otherwise. | |||||||||||||||||||||||||||||||||
or | MOD_IS_INSTALLED modTp2Name modComponent | Returns true if the modComponent of modTp2Name is installed. The syntax is the same as with REQUIRE_COMPONENT. | |||||||||||||||||||||||||||||||||
or | INSTALL_ORDER modTp2Name1 modComponent1 AFTER modTp2Name2 modComponent2 | Returns true if the modComponent1 of modTp2Name1 is installed after the modComponent2 of modTp2Name2 (and both components are installed). The syntax is the same as with REQUIRE_COMPONENT. | |||||||||||||||||||||||||||||||||
or | INSTALL_ORDER modTp2Name1 modComponent1 BEFORE modTp2Name2 modComponent2 | Returns true if the modComponent1 of modTp2Name1 is installed before the modComponent2 of modTp2Name2 (and both components are installed). The syntax is the same as with REQUIRE_COMPONENT. | |||||||||||||||||||||||||||||||||
or | ID_OF_LABEL modTp2Name String | Returns the numerical identifier (component number) of the component in modTp2Name which has the given LABEL. Returns -231 if no such tp2 exists, said tp2 has no component with that LABEL, or two or more components have that LABEL. In the last two cases will print a WARNING. | |||||||||||||||||||||||||||||||||
or | STATE_WHICH_SAYS text FROM String | String must be an in-game or in-override .dlg file. Returns:
| |||||||||||||||||||||||||||||||||
or | STATE_WHICH_SAYS value IN String1 FROM String2 | As above, except that @x references are taken from
the String1 tra file. In particular, one %s in String1 is expanded to the list
of directories; for example (as of Sola v102), solarom/%s/epilogue.tra would match
solarom/american/epilogue.tra solarom/french/epilogue.tra solarom/german/epilogue.tra solarom/italian/epilogue.tra solarom/polski/epilogue.tra solarom/portuguese/epilogue.tra solarom/russian/epilogue.tra | |||||||||||||||||||||||||||||||||
or | RESOLVE_STR_REF ( text ) | Resolves the given text (adding it to the tlk), and returns its TLK index (so you can pass the result of this command to a function which then uses WRITE_LONG rather than SAY). | |||||||||||||||||||||||||||||||||
or | NEXT_STRREF | Returns the next available string reference in the TLK file. | |||||||||||||||||||||||||||||||||
or | RANDOM ( value value ) | A random-number generator. The first value is the lower bound, the second value is the upper bound. A random integer between the lower bound and the upper bound (inclusive) is returned. Thus RANDOM(3 5) can return 3, 4 or 5. If the lower bound is greater than the upper bound, zero is returned. See also RANDOM_SEED. | |||||||||||||||||||||||||||||||||
or | BUFFER_LENGTH | Returns the length of the string currently being patched, or 0 if outside of a patch expression. | |||||||||||||||||||||||||||||||||
or | INDEX ( optcase optexact string1 string2 [ value ] ) | Returns the index of the first regexp match of string1 inside string2, or -1 if not found. If provided, value specifies from what point of string2 the searching should begin (instead of 0). | |||||||||||||||||||||||||||||||||
or | RINDEX ( optcase optexact string1 string2 [ value ] ) | Returns the index of the last regexp match of string1 inside string2, or -1 if not found. If provided, value specifies from what point of string2 the searching should begin (instead of STRING_LENGTH string2). | |||||||||||||||||||||||||||||||||
or | INDEX_BUFFER ( optcase optexact string [ value ] ) | Returns the index of the first regexp match of string inside the current string, or -1 if not found. If provided, value specifies from what point of the string the searching should begin (instead of 0). | |||||||||||||||||||||||||||||||||
or | RINDEX_BUFFER ( optcase optexact string [ value ] ) | Returns the index of the last regexp match of string inside the current string, or -1 if not found. If provided, value specifies from what point of the string the searching should begin (instead of BUFFER_LENGTH). | |||||||||||||||||||||||||||||||||
or | STRING_LENGTH String | Returns the length of the argument String (after variable evaluation). | |||||||||||||||||||||||||||||||||
or | FILE_CONTAINS fileName regexp | Evaluates to 1 if the file fileName contains the regular expression regexp and 0 otherwise. Case is ignored. Use RESOURCE_CONTAINS if you want to search game resources; FILE_CONTAINS looks in the file system and does not search biff files. | |||||||||||||||||||||||||||||||||
or | FILE_CONTAINS_EVALUATED ( fileName varsRegexp ) |
First, all WeiDU variables enclosed in %s in varsRegexp and
fileName are substituted. The expression is 1 if the resulting
regexp occurs in fileName and 0 otherwise. If
fileName does not exist or has size 0, the result is 0. The
comparison ignores case. See also FILE_CONTAINS. Use
RESOURCE_CONTAINS if you want to search game
resources. FILE_CONTAINS_EVALUATED will first search the file
system and only load game resources if the file was not found in the
file system. Example usage: COPY_EXISTING_REGEXP ~.*\.CRE~ ~override~ READ_BYTE 0x273 class READ_ASCII 0x280 deathvar PATCH_IF (class = 14) AND // class 14 = CLERIC_MAGE (NOT FILE_CONTAINS_EVALUATED(~pdialog.2da~ ~%deathvar%~)) THEN BEGIN ADD_CRE_ITEM ~POTN08~ #10 #10 #10 ~IDENTIFIED~ ~INV15~ END BUT_ONLY_IF_IT_CHANGES The example gives ten healing potions to all cleric-mages who cannot join the party. Notably it excludes Aerie since she has the death variable Aerie and pdialog.2da contains AERIE. | |||||||||||||||||||||||||||||||||
or | RESOURCE_CONTAINS fileName varsRegexp | fileName and varsRegexp are evaluated for variables. fileName is loaded as a game resource (searching only the biffs and the override directories) and a regexp search is performed on the file contents. The result is 1 if the regexp varsRegexp occurs within fileName, or otherwise 0. The comparison ignores case. If fileName does not exist or has a file size 0, the result is 0. | |||||||||||||||||||||||||||||||||
is | FILE_EXISTS fileName | Evaluates to 1 if the file exists in the filesystem (or the bif file is referenced inside the chitin.key) and evaluates to 0 otherwise. The results of FILE_EXISTS are undefined if fileName is a directory. | |||||||||||||||||||||||||||||||||
or | DIRECTORY_EXISTS dirName | Evaluates to 1 if dirName is a directory and exists in the filesystem and evaluates to 0 otherwise. | |||||||||||||||||||||||||||||||||
or | FILE_EXISTS_IN_GAME fileName | Evaluates to 1 if the file exists as a game resource and has non-zero size. Evaluates to 0 otherwise. BIFFs and the override directory are searched in the standard manner. Evaluates to 0 for a file that does not exist but has been ALLOW_MISSING’d. | |||||||||||||||||||||||||||||||||
or | FILE_MD5 fileName md5sum | Evaluates to 1 if the file exists and has the given MD5 checksum. Evaluates to 0 otherwise. Two different files are exceptionally unlikely to have the same MD5 checksum. In any event, the discovered checksum is printed to the log. If the file does not exist, it evaluates to 0. | |||||||||||||||||||||||||||||||||
or | FILE_IS_IN_COMPRESSED_BIFF fileName | Evaluates to 1 if the file is stored in a compressed biff (ignoring copies in the override). Evaluates to 0 otherwise (the file is not in a biff, or it is in an uncompressed biff). | |||||||||||||||||||||||||||||||||
or | BIFF_IS_COMPRESSED fileName | Evaluates to 1 if the file
is a compressed biff. Evaluates to 0 otherwise. FileName must match against the contents of what is stored in chitin.key
(similar to how command-line arguments that touch bif files work). For
example, | |||||||||||||||||||||||||||||||||
or | FILE_SIZE fileName fileSize | Evaluates to 1 if the size of the file fileName is exactly fileSize. Evaluates to 0 otherwise. | |||||||||||||||||||||||||||||||||
or | SIZE_OF_FILE fileName | Returns the size of fileName or -1 if the file does not exist. Variables in fileName are evaluated. fileName can be a file on the local file system or an inlined file. | |||||||||||||||||||||||||||||||||
or | VALID_SCRIPT_ACTIONS variable | Variable can be an array construct and should be or evaluate to a string of whitespace-separated Infinity Engine actions. Returns true if the string compiles into BCS without unrecoverable errors, otherwise false. | |||||||||||||||||||||||||||||||||
or | VALID_SCRIPT_TRIGGERS variable | Variable can be an array construct and should be or evaluate to a string of whitespace-separated Infinity Engine triggers. Returns true of the string compiles into BCS without unrecoverable errors, otherwise false. | |||||||||||||||||||||||||||||||||
or | %variable% | The value of the variable is used. | |||||||||||||||||||||||||||||||||
or | EVALUATE_BUFFER variable | User variables inside the given string are evaluated one additional
time. You may prepend EVALUATE_BUFFER when a value is
called for and you would normally use a string. You may also use it for
SET and SPRINT statements. Example:
SPRINT x ~y~ SET y = 5 SPRINT z EVALUATE_BUFFER ~tricky %%x%%~ SET EVALUATE_BUFFER "%x%" += 77 PATCH_PRINT "y is %y% ; z is %z%"This prints out y is 82 ; z is tricky 5. You may also do hackery like FILE_SIZE "myfile" "%%indirection%%". Be very careful with this feature. | |||||||||||||||||||||||||||||||||
or | $array(index list) | The so-called array construct. See the tutorial. | |||||||||||||||||||||||||||||||||
or | %WEIDU_ARCH% | The special variable WEIDU_ARCH is set to either "x86", "amd64" or "mac" at WeiDU startup and can be used to determine the architecture of the WeiDU binary. | |||||||||||||||||||||||||||||||||
or | %WEIDU_OS% | The special variable WEIDU_OS is set to either "win32" or "osx" or "unix" at WeiDU startup and can be used to determine the operating system for which the WeiDU binary was compiled. | |||||||||||||||||||||||||||||||||
or | %COMPONENT_NUMBER% | The special variable COMPONENT_NUMBER is set to the number of the component being installed. | |||||||||||||||||||||||||||||||||
or | %INTERACTIVE% | The special variable INTERACTIVE is set to 1 if the install is being done interactively (IE you launched setup-mymod.exe and you’re installing mymod), or is set to 0 if the install is being done non-interactively (IE you launched setup-mymod.exe and now weidu is reinstalling othermod due to the domino uninstall and reinstall). | |||||||||||||||||||||||||||||||||
or | variable | The value of the variable is used.
In a patch value, you may use either %myvar% or
myvar to get the value of a variable, provided that your variable’s
name is not the same as a WeiDU syntactic keyword or known
constant. Also note that while it is common to “call out”
variables by putting quotation marks around them, this is not necessary
if the variable is unambiguous. Thus the following three patches
are all equivalent:
SET "x" = "%y%" + "%z%" // these all SET "x" = "y" + "z" // do the SET x = y + z // same thing | |||||||||||||||||||||||||||||||||
or | NAME1 | The offset within an infinity engine resource where the unidentified general name (e.g., "Battle Axe") is stored. | |||||||||||||||||||||||||||||||||
or | NAME2 | The offset within an infinity engine resource where the identified general name (e.g., "K’logarath +4") is stored. | |||||||||||||||||||||||||||||||||
or | UNIDENTIFIED_DESC | The offset within an infinity engine resource where the unidentified description (e.g., "The hand axe or throwing axe is also known as a hatchet ...") is stored. | |||||||||||||||||||||||||||||||||
or | IDENTIFIED_DESC | As above ... ("Clans have gone to war to possess K’log...") | |||||||||||||||||||||||||||||||||
or | BIO | As above ... NPC Biography | |||||||||||||||||||||||||||||||||
or | ... | Almost everything in SNDSLOT.IDS or SOUNDOFF.IDS works as well. | |||||||||||||||||||||||||||||||||
variable | A variable is a textual name that holds a value. Variables are usually set with READ_BYTE, SET or SPRINT. | ||||||||||||||||||||||||||||||||||
is | String | You may name the variable whatever you like, but stay
away from the special characters used in regexps. When you want to
obtain the value of a variable, enclose it in %s. Example: If a file contains the two binary integers 33 and 77, then after executing: READ_LONG 0 ~myvar~ WRITE_LONG 4 ( ~%myvar%~ + 11 ) The file will contain the two binary integers 33 and 44. |
Note: it’s advised to read these tutorials in the order they were written, as often a tutorial depends on contents explained in the tutorials before it. If the tutorial-writer is kind enough, they’ll state dependencies while introducing the subject.
This tutorial was thoughtfully provided by Japheth and edited by Wisp to conform to contemporary good practices.
The purpose of COPY_EXISTING and COPY_EXISTING_REGEXP is to patch a file for patching. It will grab the file out of the BIFFs, or if an override version exists, it will grab it out of the override folder.
COPY_EXISTING_REGEXP, EXTEND_BOTTOM_REGEXP and EXTEND_TOP_REGEXP can be potentially powerful actions if you need to make some changes to a certain set of files all in one shot.
Consider this example for COPY_EXISTING_REGEXP:
In my mod I want to make it so that all 1-handed swords only do D6 damage, rather than the varying damages they do in SOA. To do this with the "normal" COPY_EXISTING, I would have to write out all the one handed swords like this:
COPY_EXISTING ~sw1h01.itm~ ~override~ ~sw1h02.itm~ ~override~ etc...
That could take a lot of time to do. Using COPY_EXISTING_REGEXP I can minimize writing 70+ lines of code into 3 lines of code. Take a look at this:
COPY_EXISTING_REGEXP ~sw1h..[^abc].*itm~ ~override~ //our patching code goes here
I’ll explain how the regexp wildcards in the above example work:
I want to avoid copying over the files sw1h54a.itm, sw1h54b.itm and
sw1h54c.itm because they are the three components that make up the
Equalizer and are thus not actual swords. The regexp fragment [^abc]
is an exclusion set that will match any character but “a”, “b” and “c”.
This tutorial was thoughtfully provided by Japheth.
Let’s take this situation that I was in:
I am making a Hirelings mod, and in order to get non-party NPC’s to transition from area to area, I needed to make use of a combination of MakeGlobal(), InMyArea(O:Object*) and MoveGlobalObject(O:Object*,O:Target*).
I originally put the script that moves them from area to area into baldur.bcs (this script is constantly running in the game), but for some strange reason, the cre’s wouldn’t move to smaller areas, only the large "main" areas. (Like The Docks, Temple District, Slums, etc.) So I figured, "Huh, guess I’m going to have to put the script in every area script". (Area scripts are scripts that are assigned to areas that are ran while you are in the area. Pretty obvious, right?)
Now, writing the code to EXTEND_TOP this little script into every area script would of taken quite a while, and well, rather than do that, I humbly requested Wes to implement the regexp feature of EXTEND_TOP and EXTEND_BOTTOM, and Westley obliged. (My penance for that request is writing the docs you are reading right now.)
So now with Wes’ help, I could EXTEND_TOP to every area in 1 fell swoop. If you were to open up NI and expand the BCS tree, you would see a whole crapload of scripts that begin with the prefix AR. These are the area scripts I mentioned before. They go from AR0014 to AR6400. So now, using regexp, here is how you would extend the script to the top of every area script:
EXTEND_TOP_REGEXP ~ar[0-6].*bcs~ ~pathtoscript/patch.bcs~
So there we are, instead of 100+ lines of code, I minimized it to 1 lines. Now, if you are understanding regexp at all, you probably are going “Hey wait, why didn’t you just go EXTEND_TOP_REGEXP "ar.*bcs" "pathtoscript/patch.bcs" ?” Well, in fact, I did do that at first, but I forgot to account that there are other “normal” scripts that begin with AR. So, I had to write it so that the regexp had the number after the initial AR so that WeiDU would know only to patch the script to area files.
This tutorial was thoughtfully provided by Japheth, and shamelessly edited by Bigg to use PATCH_IF rather than IF_EVAL. It was then further edited by Wisp to conform to contemporary good practices.
READ_BYTE, READ_SHORT and READ_LONG can be potentially powerful functions when used in conjunction with PATCH_IF.
Here’s a brief demonstration.
The scenario: I want to make all longswords require 10 strength, rather than their normal 6. To do that manually would be a pain, so why not use the great feature of READ_BYTE/LONG/SHORT?
Here’s how it’s done.
First of all, we need to copy all the items so they are ready to be patched. Here’s how we do that:
COPY_EXISTING_REGEXP ~.*\.itm~ ~override~
All this does is copy all files with a .itm extension to the override folder.
Next, we read the value of the item-type offset into a variable. We’ll use the variable name "type".
READ_SHORT 0x1c type
I found the offset by looking at the file format for items in the IESDP: https://gibberlings3.github.io/iesdp/index.htm. The variable "type" can really be anything, it’s just what I’ve chosen.
We should edit the item only if the item is really a longsword:
PATCH_IF (type = 0x14) BEGIN
0x14 is just the value for longsword. If you look at the item-type offset (which is 0x1c) in the IESDP, you will see a link to a list of known values. 0x14 is the value for large/long swords.
Now that we’re done reading that into a variable and doing the checks, we can now WRITE_BYTE the value we want to give our new longswords. That is, a strength requirement of 10.
WRITE_BYTE 0x26 10
0x26 is the offset for required strength. Again, I found this offset by referring to the IESDP beforehand.
END BUT_ONLY_IF_IT_CHANGES
Finally, we close the BEGIN that was after PATCH_IF and tell WeiDU not to copy the item if it didn’t change (IE, if it wasn’t a long sword).
So, to loop through the logic again, this is what we’re saying:
If the category is longsword, set the required strength to 10. If there are no changes, ignore the file and don’t copy it over.
And that’s it. Now all items with the category Longsword will have a required strength of 10. Pretty neat, eh? Obviously you can do many, many other types of thing with these functions. So go ahead and experiment.
Here’s the full code:
COPY_EXISTING_REGEXP ~.*\.itm~ ~override~ READ_SHORT 0x1c type PATCH_IF (type = 0x14) BEGIN WRITE_BYTE 0x26 10 END BUT_ONLY_IF_IT_CHANGES
Oh yeah, and if you’re going "But I don’t know when to READ_SHORT or WRITE_LONG. It’s so confusing," don’t worry, cause it kind of is if you’ve never worked with bytes before. Here’s a very brief synopsis.
Use READ/WRITE_SHORT for anything that is two bytes. You can determine this easily by looking at the field you want to change in the IESDP or the program Near Infinity and subtracting the next listed field’s offset from the offset of the field you want to change.
For example, if I wanted to change Bodhi’s Max HP here’s what I would do. First look at her creature file in NI. (She has many, but for the sake of argument, we’ll use bodhi.cre). If you look at the Max HP field, it says it’s offset is 26h. The next field in the creature file is Animation ID, which is 28h. So, subtracting 28h from 26h gives you 2h. The perfect WRITE_SHORT size.
Use READ/WRITE_LONG for anything that is 4 bytes. Use the above example to figure this out as well.
Use READ/WRITE_BYTE for anything that is one byte. Again, use the above example to figure this out.
Use WRITE_ASCII for things like script name, dialogues, scripts, etc. Note, that if you’re WRITE_ASCIIing to say, the script name, and the previous script-name entry is longer than the one you’re writing, then you’ll have spillover after WRITE_ASCII. To avoid this, specify a required size that equals the length of the script-name field. So, if I have
WRITE_ASCII 0x280 ~Guy~
I would actually want to write this
WRITE_ASCII 0x280 ~Guy~ #32
These are by no means "hard and fast" rules, but generally they ring true.
Also, if you tried to run the template, you’d have noticed that it didn’t work
correctly. That’s because there are some corrupt files in the key (that is,
files of size 0). To avoid that, WeiDU gives us the SOURCE_SIZE
variable,
which is set to the original file size, in number of bytes. We can simply check that the
file is large enough. Using either NI or the IESDP, we see that a basic itm file must be
least 0x72 bytes long, so we can check that before doing anything:
COPY_EXISTING_REGEXP ~.*\.itm~ ~override~ PATCH_IF (SOURCE_SIZE > 0x71) BEGIN READ_SHORT 0x1c type PATCH_IF (type = 0x14) BEGIN WRITE_BYTE 0x26 10 END END BUT_ONLY_IF_IT_CHANGES
Again, we instruct WeiDU to not do a thing if the file is not large enough.
And that, as they say, is that.
This tutorial was thoughtfully provided by Japheth, and shamelessly edited by Bigg to use PATCH_IF rather than IF_EVAL. It was then further edited by Wisp to conform to contemporary good practices.
Note well: This tutorial can be considered an example of how to use WeiDU’s low-level functionality. The preferred way of accomplishing the task in this tutorial (adding items to creatures) is with ADD_CRE_ITEM. Many other things can and should today also be done with higher-level functionality.
Using expressions with WeiDU can get quite complex, however the power they give you is more than worth the effort in learning how to use them.
Say, for instance, that you want to give a creature file a shield that doesn’t have one originally. The old way to do this would be to add the item to the creature file using Near Infinity or ShadowKeeper and then copy that creature file over upon installation of your mod. However, now that INSERT/DELETE_BYTES and READ/WRITE_BYTE/LONG/SHORT take expressions, we can do this "on the fly" when installing your mod.
The benefits of doing it this way should be obvious. Now you no longer have to overwrite someone else’s modifications to that creature file. You can simply add your shield and leave their modifications intact. (Unless of course *they* also decided to give the exact same creature a shield. And if that’s the case, the shield you specify won’t be used because of our PATCH_IF statement.)
Anyways, on to the example. I’ll use acolyte1.cre as my example creature.
Caveat: This will only work on standard-ordered creature files, for which
known_spells_offset <= memorization_offset <= memorized_spells_offset <= effects_offset <= items_offset <= item_slots_offset
The first thing we want to do is copy the creature over so it’s ready for patching, and ensure it isn’t corrupt. That’s easy to do using a simple COPY_EXISTING statement:
COPY_EXISTING ~acolyte1.cre~ ~override~ PATCH_IF (SOURCE_SIZE > 0x2d3) BEGIN
Next, we need to read in the offsets so we know where to insert our bytes and where to update our number of items. I figured out these offsets by simply viewing the creature file in Near Infinity.
The offsets we need to read in are the item slots offset, the items offset and the number of items. This is how we do it:
READ_LONG 0x2bc itemsoffset READ_LONG 0x2b8 itemslot READ_LONG 0x2c0 numberofitems
"itemsoffset", "itemslot" and "numberofitems" can be anything you choose, but I would name them something that’s easy to remember because we’ll be using them later.
Next, we’re going to read in the shield slot to make sure that it’s empty. We do this by taking the "itemslot" variable and adding 0x04 to it which we know is always going to be the shield slot. (You can verify this by looking at a creature file in NI. If you look at the offset 0x2b8 it will have the offset for item slots. If you add 0x04 to that number, it should equal the offset for the shield slot.)
This is done like so:
READ_SSHORT (itemslot + 0x04) shield
We want to tell WeiDU only to do this if the shield slot is *empty*. This is where PATCH_IF comes in handy. The way a creature file is setup is that if a slot is set to -1, it means it’s empty. WeiDU can read signed values with e.g., READ_SSHORT. Signed values are ones for which a subset of values are interpreted as having a negative sign. In this example, if we were to read the value with READ_SHORT instead, all values would be interpreted as positive (so instead of -1, the value of "shield" would be 65535).
PATCH_IF (shield = "-1") BEGIN
Now, we have to update the shield slot to reflect that it’s the newest item out of all the items the creature has equipped. This is a bit misleading because in the slots part of the creature file, they count from 0. So, all we need to do is take the value of 0x2c0 (The number of items the creature has.) and use that as our value to write.
To do that, this is what we do:
WRITE_SHORT (itemslot + 0x04) numberofitems
Now we have to update the itemslot offset value to reflect the fact that we’re adding a new item to the creature. An item is *always* 0x14 bytes. We already know the offset of the itemslot offset and we’ve already read it’s value, so all we need to do is add 0x14 to it
WRITE_LONG 0x2b8 (itemslot + 0x14)
The last step before inserting our bytes is to increase the numberofitems by 1 to reflect the fact that we’ve added an item to the creature. We’ve already read in the number of items into "numberofitems" so this is dead easy:
WRITE_LONG 0x2c0 (numberofitems + 1)
Now we can actually insert out bytes. To do this we have to take the number of items that the creature has multiplied by 0x14 and add that to the itemsoffset. (The reason we can’t just insert it right at "itemsoffset" is because it would mess up the order of the items in game. If we inserted the shield at the beginning, then the creature would more than likely have some items equipped in some weird places in the game.)
This is done like so:
INSERT_BYTES (itemsoffset + numberofitems * 0x14) 0x14
Now we can actually write our information for our item. You don’t need the .itm extension at all, just everything before the .itm. And again, since the item entry is always the first entry in an item field on a creature file, we just need to use the same formula as above because we’ll be writing at the same offset that we inserted our bytes at:
WRITE_ASCII (itemsoffset + numberofitems * 0x14) ~shld01~
If you wanted to add a magical shield to the creature file and wanted it to be already identified in the slot, then you would simply have to add 0x10 bytes to the previous expression and WRITE_LONG the value of 1. Like this:
WRITE_LONG (itemsoffset + (numberofitems * 0x14) + 0x10) 1
Finally, we close the PATCH_IF statements and add BUT_ONLY_IF_CHANGES.
END END BUT_ONLY_IF_IT_CHANGES
Here’s the full code:
COPY_EXISTING ~acolyte1.cre~ ~override~ PATCH_IF (SOURCE_SIZE > 0x2d3) BEGIN READ_LONG 0x2bc itemsoffset READ_LONG 0x2b8 itemslot READ_LONG 0x2c0 numberofitems READ_SSHORT (itemslot + 0x04) shield PATCH_IF (shield = "-1") BEGIN WRITE_SHORT (itemslot + 0x04) numberofitems WRITE_LONG 0x2b8 (itemslot + 0x14) WRITE_LONG 0x2c0 (numberofitems + 1) INSERT_BYTES (itemsoffset + numberofitems * 0x14) 0x14 WRITE_ASCII (itemsoffset + numberofitems * 0x14) ~shld01~ END END BUT_ONLY_IF_IT_CHANGES
Note that you can use the PRINT action in a TP2 to debug your code as well. Like this:
COPY_EXISTING ~acolyte1.cre~ ~override~ PATCH_IF (SOURCE_SIZE > 0x2d3) BEGIN READ_LONG 0x2bc itemsoffset READ_LONG 0x2b8 itemslot READ_LONG 0x2c0 numberofitems READ_SSHORT (itemslot + 0x04) shield PATCH_IF (shield = "-1") BEGIN WRITE_SHORT (itemslot + 0x04) numberofitems WRITE_LONG 0x2b8 (itemslot + 0x14) WRITE_LONG 0x2c0 (numberofitems + 1) INSERT_BYTES (itemsoffset + numberofitems * 0x14) 0x14 WRITE_ASCII (itemsoffset + numberofitems * 0x14) ~shld01~ END END BUT_ONLY_IF_IT_CHANGES PRINT ~The value of the items offset is %itemsoffset%~
I should also mention that the above code for adding an item to a creature file is "portable" in a sense. You would only have to change a couple things. The changes that would have to be made are as follows:
And that’s the end of it. All this can be a bit tricky, but as I mentioned above, it can be quite powerful as well.
This tutorial and this feature were thoughtfully provided by Japheth.
Using ADD_STORE_ITEM is a relatively painless procedure. Consider this example: I want to add a new item to ribald.sto in BGII. There were two ways of doing this before.
Now we can do #2 rather easily thanks to ADD_STORE_ITEM.
First off copy over the store file to set it up for patching as you normally would.
COPY_EXISTING ~ribald.sto~ ~override~
Next we issue the ADD_STORE_ITEM patch expression with the following arguments:
ADD_STORE_ITEM "myitem" #10 #0 #0 ~IDENTIFIED~ #5
"myitem" is the name of the item file that we will want to appear in the store without it’s .itm extension. #10 is the first extension headers number of charges The two #0s that follow are the second and third extension headers number of charges. IDENTIFED is the flag that we want on the item.
#5 is the number of items that will be in stock.
Pretty easy stuff right?
You can also add an optional + after ADD_STORE_ITEM if you want to overwrite an item that already exists in the store.
So, if we wanted to replace HAMM05.ITM in ribald.sto this is what we’d do:
ADD_STORE_ITEM + ~hamm05~ #10 #0 #0 ~IDENTIFIED~ #5
One final note: remember to copy over your new item because ADD_STORE_ITEM doesn’t do that part for you.
So, a complete set of actions that would patch a store file and copy over the new item would look like this:
COPY_EXISTING ~ribald.sto~ ~override~ ADD_STORE_ITEM ~myitem~ #10 #0 #0 ~IDENTIFIED~ #5 COPY ~mymod/myitem.itm~ ~override~
Note that you can also add a final optional string argument if you want the store to have unlimited copies of that item:
ADD_STORE_ITEM ~myitem~ #10 #0 #0 ~IDENTIFIED~ #1 ~UNLIMITED~
Note that as of v188 you can specify the position of the item being added:
COPY_EXISTING ~ribald.sto~ ~override~ ADD_STORE_ITEM ~myitem~ AFTER ~olditem~ #10 #0 #0 ~IDENTIFIED~ #5 ADD_STORE_ITEM ~otheritm~ BEFORE ~olditem~ #10 #0 #0 ~IDENTIFIED~ #5
Valid options for position are:
FIRST will add the item at the top of the stack. This is the default, equivalent to putting nothing there.
LAST will add the item to the bottom of the stack.
BEFORE olditem will add the item exactly before olditem, or to the top of the stack if olditem is missing.
AFTER olditem will add the item exactly after olditem, or to the bottom of the stack if olditem is missing.
As of 192, if there’s a list of items in BEFORE|AFTER olditem (IE ~item1 item2 item3~
),
we add the new item BEFORE or AFTER the first item that is present in the list.
As of 211, you can specify a (zero-based) hardcoded position via AT position
.
This tutorial and this feature were thoughtfully provided by Japheth.
In BGII, the game engine was refined so that when any NPC joined your party they would be added to the baldur.gam file, regardless of the fact if they were in there already or not.
Sadly, in BG1 this is not the case. If you were to simply CreateCreature any old NPC and add them to your party, as soon as you moved to another area the game would crash and die.
This is where ADD_GAM_NPC comes in handy for all you BG1 modders out there.
By using ADD_GAM_NPC you can patch your NPC into all the baldur.gam files on an end users install. This includes the default GAM file that’s loaded up when you start a new game as well as all the GAM files in the save and mpsave directories.
Note: However, there really is no foolproof way to back up all the save games, so you may want to warn the end user in a readme to manually back them up themselves beforehand.
Anyways, I digress.
To use ADD_GAM_NPC you have to first copy over the CRE file that you will want to appear in the GAM file. I’m pretty sure we all know how to do this, but for completeness, here it is:
COPY ~mymod/mynpc.cre~ ~override~
After the COPY statement, make sure you do all your SAYs and any other type of patching that you’ll want to do for your .cre file. You will always want to use ADD_GAM_NPC last since you want the updates to your cre file to be reflected in the GAM file as well.
Here’s a typical patch statement for most NPC mods:
COPY ~mymod/mynpc.cre~ ~override~ SAY NAME1 ~Japh~ SAY NAME2 ~Japh~
So, after doing all that, we would issue ADD_GAM_NPC with the following arguments:
ADD_GAM_NPC "mynpc" "ar2600" #123 #456
"mynpc" is the name of your NPCs cre file that you’re copying over without the .cre extension.
"ar2600" is the area that you want him/her placed in. In this case we’re placing the NPC in Candlekeep.
#123 is the x co-ordinate on the map.
#456 is the y co-ordinate on the map.
That’s all there is to it. Just to recap, here’s the full set of expressions we would use:
COPY ~mymod/mynpc.cre~ ~override~ SAY NAME1 ~Japh~ SAY NAME2 ~Japh~ // Do any other patching stuff here ADD_GAM_NPC ~mynpc~ ~ar2600~ #123 #456
Note: Again, I have to stress the importance of warning the end users to backup their save and mpsave directories beforehand. There really is no elegant way to do this via WeiDU (well, maybe there is, but I can’t think of a way) so I’d just warn them.
This tutorial was thoughtfully provided by Idobek.
There have been some queries about SET_2DA_ENTRY in the WeiDU forum recently. This is a consolidation of my posts on the subject. The command takes this form:
SET_2DA_ENTRY value value value newEntry
The first value is the row, the second is the column and the third is the required column count. The entry on the given column of the given row is set to newEntry, but only rows with at least as many columns as the required column count are considered. The upper-left entry is 0,0. For example, given the following 2DA file:
2DA V1.0 * ROWNAME LOWER MIXED HELP 0 RESERVE * * * 1 BERSERKER 25179 25151 25201 2 WIZARD_SLAYER 25180 25152 25203 3 KENSAI 25181 25153 25204 4 CAVALIER 25182 25154 25206
Then the patch:
SET_2DA_ENTRY 3 1 5 ~SAMURAI~
would result in:
2DA V1.0 * ROWNAME LOWER MIXED HELP 0 RESERVE * * * 1 BERSERKER 25179 25151 25201 2 WIZARD_SLAYER 25180 25152 25203 3 SAMURAI 25181 25153 25204 4 CAVALIER 25182 25154 25206
So the columns and rows you want WeiDU to consider are:
Column0 Column1 Column2 Column3 Column4 ROWNAME LOWER MIXED HELP Row0: 0 RESERVE * * * Row1: 1 BERSERKER 25179 25151 25201 Row2: 2 WIZARD_SLAYER 25180 25152 25203 Row3: 3 KENSAI 25181 25153 25204 Row4: 4 CAVALIER 25182 25154 25206
The total number of columns is five. If, however, you put 1 as your required column count then you tell WeiDU you want to consider all rows with at least 1 column. So your row numbers change:
Row0: 2DA V1.0 Row1: * Row2: ROWNAME LOWER MIXED HELP Row3: 0 RESERVE * * * Row4: 1 BERSERKER 25179 25151 25201 Row5: 2 WIZARD_SLAYER 25180 25152 25203 Row6: 3 KENSAI 25181 25153 25204 Row7: 4 CAVALIER 25182 25154 25206
So the required column count is what you use to tell WeiDU which row you want to use as row0. So using the code
SET_2DA_ENTRY 3 1 1 ~SAMURAI~
would result in:
2DA V1.0 * ROWNAME LOWER MIXED HELP 0 SAMURAI * * * 1 BERSERKER 25179 25151 25201 2 WIZARD_SLAYER 25180 25152 25203 3 KENSAI 25181 25153 25204 4 CAVALIER 25182 25154 25206
Hopefully, that explains the required column count a little better.
Finally, if newEntry is a string that contains any %WeiDU_Variables% they are replaced by their values. That’s the end of this tutorial.
This tutorial was thoughtfully provided by Idobek, and shamelessly edited by Bigg to use PATCH_IF rather than IF_EVAL, and PATCH_IF rather than WHILE. It was then further edited by Wisp to make it look slightly more contemporary.
Note: In almost all circumstances, it is preferable to use FOR instead.
So what is a WHILE loop? Well, very simply put it is a way of applying the same patch to a file multiple times under a different condition each time. Or it is a way of applying different patches depending upon the conditions. This example will show both of these methods. Keeping in theme we are going to modify the damage done by axes. Now, some axes have both a melee and a ranged ability. We are going to want to change both of these abilities but give different damage to ranged and melee.
First, as always, we need to copy the files ready for patching, and throw in the usual sanity check:
COPY_EXISTING_REGEXP ~.*\.itm~ ~override~ PATCH_IF (SOURCE_SIZE > 0x71) BEGIN
We only want to modify axes so let’s find out what the item is:
READ_BYTE 0x1c category
Once we are done with our patching we will use PATCH_IF to tell WeiDU to only patch axes:
PATCH_IF (category = 25) BEGIN
Now, we need to find out how many abilities there are and where they are located:
READ_LONG 0x64 abilitiesoffset READ_SHORT 0x68 numberofabilities
So far, so good. Here’s where the fun begins. We want to patch examine every ability in the item. We use a WHILE loop to patch the abilities one by one. We open the loop like so:
WHILE (numberofabilities > 0) BEGIN
For the WHILE loop to progress and close we need to modify the "numberofabilities" variable. We use the SET command to do this:
SET numberofabilities = (numberofabilities - 1) END
These lines are placed at the end of the WHILE loop. WeiDU will process the abilities from last to first and reduce the "numberofabilities" variable by one each time. Once the "numberofabilities" variable hits zero the WHILE loop will stop.
OK, now we need to find out what type of ability we are looking at. Within an item an ability is 0x38 bytes, abilities also count from 0 within items so we must take this into account:
READ_BYTE (abilitiesoffset + (numberofabilities - 1) * 0x38) abilitytype
Now we get to the meat of the patch. This patch is conditional on the ability type we have just read. First we will deal with the melee damage patch. We are going to be using a PATCH_IF command to do this
PATCHING (abilitytype = 1) BEGIN
So we are patching when the ability type is 1 (melee). Time for the actual patch:
WRITE_SHORT (abilitiesoffset + (numberofabilities - 1) * 0x38 + 0x16) 6 WRITE_SHORT (abilitiesoffset + (numberofabilities - 1) * 0x38 + 0x18) 2
Time to close the PATCH_IF statement.
END
We use exactly the same method for ranged abilities:
PATCH_IF (abilitytype = 2) BEGIN WRITE_SHORT (abilitiesoffset + (numberofabilities - 1) * 0x38 + 0x16) 12 WRITE_SHORT (abilitiesoffset + (numberofabilities - 1) * 0x38 + 0x18) 1 END
We can now close the main loop (using the aforementioned method):
SET numberofabilities = (numberofabilities - 1) END
We are done with our patches so we can add our usual END and BUT_ONLY.
END END BUT_ONLY_IF_IT_CHANGES
The full code is:
COPY_EXISTING_REGEXP ~.*\.itm~ ~override~ PATCH_IF (SOURCE_SIZE > 0x71) BEGIN READ_BYTE 0x1c category PATCH_IF (category = 25) BEGIN READ_LONG 0x64 abilitiesoffset READ_SHORT 0x68 numberofabilities WHILE (numberofabilities > 0) BEGIN READ_BYTE (abilitiesoffset + (numberofabilities - 1) * 0x38) abilitytype PATCH_IF (abilitytype = 1) BEGIN WRITE_SHORT (abilitiesoffset + (numberofabilities - 1) * 0x38 + 0x16) 6 WRITE_SHORT (abilitiesoffset + (numberofabilities - 1) * 0x38 + 0x18) 2 END PATCH_IF (abilitytype = 2) BEGIN WRITE_SHORT (abilitiesoffset + (numberofabilities - 1) * 0x38 + 0x16) 12 WRITE_SHORT (abilitiesoffset + (numberofabilities - 1) * 0x38 + 0x18) 1 END SET numberofabilities = (numberofabilities - 1) END END END BUT_ONLY_IF_IT_CHANGES
So what have we done? Let’s list the steps involved:
That’s the end of this tutorial.
This tutorial was thoughtfully provided by CamDawg, and shamelessly edited by Bigg to use PATCH_IF rather than IF_EVAL. It was then further edited by Wisp to make it look slightly more contemporary.
This was a topic that I struggled with while trying to convert some kits with unusual item restrictions. Thanks to WeiDU’s new bitwise operators, this is a process that can done dynamically and non-destructively and affect all items of a particular type, even if added or altered by another mod (assuming yours is installed after others of course ). The basic idea is to construct a function in WeiDU that:
This tutorial is basically an expansion on Japheth’s excellent READ/BYTE/LONG/SHORT tutorial and I suggest you take a look at that before going further.
My initial problem came about when trying to make specific items usable or unusable by a particular class/race/kit. If you look at the item file structure at IESDP, you see that all of the unusability flags of an item are controlled by the individual bits of bytes 0x1E, 0x1F, 0x20 and 0x21 for class/race alignment restrictions and 0x29, 0x2B, 0x2D and 0x2F for the individual kit restrictions. (Near Infinity combines and displays the four bytes of class/race restrictions as a single chunk of data.)
A quick aside about notation before I proceed. Numbers in binary (the strings of bits) are preceded with 0b and hexadecimal numbers (typically the bytes) are preceded with 0x. Eight bits make a byte and bits are read right-to-left. So if the second bit is 1 and the rest 0, then the bit would be written as 0b00000010.
If you were trying to alter the usability of a specific class or race, simply using a WRITE_BYTE command on any of these particular bytes would result in changing the usability of an item by all the classes in the particular byte. The new bitwise operators provide an easier solution.
First we need to look at what the new operators BAND and BOR do with bits. They are both ways of combining two bytes, based on different rules. Both BAND and BOR compare the individual bits (bit 0 vs bit 0, bit 1 vs bit 1, etc. all the way through bit 7 vs bit 7) of two bytes. For each individual bit, the following tables are used to determine the value:
0 BAND 0 = 0 0 BAND 1 = 0 1 BAND 0 = 0 1 BAND 1 = 1 0 BOR 0 = 0 0 BOR 1 = 1 1 BOR 0 = 1 1 BOR 1 = 1
So if byte 0x23 is 0b00110101 and byte 0x24 is 0b10010001, then 0x23 BAND 0x24 is 0b00010001 whereas 0x23 BOR 0x24 is 0b10110101.
Back to relating this to unusability in items. For an item to be flagged as unusable, the corresponding bit must be set to 1. A 0 means a particular item is usable by the class/race. Let’s look at the example of changing mage robes usable by bards. This would go into your TP2 file.
COPY_EXISTING_REGEXP GLOB ~.*\.itm~ ~override~ //copies all item files PATCH_IF (SOURCE_SIZE > 0x71) BEGIN // avoid crashing on empty items READ_BYTE 0x20 mage //reads the byte containing the mage usability flag PATCH_IF ((mage BAND 0b00000100) = 0b00000000) BEGIN // if it is usable by mages READ_BYTE 0x1E bard //reads the byte containing bard usability flag READ_SHORT 0x1C type //reads the byte containing item type PATCH_IF (type = 67) OR (type = 2) BEGIN // if it is a robe or armor WRITE_BYTE 0x1E (bard BAND 0b10111111) // makes usable by bards END END END BUT_ONLY_IF_IT_CHANGES
This is the same basic idea in Japheth’s BYTE tutorial , except now we’re utilizing the bitwise operators in the writing and evaluation commands. The mage usability flag is the bit 2 in byte 0x20 (IESDP). By checking "mage" BAND 0b00000100, the values of any of the 7 other bits (0-1,3-7) are set to 0, whereas bit 2 is equal to 0 if and only if it is 0 to begin with. Therefore the statement will only be true if the item is usable by mages (bit 2 = 0). If that is true and the item is a robe or armor, then we write ("bard" BAND 0b10111111) into the bard usability byte, 0x1E. By using 1 in bits 0-5 and 7, this ensures that the original value of the flag is preserved, and using 0 in bit 6 ensures that bit 6 is set to 0 regardless of its previous value--making it usable by bards whether it was before or not.
Just as an aside, you could read the entire usability block with a READ_LONG at 0x1E. However, it becomes a pain because then you need to start writing out all 32 bits when doing the bitwise operations.
Let’s try another example. Let’s try making large weapons such as two-handed swords and halberds unusable by short folks--gnomes, halflings, and dwarves.
COPY_EXISTING_REGEXP GLOB ~.*\.itm~ ~override~ //copies all item files PATCH_IF (SOURCE_SIZE > 0x71) BEGIN // avoid crashing on empty items READ_BYTE 0x21 race //reads the byte containing race usability flags we're interested in READ_BYTE 0x31 prof //reads the byte containing item type PATCH_IF (prof = 93) OR // two-handed sword (prof = 99) BEGIN // or halberd WRITE_BYTE 0x21 (race BOR 0b00010101) // makes unusable by dwarves, halflings, and gnomes END END BUT_ONLY_IF_IT_CHANGES
In this case, rather than reading the ’type’ byte of the item, I’ve opted for the ’proficiency’ byte. Many two-handed swords in BG2 are classed as long swords for some reason so in this case proficiency is a better indicator if it is a two-handed sword IMHO.
The unusability flags for dwarves, halflings and gnomes are all in byte 0x2F at bits 0, 2, and 4 respectively (thanks again IESDP). By writing "race" BOR 0b00010101 to byte 0x2F we’re preserving the values of bits 1,3, and 5-7 while setting the 0, 2, and 4 bits to 1 (unusable) regardless of their previous values.
Thanks especially to Smoketest for helping me with this, Japheth for the tutorial that inspired this and the help given, and the IESDP team.
While the BCS and BAF script formats are common to all Infinity Engine games, there are three main variants. The variations deal largely with how “object parameters” are expressed. The style used by Baldur’s Gate, Baldur’s Gate 2, and Icewind Dale is considered to be the default. This document will not explain Infinity Engine scripting -- look for a tutorial elsewhere.
BG BAF Object | EA.GENERAL.RACE.CLASS.SPECIFIC.GENDER.ALIGN |
PST BAF Object | EA.FACTION.TEAM.GENERAL.RACE.CLASS.SPECIFIC.GENDER.ALIGN |
IWD2 BAF Object | EA.GENERAL.RACE.SUBRACE.CLASS.SPECIFIC.GENDER.ALIGNMNT |
If SUBRACE.IDS is present in CHITIN.KEY, the scripting style is autodetected as IWD2. Otherwise, if BONES.IDS is present, the scripting style is autodetected as PST. Otherwise, if CLOWNRAN.IDS is present, the scripting syle is autodetected as IWD1. Otherwise, if FLYTHR01.MVE is present, the scripting style is autodetected as BG2. Otherwise, if OH1000.ARE or OH6000.ARE are present, the scripting style is autodetected as BGEE/BG2. Otherwise the scripting style is set to BG1. The --script-style command-line argument and the SCRIPT_STYLE TP2 flag both override this default. You should not need to use --script-style explicitly if the --game is set correctly.
Furthermore, the scripting style is coupled with the format used for TLK strings. If the scripting style is BG2, strings are stored in the BG2 format.
Also, IWD1 and IWD2 have different rules for parsing certain IDS files, which contain spaces and/or commas in the tokens.
This tutorial was thoughtfully provided by CamDawg, and edited by the Bigg to account for FORCED_SUBCOMPONENT.
SUBCOMPONENTs allow to group together a set of mutually exclusive mod components into a single menu-style selection. The primary purpose of SUBCOMPONENTs is to streamline mod installation and to make life easier for end users. For example, SUBCOMPONENTs are ideal if you wish to provide multiple portrait selections for an NPC mod, have several kits available to one NPC, or for changes that conflict with one another (i.e. raising an XP cap to 10 million or raising it to 20 million). We’ll use the first one (multiple portrait options) as an example.
Without SUBCOMPONENTs, the install dialogue would look something like this:
Install Component [Delainy Portrait 1 by Bob]? [I]nstall, [N]ot Install, or [Q]uit Install Component [Delainy Portrait 2 by Fred]? [I]nstall, [N]ot Install, or [Q]uit Install Component [Delainy Portrait 3 by Wilhelmus]? [I]nstall, [N]ot Install, or [Q]uit
The end user would need to click through the options each time. Worse, conflicting components could be installed. These problems can be limited somewhat by judicious use of predicates, but taking advantage of the SUBCOMPONENT feature yields far superior results:
Install Component [Delainy Portrait]? [N]o, [Q]uit, or choose one: 1] Portrait 1 by Bob 2] Portrait 2 by Fred 3] Portrait 3 by Wilhelmus
Only one of these options can be installed at any time; re-installing and selecting a different SUBCOMPONENT will automatically uninstall the previously installed one. Setting this up is dead simple:
BEGIN ~Portrait 1 by Bob~ /* The string above is displayed in the subcomponent listing, i.e. the list with 1] 2] 3] etc. You can, of course, use TRA references instead for this and the SUBCOMPONENT string below. */ SUBCOMPONENT ~ Delainy Portrait~ /* The string above is displayed as the component listing and must be the same for each SUBCOMPONENT. The tp2 code that follows is only executed if this SUBCOMPONENT is selected. */ COPY ~Delainy/portraits/opt1G.bmp~ ~override/CDDELAIG.bmp~ COPY ~Delainy/portraits/opt1M.bmp~ ~override/CDDELAIM.bmp~ COPY ~Delainy/portraits/opt1S.bmp~ ~override/CDDELAIS.bmp~ BEGIN ~Portrait 2 by Fred~ SUBCOMPONENT ~ Delainy Portrait~ COPY ~Delainy/portraits/opt2G.bmp~ ~override/CDDELAIG.bmp~ COPY ~Delainy/portraits/opt2M.bmp~ ~override/CDDELAIM.bmp~ COPY ~Delainy/portraits/opt2S.bmp~ ~override/CDDELAIS.bmp~ BEGIN ~Portrait 3 by Wilhelmus~ SUBCOMPONENT ~ Delainy Portrait~ COPY ~Delainy/portraits/opt3G.bmp~ ~override/CDDELAIG.bmp~ COPY ~Delainy/portraits/opt3M.bmp~ ~override/CDDELAIM.bmp~ COPY ~Delainy/portraits/opt3S.bmp~ ~override/CDDELAIS.bmp~
Any REQUIRE_FILEs or other module requirements for the whole group should be put with the first subcomponent. If such a requirement fails, none of the subcomponents can be installed. In addition, each individual subcomponent can be guarded by its own predicate. If that predicate fails, that particular subcomponent cannot be installed. Example:
BEGIN ~Imoen: Turnip-Mage~ SUBCOMPONENT ~Imoen Mage Kit~ (FILE_EXISTS_IN_GAME ~turnip.spl~) // This particular subcomponent will only be displayed if TURNIP.SPL // exists. If it does not, you can still install other subcomponents.
One note about SUBCOMPONENTs and mod ordering: WeiDU will display SUBCOMPONENTs in a single grouping no matter if they fall consecutively in the TP2. However, the component number (the one that gets placed in weidu.log and the one you check for in REQUIRE_COMPONENT et al.) is still based on their TP2 order.
As of v192, if all subcomponents fail their requirement, then we don’t ask about the specific group; moreover, if at least one of the subcomponents is defined with FORCED_SUBCOMPONENT rather than SUBCOMPONENT, then the user is forced to install one of them. This could be useful if you want to ask the user if he wants biffing of wav files or not (as you need different actions for either choice).
This tutorial was thoughtfully provided by Japheth.
This command is a little bit more complex.
Here is the syntax:
COPY_EXISTING ~alynar.cre~ ~override~ ADD_CRE_ITEM ~itemname~ #charge1 #charge2 #charge3 ~Flags~ ~Inventory Slots~ [EQUIP] [TWOHANDED] [NOMOVE]
EQUIP and TWOHANDED are optional and are only needed when dealing with weapons. NOMOVE is likewise optional and controls whether ADD_CRE_ITEM should move items to make room for the new item. If no items are moved and there are no empty slots, the new item will not be added. Conversely, if an item is moved and there is no empty slot into which to move it, the moved item is lost.
I’ll give some examples now.
Example #1:
If I want to add a Ring of Protection to Sir Alynar, here’s what I’d do:
COPY_EXISTING ~alynar.cre~ ~override~ ADD_CRE_ITEM ~ring06~ #0 #0 #0 ~IDENTIFIED~ ~RRING LRING~
This will add the item to his right ring slot. If that slot is full, it will be added to the left ring slot. If both are full, we move the current right ring (the first slot in the list) to an an empty inventory slot so that the Ring of Protection is still put in it’s appropriate slot.
Example #2:
If I want to add a +2 Longsword to his second weapon slot, but don’t want to equip it, here’s what I’d do:
COPY_EXISTING ~alynar.cre~ ~override~ ADD_CRE_ITEM ~sw1h06~ #0 #0 #0 ~IDENTIFIED~ ~WEAPON2~
Again, if there happens to be a weapon in his second weapon slot already, then we simply move that weapon to an empty inventory slot.
Example #3:
Same deal as example #2 except now I want to equip the sword.
COPY_EXISTING ~alynar.cre~ ~override~ ADD_CRE_ITEM ~sw1h06~ #0 #0 #0 ~IDENTIFIED~ ~WEAPON2~ EQUIP
This will put the sword in his second weapon slot and equip it. And again, if there’s already a weapon in that slot, it will be moved to an empty inventory slot.
Example #4:
If you want to add a two-handed weapon and equip it, here’s what you do:
COPY_EXISTING ~alynar.cre~ ~override~ ADD_CRE_ITEM ~sw2h01~ #0 #0 #0 ~NONE~ ~WEAPON2~ EQUIP TWOHANDED
This will place the item in the second weapon slot (and again, if there’s already a weapon there it’s moved to inventory), remove anything that’s in the shield slot and put it in an empty inventory slot and finally it will equip the weapon.
If you just want to add a two-handed weapon to a cre but don’t need it equipped, then you don’t have to worry about specifying EQUIP or TWOHANDED.
Finally, it is worth noting that any WeiDU variables inside %s in the itemname will be replaced by their values.
Phew, that’s it.
Note: starting from v189, you can use INV, QITEM, QUIVER, WEAPON and RING as shortcuts respectively to INV1 INV2 ... INV15 INV16, QITEM1 QITEM2 QITEM3, QUIVER1 QUIVER2 QUIVER3, WEAPON1...WEAPON4, RRING LRING.
In IWD2, INV unfolds to INV1..INV24, SHIELD to SHIELD1..4.
In PST, INV unfolds to INV1..20, TATTOO to TATTOO1..3, EARRING to EARING1..2, QITEM to QITEM1..5, QUIVER1..5. PST also supports HAND, but loses SHIELD and HELMET.
This tutorial was thoughtfully provided by Rastor, and edited by Wisp to clarify the role of prompts.tra.
A customized installer can make your mod look more unique to your end user, giving a feel that “This isn’t your basic, cookie-cutter mod.” WeiDU actually allows for a great deal of customization of the mod installer program (ie. the setup-mymod.exe file). For the most part, just about every line of text can be changed to suit your needs. This tutorial will show you how.
Understanding the Installer. WeiDU, like most computer programs, draws every line of text that it displays from a series of strings. In a default installer (simply a renamed weidu.exe and the appropriate .tp2), all of the strings that WeiDU displays are programmed into WeiDU’s source code. Fortunately, weidu allows you to overwrite these strings with ones of your own choosing.
prompts.tra. In the WeiDU download package (from http://www.weidu.org), you will find a file in the examples folder called prompts.tra. This is the file that you modify to customize your installer. Go look at it now.
WeiDU defines a number of default tra references, which are displayed at different points during the installation. The tra-reference numbers and default text are reproduced in the prompts.tra file. By editing or translating this text, and loading the resulting tra strings as any other, the text displayed by WeiDU can be changed.
An Example. Let us assume for a minute that you want the question asking if users want to install to read:
You are a big stupid moron if you do not install the [Component Name]. Install? [Y]es you idiot or [N]o, bonehead or [Q]uit
For obvious reasons, you would never want your mod’s installer to actually say that but it will serve as a good working example.
To change the install component text, find the section of prompts.tra that
corresponds with the text that you wish to change. In this case, we want
to change @-1006 and @-1008. If you look at these lines, you will
see a strange
n that does not actually appear when the installer is
running. This is a line feed (or “new line”) character. It simply
indicates that you want any text following it to be inserted below the text
that precedes the line feed.
The name of the component in your mod is always inserted immediately following @-1006 without any spaces. This means that you are going to want to include a space or a bracket or some other distinguishing character at the end of your new text, otherwise the installer’s text will look quite strange. The name of the component also is always followed (with no spaces) by either line @-1007 or @-1008 depending on whether the component is already installed or not.
If you want to include more text in your custom line before the [I]nstall or
[N]ot Install stuff, then simply place it before the
n character.
To generate the custom text that I presented above, change the lines in your prompts.tra file to read:
@-1006= "You are a big stupid moron if you do not install the [" @-1008= "]. Install?\n[Y]es you idiot or [N]o, bonehead or [Q]uit"
After making these changes, place prompts.tra into the TRA folder.
If you are familiar with the default text in the installer, then you will
recognize the all of the lines that are listed in prompts.tra. You may
change any of the text, but simply remember the meaning of the line feed.
The WeiDU installer will not word wrap, meaning that if you insert
a very large line that is not broken up by
n, the user will not be able
to see all of it. You should do some experimentation to determine the
optimal length for each line (it’s roughly 72 characters).
What you cannot change. Although WeiDU does allow for a large degree of customization, there are a few things that you cannot change. You cannot change the input keys used to collect the player’s response. You cannot change the Copying one file and Compiling dialogues (and the similar) text.
Normally I would start off with do not use this feature, but since this patch can basically only be used to do really obscure things, you’re probably already a master if you’re considering it.
We’ll use INNER_ACTION in the context of a hypothetical mod that causes all monsters to drop special “skins” that can be worn by the PCs, granting the PCs all of the “natural talents” (e.g., fire resistance, seeing through invisibility, animation avatar) of that monster.
First, we will specially prepare a “template” object -- a special blank “skin” item that has slots equipped effects like “fire resistance bonus +0” and “natural armor class 10” and “change creature animation”. Each creature “skin” will be a specialized copy of this template.
Then we’ll consider each monster in turn. We’ll read the fire resistance value from the monster and write that value into a copy of the “skin” item. Then we’ll repeat the process for “armor class”, “cold resistance”, etc. We’ll also copy over any “CRE effects” the creature might have (e.g., “can see invisible”) and make them additional equipped effects of the “skin” item. Finally we’ll add that personalized “skin” item to the monster’s inventory. If the monster is C6BODHI.CRE, we’ll call the skin C6BODHI.ITM.
Note that you would normally always use a modder prefix for your new items and other files.
We’ll give the code in pieces. First, we want to go over every existing creature and make sure that CREATURE.ITM doesn’t already exist in the game. If it does we won’t make a “skin” for that monster. We’ll also include a little size sanity check to make sure that we’re not ready any degenerate 0-byte files.
COPY_EXISTING_REGEXP GLOB ~.*\.cre~ ~override~ PATCH_IF (SOURCE_SIZE > 0x2c8) AND (NOT FILE_CONTAINS_EVALUATED("%SOURCE_RES%.ITM" "ITM")) THEN BEGIN // %SOURCE_RES%.ITM does not exist.
See SOURCE_RES. Using "ITM" as the second argument makes FILE_CONTAINS_EVALUATED behave like FILE_EXISTS_EVALUATED, since any real item contains ITM as part of its header.
Now we’re going to read the animation, armor class, resistances, and creature effects. We’ll also equip this creature with its special skin, even through we haven’t made that skin yet.
READ_SHORT 0x28 anim_id READ_SHORT 0x46 natural_ac READ_ASCII 0x59 resists (11) // read 11 bytes (even 0's) READ_LONG 0x2c4 eff_off READ_LONG 0x2c8 num_eff READ_ASCII eff_off effects (num_eff * 264) ADD_CRE_ITEM ~%SOURCE_RES%~ #0 #0 #0 ~IDENTIFIED~ ~INV10~
Since there are 11 resistances (e.g., fire, cold, magic fire, ...) we are just leaving them in a string as an array of bytes rather than making up individual variables for each one. Now we want to go about creating our special skin item. We’ll use the mysterious INNER_ACTION to do so.
INNER_ACTION BEGIN COPY ~foo/footemp.itm~ ~override/%SOURCE_RES%.ITM~ WRITE_SHORT 0x76 anim_id WRITE_SHORT 0xa6 natural_ac
Using SOURCE_RES as part of a computed COPY destination is normally somewhat dangerous because it is reset every time you enter a COPY -- but we’re OK here.
Our template footemp.itm item has a number of pre-made equipped abilities (e.g., no dispel, bypass resistance, apply 100 % of the time to the wearer, last while equipped) that are just lacking concrete values. Here we fill in the animation and armor class. Next we’ll do the resistances:
// copy over all resistances FOR (i=0; i<11 ; i=i+1) BEGIN INNER_PATCH "%resists%" BEGIN READ_BYTE i resist_i END foo_resist_off = 0xd6 + (i * (0x102 - 0xd2)) WRITE_LONG foo_resist_off resist_i END
For each of the 11 resistances we remember its value from the creature by using INNER_PATCH to get it out of our resists “array”. The resistance effects in footemp.itm are stored in the same order as the creature resistances are in a CRE file.
Having handled the standard resistances we now turn to creature effects. We don’t know in advance how many creature effects there will be, so for each one we will make a new effect structure and insert it into our skin item. We’ll get the new effect structure by copying one of the ones that was already there (called dummy_eff in the code below).
Unfortunately, creature effects are not stored in quite the same way as item effects, so we can’t copy them over directly. Instead, from each creature effect we’ll extract the opcode, value and resource fields. We end up with a three step process: (1) insert some bytes into the skin file to hold the new effect, (2) copy over the dummy_eff effect structure as a framework, and (3) fill in the appropriate values from the creature effect. Here it is:
// copy all CRE effects PATCH_IF num_eff > 0 THEN BEGIN READ_ASCII 0x72 dummy_eff (0x30) FOR (i=0; i<num_eff; i=i+1) BEGIN INNER_PATCH "%effects%" BEGIN READ_ASCII ((i * 264) + 8) "cre_eff_opcode" (0x4) READ_ASCII ((i * 264) + 20) "cre_eff_value" (0x8) READ_ASCII ((i * 264) + 40) "cre_eff_res" (0x8) END INSERT_BYTES 0x72 0x30 // step 1 WRITE_EVALUATED_ASCII 0x72 "%dummy_eff%" // step 2 WRITE_EVALUATED_ASCII 0x72 "%cre_eff_opcode%" // step 3 WRITE_EVALUATED_ASCII 0x76 "%cre_eff_value%" // step 3 WRITE_EVALUATED_ASCII 0x86 "%cre_eff_res%" // step 3 END READ_SHORT 0x70 num_global_itm_eff WRITE_SHORT 0x70 (num_global_itm_eff + num_eff) END END
Huzzah. Notice our use of variables to “pass information” between the main copying action and the INNER_ACTION. Yes, I hate WeiDU syntax as well. Why do you ask? Anyway, after that we update the item header to take into account the new global effects.
END BUT_ONLY_IF_IT_CHANGES
Finally we close off our sanity-checks and throw in the ever-popular BUT_ONLY_IF_IT_CHANGES. It would be “easy” (read: annoying and time-consuming but possible) to extend this hackery so that it also stole all of the effects from undroppable items held by creature (e.g., most “undead” immunities are actually stored in RING95.ITM and not as creature effects) by nesting yet-another INNER_ACTION. You could even be selective and avoid copying over effects like “minimum hit points”. Then you could perhaps create an on-the-fly description for the “skin” item using SAY_EVALUATED.
Bonus points if you actually understood this code. Additional bonus points if you find a real use for INNER_ACTION in your mod. As a closing warning, do not try to re-invent the WeiMorph wheel using a TP2 script -- email Japheth instead.
SET_2DA_ENTRY_LATER and SET_2DA_ENTRIES_NOW are extensions of the SET_2DA_ENTRY patch, so you should be familiar with SET_2DA_ENTRY before reading this tutorial. A tutorial for SET_2DA_ENTRY exists.
The main difference between the standard SET_2DA_ENTRY and these variants is that SET_2DA_ENTRY changes the file each instance, while SET_2DA_ENTRY_LATER stores a list of changes in memory and relies on SET_2DA_ENTRIES_NOW to flush the changes and update the file. This can result in better performance than SET_2DA_ENTRY. According to Weimer’s calculations, using the deferred approach gives an edge when 5 or more SET_2DA_ENTRY are to be applied to the same file.
To illustrate the differences, consider the following code, which allows for thieves, cleric/thieves and mage/thieves to put 3 proficiency points in Two-weapon fighting using SET_2DA_ENTRY:
COPY_EXISTING ~weapprof.2da~ ~override~ SET_2DA_ENTRY 34 7 1 3 SET_2DA_ENTRY 34 39 1 3 SET_2DA_ENTRY 34 40 1 3
Compare this with the equivalent code using SET_2DA_ENTRY_LATER and SET_2DA_ENTRIES_NOW:
COPY_EXISTING ~weapprof.2da~ ~override~ SET_2DA_ENTRY_LATER ~_#_#_#weapprof~ 34 7 3 SET_2DA_ENTRY_LATER ~_#_#_#weapprof~ 34 39 3 SET_2DA_ENTRY_LATER ~_#_#_#weapprof~ 34 40 3 SET_2DA_ENTRIES_NOW ~_#_#_#weapprof~ 1
The first approach makes three self-contained changes to the 2DA table in weapprof.2da. Each SET_2DA_ENTRY loads the table, makes a change and saves the table.
The second approach stores three changes to the 2DA table in memory
with SET_2DA_ENTRY_LATER. Finally, SET_2DA_ENTRIES_NOW loads
the 2DA table, makes the stored changes and saves the table. The
string ~_#_#_#weapprof~
is used for storing the changes and
whatever form it takes, it should only be used for
SET_2DA_ENTRY_LATER and SET_2DA_ENTRIES_NOW. Avoid using
variables with names containing the same string in other contexts. It
is safe to use the same string in consecutive uses of
SET_2DA_ENTRY_LATER, but not in parallel
uses. SET_2DA_ENTRIES_NOW finalizes a series and makes the string
safe to re-use. You can use %variables% in the string. One
suggestion is to use a prefix specific to SET_2DA_ENTRY_LATER and
onto that append the name of the file being edited, like
~_#_#_#weapprof~
, where _#_#_#
is the prefix and
weapprof
is the name of the file. It is specifically not safe
to use the same string for READ_2DA_ENTRY_FORMER as you are
using for SET_2DA_ENTRY_LATER.
READ_2DA_ENTRIES_NOW and READ_2DA_ENTRY_FORMER are extensions of READ_2DA_ENTRY. You are advised to be familiar with how READ_2DA_ENTRY works before reading this tutorial.
Much like how SET_2DA_ENTRY_LATER and SET_2DA_ENTRIES_NOW offer a performance improvement over SET_2DA_ENTRY for a sufficiently large number of writes, READ_2DA_ENTRIES_NOW and READ_2DA_ENTRY_FORMER offer a performance improvement over READ_2DA_ENTRY for a sufficiently large number of reads.
To illustrate the differences, consider the following code, which reads values from a 2DA file using READ_2DA_ENTRY:
COPY_EXISTING ~kitlist.2da~ ~override~ READ_2DA_ENTRY 0 1 9 somevar // somevar will be "RESERVE"
Compare this with the equivalent code using READ_2DA_ENTRIES_NOW and READ_2DA_ENTRY_FORMER:
COPY_EXISTING ~kitlist.2da~ ~override~ READ_2DA_ENTRIES_NOW ~_#_#_#read_kitlist~ 9 READ_2DA_ENTRY_FORMER ~_#_#_#read_kitlist~ 0 1 somevar // somevar will be "RESERVE"
In the first approach, each 2DA entry is read in a self-contained manner. Each time, the 2DA table is loaded and the entry from the given row and column is extracted and stored in the variable.
The second approach loads the table once and stores it as an array of
discrete values. Retrieving a value from the table is only a matter of
getting the value of a variable. The names of the storage variables
are derived from the string, ~_#_#_#read_kitlist~
in this
case. Additionally, a variable %_#_#_#read_kitlist%
is defined,
with a value equalling the number of rows that have at least 9
columns. In other words, using READ_2DA_ENTRIES_NOW additionally
gives you the equivalent of COUNT_2DA_ROWS. It is not
necessarily safe to make use of the same string in consecutive uses of
READ_2DA_ENTRIES_NOW. If you have used the same string when
reading from another 2DA table and you attempt to retrieve a value
from a column greater than the required column count, the result is
undefined. It is specifically not safe to use the same string you use
for READ_2DA_ENTRIES_NOW as you are using for
SET_2DA_ENTRY_LATER. In this example, the string follows the
same structure as in the SET_2DA_ENTRY_LATER and
SET_2DA_ENTRIES_NOW tutorial, but with the string read_
prepended to the file name, to make sure the two strings can never be
identical.
Macros were originally intended as a way to repeat a certain tp2 block with small or no changes without resorting to copy-and-paste.
Note well: In almost all circumstances it is preferable to use functions instead of macros.
First, disclaimers: it’s very easy to shoot yourself in the foot, since
there aren’t any limits to the variable scoping of macros. Due to this reason, this tutorial
will skip on most basic stuff - if you’re going to consider this, you should
already be a master of patching.
Also, for brevity, I’ll give only examples involving tp2 actions. You can
do everything you’re about to read here by substituting DEFINE_ACTION_MACRO
and LAUNCH_ACTION_MACRO with DEFINE_PATCH_MACRO and LAUNCH_PATCH_MACRO. There
is no change in the treatment of LOCAL_SPRINT/LOCAL_SET.
For starters, you might want to define a macro that prints "Hello World!" to the screen.
// at the tp2 flag level, IE after AUTHOR but before components. DEFINE_ACTION_MACRO ~HW~ BEGIN PRINT ~Hello World!~ END /* ... */ // a component BEGIN ~Print Hello World and do nothing~ LAUNCH_ACTION_MACRO ~HW~
So, the syntax is quite simple:
DEFINE_ACTION_MACRO name-of-the-macro BEGIN local variables list action list END
will bind the action listed here with name-of-the-macro. We’ll deal with local variables later. After that, LAUNCH_ACTION_MACRO name-of-the-macro will work like if you copy-pasted there the action list defined in DEFINE_ACTION_MACRO.
Well, suppose you wanted to say ’Hello Jon!’ or ’Hello Jack!’ or any other name, rather than only ’Hello World!’. Do you have to define a macro for each of these? Of course not, since all variables defined before the LAUNCH_ACTION_MACRO instruction is called are not cleared, and so can be read (or written) during execution of the macro.
// before calling, have the string ~HW_Name~ contain the name of the person // to greet. DEFINE_ACTION_MACRO ~HW~ BEGIN PRINT ~Hello %HW_Name%!~ END /* ... */ OUTER_SPRINT ~HW_Name~ ~Jon~ LAUNCH_ACTION_MACRO ~HW~ OUTER_SPRINT ~HW_Name~ ~Jack~ LAUNCH_ACTION_MACRO ~HW~
Variables inside macros that are changed keep their new value once the macro has finished being computed. This is bad and good at the same time. For example, you might want to calculate a factorial:
// set ~factorial_index~ to the factorial you'd like to compute, // ~factorial_result~ to 1, // keep a backup copy of ~factorial_index~ // ~factorial_result~ will yield the resulting factorial. DEFINE_ACTION_MACRO ~factorial~ BEGIN ACTION_IF factorial_index != 1 THEN BEGIN OUTER_SET factorial_result = factorial_result * factorial_index OUTER_SET factorial_index = factorial_index - 1 LAUNCH_ACTION_MACRO ~factorial~ END END /* ... */ OUTER_SET factorial_index_old = 5 OUTER_SET factorial_index = 5 OUTER_SET factorial_result = 1 LAUNCH_ACTION_MACRO ~factorial~ PRINT ~%factorial_index_old%! = %factorial_result%~
The fact that variables are kept through the entire tp2 execution (formally,
that they are global) is good, since you have a way of ’remembering’ the result;
however, the bad part is that it may happen that variables are changed even if you
didn’t want them to.
Note: risks grow exponentially as you use macros by somebody else.
By the way, another (rarely) useful feature of macros used here is that they can call themselves (formally, they are recursive).
To solve the problem with variables being changed inside a macro, I provided a rudimentary form of scoping: the aforementioned local variables list part.
Note: if ’global’ and ’local’ variables mean nothing to you (for example, if you don’t have experience in programming in real life languages) you might have problems in understanding the logic beneath the rest of this tutorial. You’ll do fine with the tutorials about standard macros.
Basically, at the beginning of the macro declaration, you may use
LOCAL_SET name = value LOCAL_SPRINT variable string
to declare a variable as local, and assign it a new value. Use this for all variables that don’t need to be changed by the macro. After computing the macro, these variables are set to the value they had before the macro was processed. They work for both actions and patches. For example, here is a corrected factorial macro (also converted to patch):
// set ~tb#factorial_index~ to the factorial you'd like to compute, // ~tb#factorial_result~ to 1, // ~ftb#actorial_result~ will yield the resulting factorial. DEFINE_PATCH_MACRO ~tb#factorial~ BEGIN LOCAL_SET tb#factorial_index = factorial_index PATCH_IF tb#factorial_index != 1 THEN BEGIN SET tb#factorial_result = tb#factorial_result * tb#factorial_index SET tb#factorial_index = tb#factorial_index - 1 LAUNCH_PATCH_MACRO ~tb#factorial~ END END /* ... */ SET ~tb#factorial_index~ = 5 SET ~tb#factorial_result~ = 1 LAUNCH_PATCH_MACRO ~tb#factorial~ PATCH_PRINT ~%tb#factorial_index%! = %tb#factorial_result%~
For your convenience, WeiDU includes some standard macros for you to use. See Macros Listing for a listing of all macros and how to invoke them in detail. Here I’ll describe the standard syntax.
In our example, we’ll use the tb#factorial macro (which is the same as the last example in the preceding tutorial).
Description (from Macros Listing):
tb#factorial
: computes the factorial of a number.
This is a PATCH macro.
tb#factorial_index
to the factorial you’d like to compute.
tb#factorial_result
to 1;
tb#factorial_result
.
Read the instructions on the variables to set:
SET ~tb#factorial_index~ = 5 SET ~tb#factorial_result~ = 1 LAUNCH_PATCH_MACRO ~tb#factorial~ PATCH_PRINT ~%tb#factorial_index%! = %tb#factorial_result%~
(since you’ve read the warnings from the previous tutorial, standard macros make use of LOCAL_SET/SPRINT to avoid overwriting variables that shouldn’t change).
Another example:
tb#fix_file_size
: overwrites all files matching a certain regexp with a
certain standard file, if they are under a certain size.
This is an ACTION macro.
tb#fix_file_size_min
to the maximum allowed size
(files whose size is exactly this value are NOT overwritten).
tb#fix_file_size_target
to the standard file used to replace file(s) that match the regexp.
tb#fix_file_size_category
to a descriptive name.
tb#fix_file_size_regexp
to the regexp for the file(s) to be checked for a match.
So, to overwrite all item files that are smaller than 0x72 with sw1h01.itm (for example the corrupted iplot*.itm files)
OUTER_SET tb#fix_file_size_min = 0x72 OUTER_SPRINT tb#fix_file_size_target "SW1H01.ITM" OUTER_SPRINT tb#fix_file_size_category "items" OUTER_SPRINT tb#fix_file_size_regexp "^.*\.itm$" LAUNCH_ACTION_MACRO ~tb#fix_file_size~
The GROUP flag came about in discussing component management with an abnormally large number of independent components, e.g. BG2 Tweaks. Previously WeiDU lacked a satisfactory way to organize such a mod. The closest feature would be the top level ASK_EVERY_COMPONENT, which only allows for a single all-or-none approach.
The solution is a new component flag, GROUP, with the following syntax (and example):
Code:
GROUP string Code: BEGIN ~100% Learn Spells~ GROUP ~Convenience Tweaks~ // component code BEGIN ~Identify All Items~ GROUP ~Convenience Tweaks~ // component code BEGIN ~Give Edwin his BG2 Stats~ GROUP ~NPC Tweaks~ // component code BEGIN ~ Give Jaheira her BG2 Stats~ GROUP ~NPC Tweaks~ // component code
Upon installing the mod, the player is now presented with meta-options on each group at a high level:
Code:
Would you like to display the category [Convenience Tweaks]? [Y]es/[N]o Would you like to display the category [NPC Tweaks]? [Y]es/[N]o
Selecting [N]o
on any group suppresses those options from being
displayed, leading to a simpler and more controlled installer experience for
the player. In the provided example, selecting [N]o
to Convenience
Tweaks and [Y]es
to NPC Tweaks would result in WeiDU starting
installation by asking to install the Give Edwin his BG2 Stats component.
GROUP operates independently of SUBCOMPONENT, meaning you can use both to organize the mod as needed. A few other items of note:
BEGIN ~100% Learn Spells~ GROUP ~Convenience Tweaks~ // component code BEGIN ~Identify All Items~ GROUP ~Convenience Tweaks~ // component code BEGIN ~Give Edwin his BG2 Stats~ GROUP ~NPC Tweaks~ // component code BEGIN ~ Give Jaheira her BG2 Stats~ GROUP ~NPC Tweaks~ // component code BEGIN ~Dummy component because it's too hard to fix this in WeiDU~ DEPRECATED ~Dummy component~ GROUP ~Convenience Tweaks~ BEGIN ~Dummy component because it's too hard to fix this in WeiDU~ DEPRECATED ~Dummy component~ GROUP ~NPC Tweaks~ GAME_IS ~bg2 tob~ // will not be asked for in TutuThis isn’t needed for the above example, but it might be needed in more complex scenarios.
The array construct is a facile way of constructing multi-component variables. If you come from a programming background, the array construct has little in common with conventional arrays. It can be more accurately thought of as a multi-dimensional cross between an array and a dictionary.
The array construct can have any number of keys but only a single result.
The construct is formed as follows:
$variable(key list)
The construct can be used to store a result when used on the left-hand side of an expression or to retrieve a result when used on the right-hand side. For example, the following code snippet illustrates how the array construct could be used to dynamically construct a dictionary at install-time. A dictionary can be used to look up an associated value for any key value included in the dictionary. Here we use it to parse kitlist.2da to construct a dictionary of which CLAB-file is associated with each kit.
COPY_EXISTING kitlist.2da override READ_2DA_ENTRIES_NOW kitlist 9 FOR (i = 1; i < kitlist; ++i) BEGIN READ_2DA_ENTRY_FORMER kitlist i 1 kit READ_2DA_ENTRY_FORMER kitlist i 5 clab SPRINT $kitlist("%kit%") "%clab%" END BUT_ONLY
Arrays can either be iterated over with the action ACTION_PHP_EACH or patch PHP_EACH.
ACTION_PHP_EACH kitlist AS kit => clab BEGIN PRINT "Kit %kit% uses CLAB-file %clab%" END
You can easily make use of the dictionary properties by using the array construct on the right-hand side of the expression and inserting one of the keys found in the construct.
OUTER_SPRINT clab $kitlist(cavalier) PRINT "The Cavalier kit uses the CLAB-file %clab%"
Using an invalid key produces the same result as trying to use an undefined variable. Using multiple keys works analogously. As a completely artificial example:
OUTER_SET $var(a b c) = 1 OUTER_SET number = $var(a b c)
The array construct can also be used to retrieve results from arrays constructed in other ways, like with ACTION_DEFINE_ARRAY or ACTION_DEFINE_ASSOCIATIVE_ARRAY. For example:
ACTION_DEFINE_ARRAY array BEGIN a b c END OUTER_SPRINT letter $array(2) PRINT "The second letter is %letter%" // Will print "b"
The MODDER flag allows for more debugging info to be reported. It is suggested to leave MODDER enabled while you’re developing your mod and then disable it before you ship it to the players. However, given different coding styles, it is possible that something gets interpreted as a critical bug for some users and as expected behavior for others. For this reason, it’s possible to fine-tune the reporting level for some of the bugs that are found. This is done by writing additional modifiers after your MODDER statement, for example:
MODDER setup_tra fail area_variables none missing_extern warn
Each option can be set to either NONE, WARN or FAIL with the above syntax, with the obvious meaning. If none are chosen, WARN is assumed. WARN may generate false positives. In this case, "INSTALLED WITH WARNINGS" is not displayed.
Options set via the the command line supercede options set via the MODDER statement.
Here the list of feedback options that you can configure, along with use cases when you should deactivate them. In all other cases, using FAIL or WARN is a case of personal preference (for example, use WARN early in the development process and FAIL when you’re near the beta status).
*Global*("varname","scope")
statement is six characters long, but not GLOBAL, MYAREA, LOCALS, or a valid area name.
INT_VAR
s or STR_VAR
s
that and not included in the function’s definition.
An important note: if a tp2 sets MODDER, the debugging mode might propagate to other mods. As such, REMEMBER TO TURN MODDER OFF WHEN YOU RELEASE YOUR MOD.
Attention. You are urged to use something other than READLN if you can. You can read more at the end of this tutorial.
This tutorial has been brought to you by plainab.
The goal of this tutorial is to explain the use of READLN within the tp2 structure. READLN can be used as an action or as a patch. My experience has been in using it as an action command. I will describe the usage of READLN and then give you some examples of its use. To put it bluntly READLN stands for "read line" and it’s use causes WeiDU to pause and read any information entered by the user on the interface screen and to then store that information within the given variable.
Syntax:
ACTION_READLN ~variable~ PATCH_READLN ~variable~
Lets build an example from scratch:
First we need to have something we want to change. For this example we will use the stack amount for item files. We could pick a high number and simply use:
COPY_EXISTING_REGEXP ~.*\.itm~ ~override~ WRITE_SHORT 0x38 9999 //stack value BUT_ONLY_IF_IT_CHANGES
Or we can let the end user choose how many items they want in their stacks. To do that we first need to ask the user a question.
PRINT ~How many of one item do you want to be able to be stacked?~
If we leave it at this, weidu will display the question, but keep running. We have to make weidu pause and that is where READLN comes in. So we now have:
PRINT ~How many of one item do you want to be able to be stacked?~ ACTION_READLN ~new_stack~
We can then take this entered value and use it on our copy block.
So the usage in this example would be:
BEGIN ~Modify the stack value of all items~ PRINT ~How many of one item do you want to be able to be stacked?~ ACTION_READLN ~new_stack~ COPY_EXISTING_REGEXP ~.*\.itm~ ~override~ WRITE_SHORT 0x38 %new_stack% //stack value BUT_ONLY_IF_IT_CHANGES
You could leave it at this and it will work, but I will tell you now that the end user can input any character from their keyboard. READLN doesn’t care what is entered, but weidu may have problems if it is expecting an integer rather than text. To get around that issue we need to add a loop that will re-ask the question when the user enters the incorrect type of information.
BEGIN ~Modify the stack value of all items~ PRINT ~How many of one item do you want to be able to be stacked?~ ACTION_READLN ~new_stack~ OUTER_WHILE NOT(IS_AN_INT %new_stack%) BEGIN PRINT ~How many of one item do you want to be able to be stacked?~ ACTION_READLN ~new_stack~ END COPY_EXISTING_REGEXP ~.*\.itm~ ~override~ WRITE_SHORT 0x38 %new_stack% //stack value BUT_ONLY_IF_IT_CHANGES
The loop we added looks to see if the entered value is an integer and if the entered value is not an integer then it will re-ask the question. This loop will go indefinitely until the user inputs the correct type of information. Should your user not understand why they keep getting asked the question you may wish to add new text to your questions to help them to understand what it is you are looking for. As in this example:
BEGIN ~Modify the stack value of all items~ PRINT ~How many of one item do you want to be able to be stacked? Please enter your answer as an integer.~ ACTION_READLN ~new_stack~ OUTER_WHILE NOT(IS_AN_INT %new_stack%) BEGIN PRINT ~Your answer was not an integer. Please enter your answer as an integer. How many of one item do you want to be able to be stacked?~ ACTION_READLN ~new_stack~ END COPY_EXISTING_REGEXP ~.*\.itm~ ~override~ WRITE_SHORT 0x38 %new_stack% //stack value BUT_ONLY_IF_IT_CHANGES
That is the basics on asking for information from the user and then applying that information later on in the install process. PATCH_READLN works just like ACTION_READLN only it’s location of use within the tp2 is different.
Another use of READLN is to create options that the user can choose from.
Top level options are taken care of by the use of SUBCOMPONENT and/or GROUP, but since
WeiDU cannot nest subcomponents, you can use READLN to offer additional choices beneath
the first set of subcomponents. Here’s an example of a finished product where READLN is
used to offer additional choices to a subcomponent. The relevant parts to this discussion
were taken from iiProjectileRetrievalMod. I helped to create this portion of the mod.
It works. I have however since learned of better methods that would eliminate some of the
READLN usage from this example.
BEGIN ~Easy TUTU~ SUBCOMPONENT ~Projectile Retrieval Mod~ INCLUDE ~iiprojectiler/E22.tph~ // Easy TUTU version of mod BEGIN ~All other Infinity Engine Games~ SUBCOMPONENT ~Projectile Retrieval Mod~ PRINT ~Please tell me how you'd like to modify your files. 1. I want to modify ALL thrown/projectiles at once. 2. I want to modify each thrown/projectile one at a time. PLEASE ENTER 1 OR 2 ~ ACTION_READLN how OUTER_WHILE NOT(IS_AN_INT %how%) || (%how% > 2) || (%how% < 1) BEGIN PRINT ~Please tell me how you'd like to modify your files. 1. I want to modify ALL thrown/projectiles at once. 2. I want to modify each thrown/projectile one at a time. PLEASE ENTER 1 OR 2 ~ ACTION_READLN how END ACTION_IF (%how% = 1) THEN BEGIN PRINT ~Please choose how you'd like ALL your projectiles retrieved. 1. Plain projectile only -> Plain projectile only 2. All projectiles -> Plain projectile only 3. Each projectile -> Each projectile 4. Magical projectiles -> Reduced Magical/Plain projectiles PLEASE ENTER 1, 2, 3, OR 4 ~ ACTION_READLN choice OUTER_WHILE NOT(IS_AN_INT %choice%) OR (%choice% > 4) OR (%choice% < 1) BEGIN PRINT ~Please choose how you'd like ALL your projectiles retrieved. 1. Plain projectile only -> Plain projectile only 2. All projectiles -> Plain projectile only 3. Each projectile -> Each projectile 4. Magical projectiles -> Reduced Magical/Plain projectiles PLEASE ENTER 1, 2, 3, OR 4 ~ ACTION_READLN choice END
There is a problem with using READLN. Notably, READLN pauses the installation until the user inputs an answer. This requires the player to baby-sit the installation and attentively watch for READLNs. It also causes problems for automated installation tools, which are increasingly becoming popular. Modders are urged to be choose solutions beside READLN, such as subcomponents, or even making use of configuration files, if these are suitable.
Macros can have unintended side effects because they change the global variable environment. You have to be extremely careful when interacting with the “outside”.
That’s why functions were introduced in v209. Any changes to the variables are only visible inside the function. You can also provide default arguments and return values.
As with macros these functions come in an ACTION and a PATCH variant. So the corresponding commands are: DEFINE_ACTION_FUNCTION / DEFINE_PATCH_FUNCTION (both actions) and LAUNCH_ACTION_FUNCTION (action) / LAUNCH_PATCH_FUNCTION (patch).
Before you can use a function you have to DEFINE it:
DEFINE_PATCH_FUNCTION count_spells INT_VAR level = 1 STR_VAR type = "WI" RET num BEGIN SET num = 0 PATCH_IF (~%SOURCE_EXT%~ STRING_EQUAL_CASE ~CRE~) THEN BEGIN // do some magic here that actually counts the number of spells and sets // the variable num to that number. END END
The function is defined with default values for the variables
level
and type
. These default values override any values
the corresponding variables may have in the environment of the
function’s caller, unless other values are provided as part of calling
the function (vide infra).
After that you can LAUNCH it:
COPY_EXISTING ~my.cre~ ~override~ // count number of level 1 wizard spells (default arguments of the function) LAUNCH_PATCH_FUNCTION count_spells RET lvl1_wizard = num END // and now the level 5 priest spells LAUNCH_PATCH_FUNCTION count_spells INT_VAR level = 5 STR_VAR type = "PR" RET lvl5_priest = num END PATCH_PRINT ~This CRE has %lvl1_wizard% level 1 wizard spells and %lvl5_priest% level 5 priest spells~
The first time the function is called, the definition’s default values
for level
and type
are used. The second time, the values
of the variables are given when the function is called.
Any variables you change inside the function will revert back to their old value
after the function finishes.
That’s why you have to specify what values should be return values.
You can do this in the RET part of the function definition (RET num
).
When you launch the function you can use these variables to import the value
into your current variable environment (RET lvl1_wizard = num
).
Separate the variables with a space character if you have multiple return values.
As of V244, you can return whole arrays with the RET_ARRAY
keyword. Like with RET
, you can optionally assign the array to
a different name by using assignment when launching the function.
DEFINE_ACTION_FUNCTION foobar RET_ARRAY foo bar BEGIN ACTION_DEFINE_ARRAY foo BEGIN 1 2 3 END ACTION_DEFINE_ARRAY bar BEGIN a b c END END LAF foobar RET_ARRAY foo baz = bar END // After the function has returned, the arrays foo and baz will exist // The array baz will be derived from the array bar ACTION_PHP_EACH foo AS _ => v BEGIN PRINT "%v%" // will print 1, 2 and 3 END ACTION_PHP_EACH baz AS _ => v BEGIN PRINT "%v%" // will print a, b and c END
If you don’t need certain features simply omit them:
DEFINE_ACTION_FUNCTION xyz INT_VAR x = 1 y = 2 z = 3 BEGIN PRINT ~x: %x%, y: %y%, z: %z%~ END LAUNCH_ACTION_FUNCTION xyz END
Starting from V210, you can also specify a variable that will be local to the function environment, without being passed back to the calling environment, by adding either STR_VAR or INT_VAR to the LAUNCH bit:
DEFINE_ACTION_FUNCTION xyz INT_VAR x = 1 y = 2 z = 3 BEGIN PRINT ~x: %x%, y: %y%, z: %z%~ END LAUNCH_ACTION_FUNCTION xyz INT_VAR x = 0 END OUTER_SET y = 5 LAUNCH_ACTION_FUNCTION xyz INT_VAR y END // synonym for INT_VAR y = y
Many thanks to Wisp for providing this tutorial.
GET_OFFSET_ARRAY and GET_OFFSET_ARRAY2 are means for obtaining information about certain types of file structures without using the usual READs, FORs etc. GET_OFFSET_ARRAY can, for example, be used to find the starting offsets for all the extended headers (abilities) in an item, while GET_OFFSET_ARRAY2 can be used to find the starting offsets for all extended effects in the item. Since the effect structure is organised by the ability structure, GET_OFFSET_ARRAY2 is conventionally used together with GET_OFFSET_ARRAY.
The seven values required by GET_OFFSET_ARRAY are:
1. Offset 2. Read length of "Offset" 3. Iterations 4. Read length of "Iterations" 5. Index 6. Read length of "Index" 7. Length between iterations.
Acceptable values for read length are 0 (don’t read), 2 (SHORT) and 4 (LONG). Length between iterations depends on the file structure you are working with. In our item example, length between iterations is 0x38 while we are using GET_OFFSET_ARRAY. If we were working with a spell instead, it would be 0x28.
In the item example, "Offset" is 0x64, which gives the ability offset when read. The value read from "Iterations" is the number reads that should be performed. "Iterations" is 0x68 in the item example, which gives the number of abilities when read. The value read from "Index" is the number of iterations into the file the current read should be made. This is not always relevant for GET_OFFSET_ARRAY, but for reading general effects (on-equip effects), this is 0x6e.
GET_OFFSET_ARRAY2 requires an additional value. Let’s call it Offset2. This value is normally obtained as the result from GET_OFFSET_ARRAY. In our item example this value is the starting offset for the current ability. The value of "Offset" is now 0x6a, which gives the effect offset. The value of "Iterations" is now 0x1e, which gives the number of effects, the value of "Index" is now 0x20 and both are read from Offset2 + value (unlike GET_OFFSET_ARRAY where they are read from the start of the file).
For example, if we wanted to know how many levels Blackrazor can drain, we could use this code:
COPY_EXISTING miscbc.itm override GET_OFFSET_ARRAY ab_array 0x64 4 0x68 2 0 0 0x38 PHP_EACH ab_array AS int => ab_off BEGIN GET_OFFSET_ARRAY2 fx_array ab_off ITM_V10_HEAD_EFFECTS PHP_EACH fx_array AS int => fx_off BEGIN READ_SHORT fx_off fx_type PATCH_IF fx_type = 216 BEGIN READ_LONG fx_off + 0x4 amount PATCH_PRINT "I drain %amount% level(s)" END END END BUT_ONLY
MATCH
is the equivalent of switch..case in mainstream languages (except
it also works on strings, similar to OCaml’s match..with). This is a type of
selection control statement whose purpose is to allow the value of a variable
or expression to control the flow of program execution via a multiway branch.
The main reasons for using this type of statement include improving clarity
and reducing otherwise repetitive coding.
N.B. TRY is generally not safe to use because many errors are intended to be fatal and if the mod installation were to proceed anyway, it might do so in an inconsistent state, with resource leaks or with other errors. ACTION_RERAISE and PATCH_RERAISE mitigates or eliminates these risks, since the unsafe part is allowing the installation to continue and re-raising the error allows it to fail, like intended.
TRY
is used to detect TP2 failures (= anything that blocks the installation
and prints "NOT INSTALLED DUE TO ERRORS") and allows the modder to run custom
code (without forcibly failing the installation). The syntax is very similar
to MATCH
, which is why these two commands are presented together.
This tutorial documents ACTION_MATCH
and ACTION_TRY
, but the
patch equivalents (PATCH_MATCH
and PATCH_TRY
) are also
available.
This is a sample code for MATCH
:
ACTION_MATCH 12 + 14 // value WITH 15 16 // guards BEGIN PRINT ~15 or 16~ // guarded actions END 15 + 5 ~str.*with%vars% and regexp~ BEGIN PRINT ~15 + 5 or matches "str.*with%vars% and regexp"~ END DEFAULT PRINT ~default~ // default actions END
To begin with, the value (12 + 14) is evaluated (can be any integer or a string).
After that, it is compared against each guard: it is neither 15 nor 16, so its
guarded action is not executed. Similarly, it is neither 15 + 5 (= 20)
nor does it match the regexp ~str.*with%vars% and regexp~
after evaluating
variables and case-insensitive regexp, so its guarded actions aren’t executed either.
Since no listed value is printed, the default action(s) are executed (in this case,
PRINT ~default~
).
MATCH
can also compare against strings:
OUTER_SET var = 15 ACTION_MATCH ~string%var%~ WITH ~string12~ ~string2.*~ BEGIN PRINT ~string12 or string2.*~ END ~string1[12345]~ BEGIN PRINT ~string1[12345]~ END DEFAULT PRINT ~default~ END
In this case, PRINT ~string1[12345]~
is executed.
Guards can also have a condition associated; in that case, the value must match against one of the guards and the condition must be true:
ACTION_MATCH ~%something%~ WITH ~none~ WHEN GAME_IS ~BG1 TotSC~ BEGIN // ~none~ and the game is BG1 END ~none~ BEGIN // ~none~ and the game is not BG1 END DEFAULT // not ~none~ END
It is also possible to have a condition without a guard, in which case the syntax is:
ACTION_MATCH ~%something%~ WITH ANY GAME_IS ~BG1 TotSC~ BEGIN // the game is BG1 END ANY GAME_IS ~SoA ToB~ BEGIN // the game is BG2 END DEFAULT // not BG1 or BG2 END
Specific cases that might be worth mentioning: it is possible for a guarded action to be empty, to have no default actions, or to only have default actions, as per the following examples:
ACTION_MATCH ~%something%~ WITH ~no action~ ~none~ ~empty~ ~~ BEGIN // empty guarded action: default action never executed END ~error~ BEGIN FAIL ~error~ END DEFAULT DO_THIS_AND_THAT END ACTION_MATCH ~%something%~ WITH ~value1~ ~value2~ ~value3~ BEGIN DO_SOMETHING END ~error~ BEGIN FAIL ~error~ END DEFAULT // no default action: nothing is done for unmatched values END ACTION_MATCH ~%something%~ WITH DEFAULT // no guarded actions, only default actions: always executed DO_THIS_AND_THAT END
Also, no more than one group of actions is executed:
ACTION_MATCH ~string123~ WITH ~string123~ BEGIN DO_THIS // executed END ~string123~ BEGIN DO_THAT // not executed END DEFAULT DO_BOTH // also not executed END
TRY
is similar to MATCH
:
ACTION_TRY COPY ~override/sw1h01.itm~ ~override~ DO_SOMETHING_ELSE WITH ~Unix.Unix_error(20, "stat", "override/sw1h01.itm")~ BEGIN PRINT ~I caught sw1h01.itm not found!~ END ~Unix.Unix_error(20, "stat", "override/.*.itm")~ BEGIN PRINT ~I caught another missing item!~ END DEFAULT PRINT ~I caught another error!~ END
The actions in ACTION_TRY
are executed. If none of those fails, the
WITH
part is skipped; otherwise, the error message is printed as usual
(use SILENT), the exception text (from the ERROR: blah blah line) is
saved to the %ERROR_MESSAGE%
variable, and a MATCH
is executed
on that.
After running the correct error handling code, execution continues after the
end of the ACTION_TRY
block. If you still want to block the
installation, use ACTION_RERAISE
(in theory, you could use
FAIL ~%ERROR_MESSAGE%~
, but this changes the exception and its text,
which would cause you headaches when nesting multiple TRY
s).
This tp2 flag is used to define groups of components that the user will be offered to install in a single step. Sample code:
QUICK_MENU ALWAYS_ASK 5 END ~Red~ BEGIN 1 2 END ~Green~ BEGIN 2 3 END ~Blue~ BEGIN 4 6 END END
The user will be presented with the following prompt:
The test/test.tp2 mod has 13 distinct optional components. To save time, you can choose what to do with them at a high level rather than being asked about each one. [A]sk about each component, [S]kip all, or choose a pre-defined selection: 1] Red 2] Green 3] Blue
If some mod components were already installed, the option to [U]ninstall or [R]einstall the currently installed components will also be offered.
If the user chooses to install a pre-defined selection, then its components will be installed, and ALL other components of the mod will be uninstalled if already installed.
Components in ALWAYS_ASK will always be asked for individually if the user chose to install one of the pre-defined selections (or, of course, if he chose [A]). If the user chose [S], [R] or [U] the same action will be applied to the components in ALWAYS_ASK.
Caveats:
This also means that your mod’s components will always be grouped together (you can’t install a component from your mod, then a second mod, and then other components of your mod).
HANDLE_AUDIO and HANDLE_TILESETS are convenience functions for the common tasks of installing mod-supplied audio and tilesets. All IE games are supported and the specifics for each game is handled automatically. The functions are designed to take care of as many as the platform-specific details as possible, including the differences between Windows, OS X and GNU/Linux.
There is one exception to this, however. At present, to use
HANDLE_AUDIO
you need to include oggdec.exe for Windows and SoX
for OS X in your mod. To use
HANDLE_TILESETS
you need to include tisunpack for Windows and
OS X. In both cases it is recommended
that GNU/Linux users install oggdec and tisunpack themselves. Most
modern GNU/Linux distributions come with oggdec already installed. A
copy of tisunpack compiled for GNU/Linux is distributed together with
the Linux version of WeiDU. If you want to facilitate the installation
of your mod on GNU/Linux, you should state that your mod depends on
oggdec or tisunpack, as appropriate.
HANDLE_AUDIO expects your audio to be in the Ogg Vorbis format.
If you have WAV or WAVC files, you do not need HANDLE_AUDIO
,
you can simply copy them to the override.
With no configuration, HANDLE_AUDIO
expects your audio,
oggdec.exe and SoX to be located in mymod/audio
. Using
HANDLE_AUDIO
is then as simple as a single line of TP2:
LAF HANDLE_AUDIO END
LAF
is an alias for LAUNCH_ACTION_FUNCTION.
HANDLE_AUDIO
is a completely independent function, and does not
have no be placed before or after anything else in your TP2 file. It
is equivalent to a TP2 action and you obviously need to place it in
the right component, but that is it.
HANDLE_AUDIO
can also be configured to use audio in a different
directory, through the string variable audio_path
. The value of
this variable is the directory in which HANDLE_AUDIO
should
look for audio, oggdec.exe and SoX. So, for instance, if you keep your
sounds in mymod/sound
you would configure HANDLE_AUDIO
like so:
LAF HANDLE_AUDIO STR_VAR audio_path = ~mymod/sound~ END
If you want HANDLE_AUDIO
to look for oggdec.exe or SoX in
another directory than audio_path
, you can use the
oggdec_path
and sox_path
variables. For example, if you
have your voice-overs in mymod/audio/vo
, your music in
mymod/audio/music
, and oggdec.exe and SoX in mymod/audio
you could install it like so:
LAF HANDLE_AUDIO STR_VAR audio_path = ~mymod/audio/vo~ oggdec_path = ~mymod/audio~ sox_path = ~mymod/audio~ END LAF HANDLE_AUDIO INT_VAR music = 1 STR_VAR audio_path = ~mymod/audio/music~ oggdec_path = ~mymod/audio~ sox_path = ~mymod/audio~ END
The integer variable music
simply tells HANDLE_AUDIO
that you want the audio handled as music. This only makes a difference
on BGEE.
The function HANDLE_TILESETS follow the same design as
HANDLE_AUDIO
. HANDLE_TILESETS
decompresses and installs
TIZ files rather than OGG files and uses tisunpack rather than
oggdec/SoX.
With no configuration, HANDLE_TILESETS
expects your TIZ files
to be located in mymod/tiz
. Additionally, it expects tisunpack
for Windows to be located in mymod/tiz/win32
and tisunpack for
OS X to be located in
mymod/tiz/osx
. You install you tilesets like so:
LAF HANDLE_TILESETS END
The string variables tiz_path
and tisunpack_path
allow
you to configure HANDLE_TILESETS
to look for TIZ files or
tisunpack in other directories. Note that you do not specify the
individual directories for tisunpack for Windows or tisunpack for OS
X. Instead you specify the parent directory and WeiDU will then use
the value of the variable WEIDU_OS to find the right version
of tisunpack. For example, if you keep your TIZ files in
mymod/tilesets
and tisunpack for Windows in
mymod/bin/win32
and tisunpack for
OS X in mymod/bin/osx
you
could install your tilesets like so:
LAF HANDLE_TILESETS STR_VAR tiz_path = ~mymod/tilesets~ tisunpack_path = ~mymod/bin~ END
To conclude this tutorial, here is a fictional (but valid) example of
a TP2 file of a mod where HANDLE_AUDIO
and
HANDLE_TILESETS
are used:
BACKUP ~fl#xmpl/backup~ AUTHOR ~Wisp~ BEGIN ~E. G. McExample NPC~ COPY ~fl#xmpl/copy~ override COMPILE ~fl#xmpl/compile~ LAF HANDLE_AUDIO END LAF HANDLE_TILESETS END COPY ~fl#xmpl/character/fl#xmpl.cre~ override SAY NAME1 ~E. G. McExample~ SAY NAME2 ~E. G. McExample~ // Other SAYs // End of File
This section contains code snippets that serve to document functionality not covered elsewhere.
These snippets are provided by Miloch.
//For each dialogue, change GivePartyGold() to GiveGoldForce() actions ACTION_FOR_EACH dialog IN ~arkion~ ~ardrou~ ~berrun~ ~brevli~ ~brielb~ ~forthe~ ~gurke~ BEGIN ACTION_IF FILE_EXISTS_IN_GAME ~%dialog%.dlg~ BEGIN COPY_EXISTING ~%dialog%.dlg~ ~override~ PATCH_IF SOURCE_SIZE > 0x34 BEGIN DECOMPILE_DLG_TO_D REPLACE_TEXTUALLY CASE_INSENSITIVE EXACT_MATCH ~GivePartyGold~ ~GiveGoldForce~ COMPILE_D_TO_DLG END BUT_ONLY END END //Compress a BAM COPY ~bambatch/bam~ ~bambatch/bam~ READ_ASCII 0x0 sg (4) //Signature PATCH_IF (~%sg%~ STRING_EQUAL_CASE ~BAM ~ = 1) BEGIN dl = SOURCE_SIZE //Data length COMPRESS_REPLACE_FILE 0 dl 9 INSERT_BYTES 0x0 0xc WRITE_ASCII 0x0 ~BAMCV1 ~ WRITE_LONG 0x8 dl END ELSE BEGIN PATCH_PRINT ~%SOURCE_FILE% is not an uncompressed BAM.~ END BUT_ONLY //Decompress a BAM COPY ~bambatch/bam~ ~bambatch/bam~ READ_ASCII 0x0 sg (4) //Signature PATCH_IF (~%sg%~ STRING_EQUAL_CASE ~BAMC~ = 1) BEGIN READ_LONG 0x8 dl //Uncompressed data length DECOMPRESS_REPLACE_FILE 0xc (SOURCE_SIZE - 0xc) dl END ELSE BEGIN PATCH_PRINT ~%SOURCE_FILE% is not a compressed BAM.~ END BUT_ONLY //Defines an array associating creatures with a new amount of gold ACTION_DEFINE_ASSOCIATIVE_ARRAY newgold BEGIN ~arkion~ => 100 ~ardrou~ => 10 ~berrun~ => 58 ~brevli~ => 5 ~brielb~ => 200 ~gurke~ => 20 ~oublek~ => 1000 ~perdue%~ => 75 ~pheirk%~ => 38 ~scar~ => 1026 ~sonner~ => 150 ~wenric~ => 170 ~jarlaxle~ => 25 ~ppsime~ => 50 ~yschearl~ => 150 END //Writes the new amount of gold for each creature above if it exists ACTION_PHP_EACH newgold AS crename => gold BEGIN ACTION_IF FILE_EXISTS_IN_GAME ~%crename%.cre~ BEGIN COPY_EXISTING ~%crename%.cre~ ~override~ PATCH_IF SOURCE_SIZE > 0x2d3 BEGIN WRITE_LONG 0x1c gold END BUT_ONLY END END
These macros are automatically shipped with WeiDU.
tb#factorial
: computes the factorial of a number.
This is a PATCH macro.
tb#factorial_index
to the factorial you’d like to compute.
tb#factorial_result
to 1;
tb#factorial_result
.
tb#fix_file_size
: overwrites all files matching a certain regexp with a
certain standard file, if they are under a certain size.
This is an ACTION macro.
tb#fix_file_size_min
to the maximum allowed size
(files whose size is exactly this value are NOT overwritten).
tb#fix_file_size_target
to the standard file used to replace file(s) that match the regexp.
tb#fix_file_size_category
to a descriptive name.
tb#fix_file_size_regexp
to the regexp for the file(s) to be checked for a match.
Note that, differently from the standalone versions, for these macros you need
to set all the needed values - they aren’t initialized automatically. This is untrue functions
(they are initialized to either 0 or the empty string, except for probability1
which is initialized to 100).
DELETE_SPELL_EFFECT
: deletes all extended effects with specified opcode from a spell.
This is a PATCH macro and function.
opcode_to_delete
to the opcode of the effect you want to delete. Opcode of (-1) will match all effects.
DELETE_ITEM_EFFECT
: deletes all extended effects with specified opcode from an item.
This is a PATCH macro and function.
opcode_to_delete
to the opcode of the effect you want to delete. Opcode of (-1) will match all effects.
DELETE_ITEM_EQEFFECT
: deletes all equipping effects with specified opcode from an item.
This is a PATCH macro and function.
opcode_to_delete
to the opcode of the effect you want to delete. Opcode of (-1) will match all effects.
DELETE_CRE_EFFECT
: deletes all effects with specified opcode from a creature. (Warning: doesn’t check EFF version)
This is a PATCH macro and function.
opcode_to_delete
to the opcode of the effect you want to delete. Opcode of (-1) will match all effects.
ITEM_EFFECT_TO_SPELL
: copies all extended effects from the current item to the first extended header of a spell.
This is a PATCH macro and function.
type
to the header type to copy the effect from (use 99 to specify ’all types’).
header
to number of extended header the effect should be copied from (use 99 to specify ’every header’).
insert_point
to the index into the spell file at which the effects should be inserted. A value of 0 will have the effects inserted as the first effects of the spell. Effects are inserted as the last effects by default.
new_itm_spl
to a spell you want to copy effects to.
ADD_SPELL_EFFECT
: adds an extended effect to a spell. All variables except probability1
and insert_point
are 0 by default.
This is a PATCH macro and function.
opcode
to opcode
target
to target type
timing
to timing type
parameter1
to first parameter
parameter2
to second parameter
power
to power
resist_dispel
to magic resistance/dispel type
duration
to duration
probability1
to probability 1 (default 100)
probability2
to probability 2
resource
to resource (8 chars max)
dicenumber
to number of dices to be thrown
dicesize
to size of dices to be thrown
savingthrow
to type of savingthrow to be allowed against the effect
savebonus
to saving throw bonus
special
to the special parameter (only valid for functions; not macros)
header
to number of extended header (starting from 1) the effect should be added to (by default the effect is added to every header).
insert_point
to the position of the effect. If this variable is 0 the effect will be inserted as the first effect of the extended header. If this variable is negative or greater than the number of effects, the effect will be inserted as the last effect. The effect is added as the last effect by default.
ADD_ITEM_EFFECT
: adds an extended effect to an item. All variables except probability1
, type
and insert_point
are 0 by default.
This is a PATCH macro and function.
opcode
to opcode
target
to target type
timing
to timing type
parameter1
to first parameter
parameter2
to second parameter
power
to power
resist_dispel
to magic resistance/dispel type
duration
to duration
probability1
to probability 1 (default 100)
probability2
to probability 2
resource
to resource (8 chars max)
dicenumber
to number of dices to be thrown
dicesize
to size of dices to be thrown
savingthrow
to type of savingthrow to be allowed against the effect
savebonus
to saving throw bonus
special
to the special parameter (only valid for functions; not macros)
header
to number of extended header (starting from 1) the effect should be added to (by default the effect is added to every header).
type
to the type of header to which the effect should be added or 99 for all types. Defaults to 3 (magic).
insert_point
to the position of the effect. If this variable is 0 the effect will be inserted as the first effect of the extended header. If this variable is negative or greater than the number of effects, the effect will be inserted as the last effect. The effect is added as the last effect by default.
ADD_ITEM_EQEFFECT
: adds an equipping effect to an item. All variables except probability1
are 0 by default.
This is a PATCH macro and function.
opcode
to opcode
target
to target type
timing
to timing type
parameter1
to first parameter
parameter2
to second parameter
power
to power
resist_dispel
to magic resistance/dispel type
duration
to duration
probability1
to probability 1 (default 100)
probability2
to probability 2
resource
to resource (8 chars max)
dicenumber
to number of dices to be thrown
dicesize
to size of dices to be thrown
savingthrow
to type of savingthrow to be allowed against the effect
savebonus
to saving throw bonus
special
to the special parameter (only valid for functions; not macros)
insert_point
to the position of the effect. If this variable is 0 the effect will be inserted as the first effect of the extended header. If this variable is negative or greater than the number of effects, the effect will be inserted as the last effect. The effect is added as the first effect by default.
ADD_SPELL_CFEFFECT
: adds a casting-feature effect to a spell. All variables except probability1
are 0 by default.
This is a PATCH macro and function.
opcode
to opcode
target
to target type
timing
to timing type
parameter1
to first parameter
parameter2
to second parameter
power
to power
resist_dispel
to magic resistance/dispel type
duration
to duration
probability1
to probability 1 (default 100)
probability2
to probability 2
resource
to resource (8 chars max)
dicenumber
to number of dices to be thrown
dicesize
to size of dices to be thrown
savingthrow
to type of savingthrow to be allowed against the effect
savebonus
to saving throw bonus
special
to the special parameter (only valid for functions; not macros)
insert_point
to the position of the effect. If this variable is 0 the effect will be inserted as the first effect of the extended header. If this variable is negative or greater than the number of effects, the effect will be inserted as the last effect. The effect is added as the first effect by default.
ADD_CRE_EFFECT
: adds an effect to a creature. All variables except probability1 are 0 by default.
This is a PATCH macro and function.
opcode
to opcode
timing
to timing type
target
to target type
parameter1
to first parameter
parameter2
to second parameter
parameter3
to third parameter
parameter4
to forth parameter
power
to power
resist_dispel
to magic resistance/dispel type
duration
to duration
probability1
to probability 1 (default 100)
probability2
to probability 2
resource
to resource (8 chars max)
resource2
to second resource (8 chars max)
vvcresource
to VVC resource (8 chars max)
effsource
to effect source
effvar
to effect variable
dicenumber
to number of dices to be thrown
dicesize
to size of dices to be thrown
savingthrow
to type of savingthrow to be allowed against the effect
savebonus
to saving throw bonus
school
to magical school
special
to the special parameter (only valid for functions; not macros)
lowestafflvl
to lowest affected level
highestafflvl
to highest affected level
casterx
to caster X position
castery
to caster Y position
targetx
to target X position
targety
to target Y position
restype
to source resource type
sourceslot
to source resource slot
casterlvl
to caster level
sectype
to secondary type
insert_point
to the index at which the new effect
is to be inserted. 0 is first and negative values or values
equal to or exceeding the existing number of effects are last.
DELETE_CRE_ITEM
: deletes all matching items from a creature. Regexp allowed.
This is a PATCH macro and function.
item_to_delete
to the item you want to delete.
DELETE_STORE_ITEM
: deletes all matching items from a store. Regexp allowed.
This is a PATCH macro and function.
item_to_delete
to the item you want to delete.
DELETE_AREA_ITEM
: deletes all matching items from an area. Regexp allowed.
This is a PATCH macro and function.
item_to_delete
to the item you want to delete.
REPLACE_STORE_ITEM
: replaces all matching items in a store with another item. Regexp allowed.
This is a PATCH macro and function.
old_item
to the item you want to be replaced
new_item
to the new item
number_in_stock
to number of new items in stock (default 0)
flags
to flags new item should have (usual weidu syntax: STOLEN, IDENTIFIEDSTOLEN, etc). Default - no flags.
charges1
to number of charges of first magical ability (default 0)
charges2
to number of charges of second magical ability (default 0)
charges3
to number of charges of third magical ability (default 0)
REPLACE_AREA_ITEM
: replaces all matching items in an area with another item. Regexp allowed.
This is a PATCH macro and function.
old_item
to the item you want to be replaced
new_item
to the new item
flags
to flags the new item should have (usual weidu syntax: STOLEN, IDENTIFIEDSTOLEN, etc). Default - no flags.
charges1
to amount in stock/number of charges of first magical ability (default 0)
charges2
to number of charges of second magical ability (default 0)
charges3
to number of charges of third magical ability (default 0)
ADD_AREA_ITEM
: adds an item to a container of an area.
This is a PATCH macro and function.
item_to_add
to the item you want to add
container_to_add_to
to the number of the container the item should be added to. The count starts at 1.
flags
to flags the item should have (usual weidu syntax: STOLEN, IDENTIFIEDSTOLEN, etc). Default - no flags.
charges1
to amount in stock/number of charges of first magical ability (default 0)
charges2
to number of charges of second magical ability (default 0)
charges3
to number of charges of third magical ability (default 0)
ADD_CRE_ITEM_FLAGS
: adds flags to all matching items possessed by a creature. Regexp allowed.
This is a PATCH macro and function.
item_to_change
to the item the flags should be added to
flags
to flags you need to add (usual weidu syntax: STOLEN, IDENTIFIEDSTOLEN, etc).
REMOVE_CRE_ITEM_FLAGS
: removes flags from all matching items possessed by a creature. Regexp allowed.
This is a PATCH macro and function.
item_to_change
to the item the flags should be removed from
flags
to flags you need to remove (usual weidu syntax: STOLEN, IDENTIFIEDSTOLEN, etc).
SET_CRE_ITEM_FLAGS
: set flags to all matching items possessed by a creature. Regexp allowed.
This is a PATCH macro and function.
item_to_change
to the item which flags should be set
flags
to flags you need to set (usual weidu syntax: STOLEN, IDENTIFIEDSTOLEN, etc).
READ_SOUNDSET
: reads soundset of a creature and stores it as an array of string references This is an ACTION macro.
npc
to the target creature (with .cre extension)
soundset
to the name of soundset you want.
WRITE_SOUNDSET
: writes soundset to all matching creatures. Regexp allowed.
This is an ACTION macro.
npc
to a creature in question (with .cre extension)
soundset
to name of soundset you have.
overwrite
to 2 if you want new soundset to overwrite the old one completely. Set it to 1 if you want to overwrite old string references only with non-empty new references (may keep some old). Set it to 0 it you want to overwrite only empty old references with new ones.
FJ_CRE_VALIDITY
: Checks whether a CRE file is well-formed or not, fixes some common bugs, and reports whether the CRE file is still broken or not. In particular:
This is a PATCH function.
do_message
to 1 if you want explicit an explicit message regarding the cre invalidity. Defaults to 0.
do_reindex
to 0 if you don’t want the creature file to be reindexed. Defaults to 1.
do_eff
to 0 if you don’t want EFFv1 <-> EFFv2 conversion to be applied. Defaults to 1.
valid
returns 1 if the CRE is well-formed, 0 otherwise.
FJ_CRE_REINDEX
: reorders creatures with nonstandard offset orders. Called by FJ_CRE_VALIDITY automatically if relevant.
This is a PATCH function.
do_reindex
to 0 if you don’t want the creature file to be reindexed. Defaults to 1.
do_eff
to 0 if you don’t want EFFv1 <-> EFFv2 conversion to be applied. Defaults to 1.
FJ_CRE_EFF_V2
: Converts creatures using version 1 effects to version 2. Called by FJ_CRE_VALIDITY or FJ_CRE_REINDEX automatically if relevant.
This is a PATCH function.
T-CRE_EFF_V1
: Converts creatures using version 2 effects to version 1. Called by FJ_CRE_VALIDITY or FJ_CRE_REINDEX automatically if relevant.
This is a PATCH function.
ADD_AREA_REGION_TRIGGER
: adds an area region to the current are file.
This is a PATCH macro and function. Unlike other macros you do not need to set every variable. You are only required to set those values that you need to write.
ab_RT_Name
-- Name to assign to new region
ab_RT_Type
-- Type of trigger -- 0=proximity;1=info;2=travel
ab_RT_BbLX
-- Bounding Box - low x value - LEFT
ab_RT_BbLY
-- Bounding Box - low y value - TOP
ab_RT_BbHX
-- Bounding Box - High x value - RIGHT
ab_RT_BbHY
-- Bounding Box - High y value - BOTTOM
ab_RT_VxPr
-- Number of Vertex Pairs for current region
ab_RT_CuId
-- Cursor Index - points to a graphic in cursors.bam
ab_RT_Dest
-- Destination Area
ab_RT_EntN
-- Entrance Name
ab_RT_Fbit
-- Flag bits set in bit format which are read right to left - 0=off; 1=on
ab_RT_Itxt
-- Info text
ab_RT_TDtD
-- Trap Detection Difficulty
ab_RT_TRmD
-- Trap Removal Difficulty
ab_RT_TSet
-- Trap is set - 0=no; 1=yes
ab_RT_TDet
-- Trap is detected - 0=no; 1=yes
ab_RT_LPoX
-- Launch Point X
ab_RT_LPoY
-- Launch Point Y
ab_RT_KeyI
-- Key Item
ab_RT_Rbcs
-- Region Script file
ab_RT_ALPX
-- Alternate Launch Point X
ab_RT_ALPY
-- Alternate Launch Point Y
ab_RT_Dial
-- Dialog file (used only in PST)
The vertex points are separated by their XY values
You may have as many pairs as you need. Just copy this set and increment the #.
Example: For the X values: ab_RT_Vx_X_0
, ab_RT_Vx_X_1
, ab_RT_Vx_X_2
For the Y values: ab_RT_Vx_Y_0
, ab_RT_Vx_Y_1
, ab_RT_Vx_Y_2
ab_RT_Vx_X_0
-- Point #0 X value
ab_RT_Vx_Y_0
-- Point #0 Y value
NOTE: The original code released in WeiDU v211 has been overhauled and re-released in v212. Therefore, the user defined variable names have been changed. If you used the 211 version you will need to update your mod to use the variables in this version. Sorry for the inconvenience...
RES_NUM_OF_SPELL_NAME
: converts a spell.ids reference to resource name.
This is an ACTION and PATCH macro and function.
spell_name
to the IDS symbolic reference of a spell.
spell_num
to the IDS numerical reference of that spell.
spell_res to
the resource name for the referenced spell.
RES_NAME_OF_SPELL_NUM
: converts a spell.ids reference to resource name.
This is an ACTION and PATCH macro and function.
spell_num
to the IDS numerical reference of that spell.
spell_name
to the IDS symbolic reference of a spell.
spell_res
to the resource name for the referenced spell.
NAME_NUM_OF_SPELL_RES
: converts a spell resource name to a spell.ids reference.
This is an ACTION and PATCH macro and function.
spell_res
to the resource name for the referenced spell.
spell_num
to the IDS numerical reference of that spell.
spell_name
to the IDS symbolic reference of a spell.
GET_UNIQUE_FILE_NAME
: Creates a file name that is currently unallocated.
This is a PATCH and ACTION function.
This function will create a filename using __ as a prefix. __ is reserved by
The Bigg and writeable by anybody, as long as you dynamically create the file
name (either by using this function or other code of your devising). The algorithm
creates file names in base 36, going from __0000.ext to __zzzz.ext; this allows for
36 ^ 4 = 1679616 unique file names.
To allow users to reinstall your mod without breaking the identity of these files, you
can use the ’base’ parameter to ensure that the same thing will be mapped to the same
file on reinstalls. For instance, if you’re iterating through Mages to give each of
them a spellbook, base should contain your modder prefix, an identifier for the purpose
of the call, and the file name you’re working on, for instance:
~tb#spellbooks_%SOURCE_RES%~
extension
to the desired extension (required).
base
to a string unique to your mod and current file (optional).
filename
the currently unallocated file name (sans the extension).
ALTER_AREA_ENTRANCE
: patch coordinates and orientation of area party entrance points.
This is a PATCH function. For all integer variables, a negative value results in no change to that field and the default value is -1. The variable entrance_name
is required.
x_coord
to the new X coordinate.
y_coord
to the new Y coordinate.
orient
to the new orientation.
entrance_name
to the name of the entrance point to be altered. This variable is required.
ALTER_AREA_REGION
: patch area regions, also known as area triggers.
This is a PATCH function. For all integer variables except info_point
, a negative value results in no change and the default value is -1. The variable info_point
results in no change if it has the default value 99999999. In the case of flags, a value of 1 will set the flag and a value of 0 will unset it. For all string variables except region_name
, the default value is the string "same", which results in no change to that field. The variable region_name
is required.
type
to the new type of the region.
cursor
to the new cursor index to be used.
trap_detect
to the new difficulty of trap detection.
trap_remove
to the new difficulty of trap removal.
trapped
to whether the region should be trapped.
detected
to whether the trap should be detected.
flag_locked
to the new value of the flag known as locked or invisible trap (bit 0).
flag_resets
to the new value of the flag known as trap resets (bit 1).
flag_party_required
to the new value of the flag known as party is required (bit 2).
flag_trap_detectable
to the new value of the flag known as trap is detectable (bit 3).
flag_trap_enemies
to the new value of the flag known as trap can be set off by enemies (bit 4).
flag_tutorial
to the new value of the flag known as tutorial trigger (bit 5).
flag_trap_npcs
to the new value of the flag known as trap can be set off by NPCs (bit 6).
flag_silent
to the new value of the flag known as silent trigger (bit 7).
flag_deactivated
to the new value of the flag known as deactivated (bit 8).
flag_impassable_npc
to the new value of the flag known as cannot be passed by NPCs (bit 9).
flag_activation_point
to the new value of the flag known as use activation point (bit 10).
flag_connect_to_door
to the new value of the flag known as connected to door (bit 11).
bounding_left
to the new value of the left edge of the bounding box. The vertices remain unchanged.
bounding_top
to the new value of the top edge of the bounding box. The vertices remain unchanged.
bounding_right
to the new value of the right edge of the bounding box. The vertices remain unchanged.
bounding_bottom
to the new value of the bottom edge of the bounding box. The vertices remain unchanged.
info_point
to the new string reference of the info point.
launch_x
to the new x coordinate of the launch point.
launch_y
to the new y coordinate of the launch point.
activate_x
to the new x coordinate of the activation point.
activate_y
to the new y coordinate of the activation point.
region_name
to the name of the region to be patched. This variable is required.
destination_area
to the resref of the new destination area.
entrance_name
to the new entrance name of the destination area.
region_key
to the resref of the key item.
region_script
to the resref of the region script.
ALTER_AREA_ACTOR
: patch area actors.
This is a PATCH function. All integer variables, except expiry
, default to the value -1 and a negative value results in no change to the corresponding field. The variable expiry
instead has a default value of -2. In the case of flags, a value of 1 will set the flag and a value of 0 will unset it. All string variables except actor_name
default to the string "same", which results in no change to the corresponding field. The variable actor_name
is required.
x_coord
to the new x coordinate. Unless dest_x
is defined, x_coord
is used as the x-value of the destination, as well.
y_coord
to the new y coordinate. Unless dest_y
is defined, y_coord
is used as the y-value of the destination, as well.
dest_x
to the new x destination.
dest_y
to the new y destination.
spawned
to the new spawned value.
animation
to the new animation value.
orient
to the new facing direction.
expiry
to the new expiration time.
wander
to the new wander-distance value.
follow
to the new follow-distance value.
times_talked
to the new number of times the actor has been talked to.
flag_cre_unattached
to the new value of the flag known as CRE attached (bit 0).
flag_seen_party
to the new value of the flag known as has seen party (bit 1).
flag_invulnerable
to the new value of the flag known as invulnerable (bit 2).
flag_override_script_name
to the new value of the flag known as override script name (bit 3).
flag_time_0
through flag_time_23
to the new values of the actor’s appearance schedule.
actor_name
to the name of the actor to be patched. This variable is required.
dlg_file
to the resref of the new dialogue file.
script_override
to the resref of the new override script.
script_general
to the resref of the new general script.
script_class
to the resref of the new class script.
script_race
to the resref of the new race script.
script_default
to the resref of the new default script.
script_specifics
to the resref of the new specifics script.
cre_file
to the resref of the new creature file.
ALTER_AREA_CONTAINER
: patch area containers, but not their contents. This is a PATCH function. All integer variables default to -1 and negative values result in no change to the corresponding field. In the case of flags, a value of 1 will set the flag and a value of 0 will unset it. All string variables except container_name
default to the string "same", which results in no change to the corresponding field. The variable container_name
is required.
coord_x
to the new x coordinate.
coord_y
to the new y coordinate.
container_type
to the new type of the container.
trapped
to whether the container should be trapped.
detected
to whether the container trap should be detected.
launch_x
to the new x coordinate of the launch point.
launch_y
to the new y coordinate of the launch point.
bounding_left
to the new value of the left edge of the bounding box. The vertices remain unchanged.
bounding_top
to the new value of the top edge of the bounding box. The vertices remain unchanged.
bounding_right
to the new value of the right edge of the bounding box. The vertices remain unchanged.
bounding_bottom
to the new value of the bottom edge of the bounding box. The vertices remain unchanged.
range
to the new trigger-range value.
lockpick_strref
to the new strref of the lockpick string.
lock_difficulty
to the new difficulty of the lock.
trap_detect
to the new difficulty of trap detection.
trap_remove
to the new difficulty of trap removal.
flag_locked
to the new value of the flag known as locked (bit 0).
flag_mlocked
to the new value of the flag known as magical lock (bit 2).
flag_resets
to the new value of the flag known as trap resets (bit 3).
flag_disabled
to the new value of the flag known as disabled (bit 5).
container_name
to the name of the container to be patched. This variable is required.
container_script
to the resref of the new container script.
container_key
to the resref of the new container key.
ALTER_AREA_DOOR
: patch area doors. This is a PATCH function. All integer variables, except string_unlock
and string_speaker
, default to -1 and negative values result in no change to the corresponding field. The variables string_unlock
and string_speaker
instead default to 99999999. In the case of flags, a value of 1 will set the flag and a value of 0 will unset it. All string variables except door_name
default to the string "same", which results in no change to the corresponding field. The variable door_name
is required.
bounding_open_left
to the new value of the left edge of the open-state bounding box. The vertices remain unchanged.
bounding_open_top
to the new value of the top edge of the open-state bounding box. The vertices remain unchanged.
bounding_open_right
to the new value of the right edge of the open-state bounding box. The vertices remain unchanged.
bounding_open_bottom
to the new value of the bottom edge of the open-state bounding box. The vertices remain unchanged.
bounding_closed_left
to the new value of the left edge of the closed-state bounding box. The vertices remain unchanged.
bounding_closed_top
to the new value of the top edge of the closed-state bounding box. The vertices remain unchanged.
bounding_closed_right
to the new value of the right edge of the closed-state bounding box. The vertices remain unchanged.
bounding_closed_bottom
to the new value of the bottom edge of the closed-state bounding box. The vertices remain unchanged.
door_hp
to the new hit-point value.
door_ac
to the new armor-class value.
cursor
to the new cursor index.
trap_detect
to the new difficulty of trap detection.
trap_remove
to the new difficulty of trap removal.
trapped
to whether the door should be trapped.
detected
to whether the door trap should be detected.
launch_x
to the new x coordinate of the launch point.
launch_y
to the new y coordinate of the launch point.
door_detect
to the new difficulty of door detection.
lock_difficulty
to the new difficulty of the lock.
open_x
to the new x coordinate of the opening point.
open_y
to the new y coordinate of the opening point.
close_x
to the new x coordinate of the closing point.
close_y
to the new y coordinate of the closing point.
string_unlock
to the new string reference of the unlock message.
string_speaker
to the new string reference of the dialogue-speaker name.
flag_open
to the new value of the flag known as door open (bit 0).
flag_locked
to the new value of the flag known as door locked (bit 1).
flag_resets
to the new value of the flag known as reset trap (bit 2).
flag_detectable
to the new value of the flag known as trap detectable (bit 3).
flag_forced
to the new value of the flag known as broken (bit 4).
flag_no_close
to the new value of the flag known as can’t close (bit 5).
flag_located
to the new value of the flag known as linked (bit 6).
flag_secret
to the new value of the flag known as door hidden (bit 7).
flag_detected
to the new value of the flag known as door found (bit 8).
flag_no_look
to the new value of the flag known as don’t block line of sight (bit 9).
flag_uses_key
to the new value of the flag known as remove key (bit 10).
flag_sliding
to the new value of the flag known as ignore obstacles when closing (bit 11).
door_name
to the name of the door to be patched. This variable is required.
door_open_sound
to the resource reference of the opening sound.
door_close_sound
to the resource reference of the closing sound.
door_key
to the resref of the new door key.
door_script
to the resref of the new door script.
travel_trigger
to the new travel-trigger name. This field is 24 bytes in length.
dialogue
to the new resource reference of the dialogue file.
ALTER_ITEM_EFFECT
: patch global (equipping) effects and/or effects on extended headers. This is a PATCH function. All integer variables except check_globals
, check_headers
, header
and savebonus
default to -1 and negative values result in no change to the corresponding field. The integer variables check_globals
, check_headers
and header
default to 0. The integer variable savebonus
defaults to -11 and values lower than -10 result in no change to the corresponding field. The string variable resource
defaults to the string "same", which results in no change to the corresponding field.
Note that both check_globals
and check_headers
are 0 by default, so you need to change one or both of these to 1 to perform any patching at all. If you patch headers, you can further target by using type to target melee, ranged, or magical headers. match_opcode
can be left at -1 to match all effects on your targeted range or targeted to a specific opcode. If the opcode itself needs to be changed, you can use match_opcode
to target the existing effect and new_opcode
as the new opcode to use. duration_high
is an alternative to duration
, mainly for changing the overall duration of an item’s effects. The idea is to allow you to mass patch effects to new durations while leaving the one-time only cosmetics and visuals--which are usual instant or only run for a few seconds--unchanged.
check_globals
to whether to check global (on-equip) effects (0 for no, 1 for yes). (default 0)
check_headers
to whether to check effects on extended headers (0 for no, 1 for yes). (default 0)
header
to the number of the ability header that should be altered. A value of 0 matches all headers. (default 0)
header_type
to which type of header that should be checked. If this variable is -1, all header types will be checked.
match_opcode
to which type of opcode to match. If this variable is -1, all opcodes are a match.
new_opcode
to the value the matched opcode should be changed into.
target
to the new target.
power
to the new power.
parameter1
to the new parameter1.
parameter2
to the new parameter2.
timing
to the new timing mode.
resist_dispel
to the new resist/dispel.
duration
to the new duration.
duration_high
to the new duration, but is only written if the existing duration is greater than 5.
probability1
to the new probability1 (the upper bound).
probability2
to the new probability2 (the lower bound).
dicenumber
to the new number of dice. This field is instead used for maximum hit-dice (HD) by some opcodes.
dicesize
to the new size of dice. This field is also instead for minimum hit-dice (HD) by some opcodes.
savingthrow
to the new saving-throw type.
savebonus
to the new saving-throw bonus/penalty. This variable can take negative values down to -10 while still writing to the corresponding field. (default -11)
special
to the new special parameter.
resource
to the new resource reference.
ALTER_ITEM_HEADER
: patch ability headers on items. This is a PATCH function. All integer variables except match_icon
and header
default to -1 and negative values result in no change to the corresponding field. The integer variables match_icon
and header
default to 0. In the case of flags, a value of 1 will set the flag and a value of 0 will unset it. The string variable icon
defaults to the string "same", which results in no change to the corresponding field.
header_type
is used to limit the scope of matching. The default value of -1 will match all types of headers while values of 0-4 will match headers with those values. If match_icon
is 1, the icon resource reference will be match with the icon
variable as an additional qualifier. This is useful for items which have multiple magic abilities. header
can also be used to limit patching to the Nth header, counting from 1 as the first header. The default is 0, which will match all headers.
header_type
to the type of header to be matched. If this variable is -1, all headers will be a match. (default -1)
match_icon
to whether to match the ability icon (0 for no, 1 for yes). (default 0)
header
to the number of the ability header (starting from 1) that should be altered. A value of 0 matches all header. (default 0)
new_header_type
to the value the matched header should be changed into.
identify
to the new identification requirement.
location
to the new new ability location.
target
to the new target.
target_count
to the new target count.
range
to the new range.
launcher
to the new required launcher.
speed
to the new speed factor.
thac0_bonus
to the new THAC0 bonus.
dicesize
to the new dice size.
primary_type
to the new primary type (school).
dicenumber
to the new number of dice.
secondary_type
to the new secondary type.
damage_bonus
to the new damage bonus.
damage_type
to the new damage type.
charges
to the new number of charges.
drained
to the new charge-depletion behaviour.
projectile
to the new projectile.
animation_overhand
to the new overhand animation percentage.
animation_backhand
to the new backhand animation percentage.
animation_thrust
to the new thrust animation percentage.
arrow
to the new arrow qualifier.
bolt
to the new bolt qualifier.
bullet
to the new bullet qualifier.
flag_strength
to the new value of the flag known as add strength bonus (bit 0).
flag_break
to the new value of the flag known as breakable (bit 1).
flag_strength_damage
to the new value of the flag known as add strength bonus to damage only (bit 2). This flag is not available in all games.
flag_strength_thac0
to the new value of the flag known as add strength bonus to THAC0 only (bit 3). This flag is not available in all games.
flag_hostile
to the new value of the flag known as hostile (bit 10).
flag_recharge
to the new value of the flag known as recharges (bit 11).
flag_bypass
to the new value of the flag known as bypass armor (bit 16). This flag is not available in all games.
flag_keenedge
to the new value of the flag known as keen edge (bit 17). This flag is not available in all games.
flag_backstab
to the new value of the flag known as toggle backstab (bit 25). This flag is not available in all games.
flag_noinvisible
to the new value of the flag known as cannot target invisible (bit 26). This flag is not available in all games.
icon
to the resource reference to be used for matching if match_icon
is 1, or the new ability icon if match_icon
is 0.
DELETE_ITEM_HEADER
: delete ability headers, also known as extended headers, from items. This is a PATCH function. All integer variables default to 0.
This function will delete one or more ability headers, along with all of their associated effects, and properly re-index the file.
header_type
to the header type to delete. If this variable is -1, all header types will be a match. (default 0)
ALTER_SPELL_EFFECT
: patch effects on spells. This is a PATCH function. All integer variables except check_globals
, check_headers
, header
and savebonus
default to -1 and negative values result in no change to the corresponding field. The integer variables check_globals
and header
default to 0. The integer variable check_headers
defaults to 1. The integer variable savebonus
defaults to -11 and values lower than -10 result in no change to the corresponding field. The string variable resource
defaults to the string "same", which results in no change to the corresponding field.
The function will by default only check effects on headers. You can change this by changing the values of the variables check_globals
and check_headers
. If you patch headers, you can further target by using type to target melee, ranged, or magical headers. match_opcode
can be left at -1 to match all effects on your targeted range or targeted to a specific opcode. If the opcode itself needs to be changed, you can use match_opcode
to target the existing effect and new_opcode
as the new opcode to use. duration_high
is an alternative to duration
, mainly for changing the overall duration of an item’s effects. The idea is to allow you to mass patch effects to new durations while leaving the one-time only cosmetics and visuals--which are usual instant or only run for a few seconds--unchanged.
check_globals
to whether to check global effects (0 for no, 1 for yes). (default 0)
check_headers
to whether to check effects on extended headers (0 for no, 1 for yes). (default 1)
header
to the number of the ability header that should be altered. A value of 0 matches all headers. (default 0)
header_type
to which type of header that should be checked. If this variable is -1, all header types will be checked.
match_opcode
to which type of opcode to match. If this variable is -1, all opcodes are a match.
new_opcode
to the value the matched opcode should be changed into.
target
to the new target.
power
to the new power.
parameter1
to the new parameter1.
parameter2
to the new parameter2.
timing
to the new timing mode.
resist_dispel
to the new resist/dispel.
duration
to the new duration.
duration_high
to the new duration, but is only written if the existing duration is greater than 5.
probability1
to the new probability1 (the upper bound).
probability2
to the new probability2 (the lower bound).
dicenumber
to the new number of dice. This field is instead used for maximum hit-dice (HD) by some opcodes.
dicesize
to the new size of dice. This field is also instead for minimum hit-dice (HD) by some opcodes.
savingthrow
to the new saving-throw type.
savebonus
to the new saving-throw bonus/penalty. This variable can take negative values down to -10 while still writing to the corresponding field. (default -11)
special
to the new special parameter.
resource
to the new resource reference.
ALTER_SPELL_HEADER
: patch ability headers on spells. This is a PATCH function.All integer variables except match_icon
and header
default to -1 and negative values result in no change to the corresponding field. The integer variables match_icon
and header
default to 0. The string variable icon
defaults to the string "same", which results in no change to the corresponding field.
header_type
is used to limit the scope of matching. The default value of -1 will match all types of headers while values of 0-4 will match headers with those values. If match_icon
is 1, the icon resource reference will be match with the icon
variable as an additional qualifier. This is useful for items which have multiple magic abilities. header
can also be used to limit patching to the Nth header, counting from 1 as the first header. The default is 0, which will match all headers.
header_type
to the type of header to be matched. If this variable is -1, all headers will be a match. (default -1)
match_icon
to whether to match the ability icon (0 for no, 1 for yes). (default 0)
header
to the number of the ability header (starting from 1) that should be altered. A value of 0 matches all header. (default 0)
new_header_type
to the value the matched header should be changed into.
location
to the new new ability location.
target
to the new target.
target_count
to the new target count.
range
to the new range.
min_level
to the new required minimum level.
speed
to the new speed factor.
thac0_bonus
to the new THAC0 bonus.
dicesize
to the new dice size.
dicenumber
to the new number of dice.
damage_bonus
to the new damage bonus.
damage_type
to the new damage type.
charges
to the new number of charges.
projectile
to the new projectile.
icon
to the resource reference to be used for matching if match_icon
is 1, or the new ability icon if match_icon
is 0.
DELETE_SPELL_HEADER
: delete ability headers, also known as extended headers, from spells. This is a PATCH function. The integer variable header_type
defaults to 0. The integer variable min_level
defaults to -1 and if this variable is 0 or greater, only headers with a matching minimum level will be deleted.
This function will delete one or more ability headers, along with all of their associated effects, and properly re-index the file.
header_type
to the header type to delete. If this variable is -1, all header types will be a match. (default 0)
min_level
to the minimum level to be matched. If this variable is negative, it will not be used for matching. (default -1)
CLONE_EFFECT
: This is a patch function for creature, item, or spell files that will match an existing effect and create a new one based on the matched effect. Numerous variables are available for matching the specified effect and for specifying new values in the new effect based on the matched effect.
The first batch of variables are meta-variables which will help determine the scope of the function:
check_globals
to whether the function should loop through global effects on items (also known as equipping effects) and spells (default is 1). Creature effects are all global effects, so this variable will always be considered to be one when this function is run on a creature.
check_headers
to whether the function should loop through effects on extended headers on items and spells if set to 1 (default is 1). Creatures have no extended headers, so this variable is ignored when used on creatures.
header
to whether the function should target effects on one specific header, counting the first header as zero. A negative value will match all headers (default is -1). Creatures have no extended headers, so this variable is ignored when used on creatures.
header_type
to whether the function should only clone effects on extended headers of the specified type (1 - melee, 2 - ranged, etc.). Negative values will look at effects on all headers (default is -1). Creatures have no extended headers, so this variable is ignored when used on creatures.
multi_match
to the number of effects to clone in the active stack. If you just want to match the first effect and have the function stop, use 1. Otherwise the function will continue matching until the number of cloned effects matches this value. The function will always make at least one change (e.g. 0 or negative values are treated as 1). (default is 999).
verbose
to whether the function should provide some rudimentary information on how many effects were added (default is 0).
silent
to whether the function should suppress warnings when no effects were added. This option also suppresses the verbose option (default is 0).
insert
to the relative position the cloned effect should be inserted at. A value of ’below’ puts the new, cloned effect immediately below the matched effect. Values of ’first’ or last’ will put the new effect at the top or bottom of the effect stack, respectively. All other values will default to ’above’, where the effect is added immediately before the matched effect (default is ’above’).
The next batch of variables sets the function boundaries on matching an effect to clone. Any variables not specified will not be used to determine a match. The function will only determine an effect is a match only if ALL of the variables specified are matched.
match_opcode
to the opcode of effects to be matched (default -1).
match_target
to the target of effects to be matched (default -1).
match_power
to the power of effects to be matched (default -1).
match_parameter1
to parameter1 of effects to be matched (default -1).
match_parameter2
to parameter2 of effects to be matched (default -1).
match_timing
to the timing of effects to be matched (default -1).
match_resist_dispel
to the resist/dispel setting of effects to be matched (default -1).
match_duration
to the duration of effects to be matched (default -1).
match_probability1
to probability1 (the upper bound) of effects to be matched (default -1).
match_probability2
to probability2 (the lower bound) of effects to be matched (default -1).
match_dicenumber
to the number of dice of effects to be matched (default -1). This field is instead used for maximum hit-dice (HD) by some opcodes.
match_dicesize
to the size of dice of effects to be matched (default -1). This field is used instead for minimum hit-dice (HD) by some opcodes.
match_savingthrow
to the saving throw type of effects to be matched (default -1).
match_savebonus
to the saving throw bonus of effects to be matched (default -11). Since saving throws can range into negative values, this variable can match all the way down to -10.
match_special
to the special field of effects to be matched (default -1). The special field is used by some EE effects.
match_resource
to the resource of effects to be matched (default "SAME").
Once a matching effect is found, a new effect is created (STR_VAR
insert
determines its placement) with all of the same
specifications as the matched effect. The next series of variables
allows the function to change the fields to new values in the
newly-created effect. All integer variables have the default value -1,
except savebonus which defaults to -11. If the value of a variable is
less than or equal to the default value, the new effect inherits the
corresponding value of the matched effect.
opcode
to the value the of the new effect’s opcode.
target
to the new effect’s target.
power
to the new effect’s power.
parameter1
to the new effect’s parameter1.
parameter2
to the new effect’s parameter2.
timing
to the new effect’s timing mode.
resist_dispel
to the new effect’s resist/dispel setting.
duration
to the new effect’s duration.
probability1
to the new effect’s probability1 (the upper bound).
probability2
to the new effect’s probability2 (the lower bound).
dicenumber
to the new effect’s number of dice. This field is instead used for maximum hit-dice (HD) by some opcodes.
dicesize
to the new effect’s size of dice. This field is also instead for minimum hit-dice (HD) by some opcodes.
savingthrow
to the new effect’s saving-throw type.
savebonus
to the new effect’s saving-throw bonus/penalty. This variable can take negative values down to -10 while still writing to the corresponding field.
special
to the new effect’s special field. The special field is used by many EE effects.
resource
to the new effect’s resource reference.
DELETE_EFFECT
: This is a patch function for creature, item, or spell files that will match an existing effect and then delete it. Numerous variables are available for matching the specified effect.
The first batch of variables are meta-variables which will help determine the scope of the function:
check_globals
to whether the function should loop through global effects on items (also known as equipping effects) and spells (default is 1). Creature effects are all global effects, so this variable will always be considered to be one when this function is run on a creature.
check_headers
to whether the function should loop through effects on extended headers on items and spells (default is 1). Creatures have no extended headers, so this variable is ignored when used on creatures.
header
to whether the function should target effects on one specific header, counting the first header as zero. A negative value will match all headers (default is -1). Creatures have no extended headers, so this variable is ignored when used on creatures.
header_type
to whether the function should only delete effects on extended headers of the specified type (1 - melee, 2 - ranged, etc.). Negative values will look at effects on all headers (default is -1). Creatures have no extended headers, so this variable is ignored when used on creatures.
multi_match
to the number of effects to delete in the active stack. If you just want to match the first effect and have the function stop, use 1. Otherwise the function will continue matching until the number of deleted effects matches this value. The function will always make at least one change (e.g. 0 or negative values are treated as 1). (default is 999).
verbose
to whether the function should provide some rudimentary information on how many effects were deleted (default is 0).
The next batch of variables sets the function boundaries on matching an effect to delete. Any variables not specified will not be used to determine a match. The function will only determine an effect is a match only if ALL of the variables specified are matched.
match_opcode
to the opcode of effects to be matched (default -1).
match_target
to the target of effects to be matched (default -1).
match_power
to the power of effects to be matched (default -1).
match_parameter1
to parameter1 of effects to be matched (default -1).
match_parameter2
to parameter2 of effects to be matched (default -1).
match_timing
to the timing of effects to be matched (default -1).
match_resist_dispel
to the resist/dispel setting of effects to be matched (default -1).
match_duration
to the duration of effects to be matched (default -1).
match_probability1
to probability1 (the upper bound) of effects to be matched (default -1).
match_probability2
to probability2 (the lower bound) of effects to be matched (default -1).
match_dicenumber
to the number of dice of effects to be matched (default -1). This field is instead used for maximum hit-dice (HD) by some opcodes.
match_dicesize
to the size of dice of effects to be matched (default -1). This field is used instead for minimum hit-dice (HD) by some opcodes.
match_savingthrow
to the saving throw type of effects to be matched (default -1).
match_savebonus
to the saving throw bonus effects to be matched (default -11). Since saving throws can range into negative values, this variable can match all the way down to -10.
match_special
to the special field of effects to be matched (default -1). The special field is used by some EE effects.
match_resource
to the resource of effects to be matched (default "SAME").
Once a matching effect is found it is deleted. The function will continue to delete effects from the stack until the number of effects deleted matches the multi_match
variable.
ALTER_EFFECT
: This is a patch function for creature, item, or spell files that will match an existing effect and alter it based on the defined variables. Numerous variables are available for matching the specified effect and for specifying new values.
The first batch of variables are meta-variables which will help determine the scope of the function:
check_globals
to whether the function should loop through global effects on items (also known as equipping effects) and spells (default is 1). Creature effects are all global effects, so this variable will always be considered to be one when this function is run on a creature.
check_headers
to whether the function should loop through effects on extended headers on items and spells (default is 1). Creatures have no extended headers, so this variable is ignored when used on creatures.
header
to whether the function should target effects on one specific header, counting the first header as zero. A negative value will match all headers (default is -1). Creatures have no extended headers, so this variable is ignored when used on creatures.
header_type
to whether the function should only alter effects on extended headers of the specified type (1 - melee, 2 - ranged, etc.). Negative values will look at effects on all headers (default is -1). Creatures have no extended headers, so this variable is ignored when used on creatures.
multi_match
to the number of effects to alter in the active stack. If you just want to match the first effect and have the function stop, use 1. Otherwise the function will continue matching until the number of altered effects matches this value. The function will always make at least one change (e.g. 0 or negative values are treated as 1). (default is 999).
verbose
to whether the function should provide some rudimentary information on how many effects were added (default is 0).
silent
to whether the function should suppress warnings when no effects were altered. This option also suppresses the verbose option (default is 0).
The next batch of variables sets the function boundaries on matching an effect to alter. Any variables not specified will not be used to determine a match. The function will only determine an effect is a match only if ALL of the variables specified are matched.
match_opcode
to the opcode of effects to be matched (default is -1).
match_target
to the target of effects to be matched (default is -1).
match_power
to the power of effects to be matched (default is -1).
match_parameter1
to parameter1 of effects to be matched (default is -1).
match_parameter2
to parameter2 of effects to be matched (default is -1).
match_timing
to the timing of effects to be matched (default is -1).
match_resist_dispel
to the resist/dispel setting of effects to be matched (default is -1).
match_duration
to the duration of effects to be matched (default is -1).
match_probability1
to probability1 (the upper bound) of effects to be matched (default is -1).
match_probability2
to probability2 (the lower bound) of effects to be matched (default is -1).
match_dicenumber
to the number of dice of effects to be matched (default is -1). This field is instead used for maximum hit-dice (HD) by some opcodes.
match_dicesize
to the size of dice of effects to be matched (default is -1). This field is used instead for minimum hit-dice (HD) by some opcodes.
match_savingthrow
to the saving throw type of effects to be matched (default is -1).
match_savebonus
to the saving trow bonus effects to be matched (default is -11). Since saving throws can range into negative values, this variable can match all the way down to -10.
match_special
to the special field of effects to be matched (default is -1). The special field is used by some EE effects.
match_resource
to the resource of effects to be matched (default is "SAME").
Once a matching effect is found, the next series of variables allows the function to change the fields to these new values in the matched effect. All integer variables have the default value -1, except savebonus which defaults to -11. The function does not change the fields if the value of the corresponding value is less than or equal to the default value.
opcode
to the value the of the altered effect’s opcode.
target
to the altered effect’s target.
power
to the altered effect’s power.
parameter1
to the altered effect’s parameter1.
parameter2
to the altered effect’s parameter2.
timing
to the altered effect’s timing mode.
resist_dispel
to the altered effect’s resist/dispel setting.
duration
to the altered effect’s duration.
probability1
to the altered effect’s probability1 (the upper bound).
probability2
to the altered effect’s probability2 (the lower bound).
dicenumber
to the altered effect’s number of dice. This field is instead used for maximum hit-dice (HD) by some opcodes.
dicesize
to the altered effect’s size of dice. This field is also instead for minimum hit-dice (HD) by some opcodes.
savingthrow
to the altered effect’s saving-throw type.
savebonus
to the altered effect’s saving-throw bonus/penalty. This variable can take negative values down to -10 while still writing to the corresponding field.
special
to the new effect’s special field. The special field is used by many EE effects.
resource
to the altered effect’s resource reference.
SUBSTRING
: returns a substring of the provided string. This is an ACTION and PATCH function. All integer variables default to 0.
The function will fail with an error if either start
or length
are negative or if any of start
, length
or the sum of the two is greater than the length of string
.
start
to the string index (0 based) from which the start of the substring should be read.
length
to the length of the substring to be read.
string
to the string the substring should be read from.
substring
the substring read from the provided string.
ADD_CRE_SCRIPT
: assigns a script to a creature in the first available slot. This is a PATCH function. By default, the function will try all 5 script slots from SCRIPT_OVERRIDE
to SCRIPT_DEFAULT
.
The function will fail with an error if the file being patched does not have a signature (first 3 bytes) that case-sensitively equals CRE, if either offset_start
or offset_end
are negative, if offset_end
is less than offset_start
or if offset_end
is greater than the size of the file being patched.
offset_start
to the offset from which the search for an empty slot should start. (default SCRIPT_OVERRIDE
)
offset_end
to the offset at which the search should end. (default SCRIPT_DEFAULT
)
script
to the resource reference of the script to be assigned.
success
a return value indicating whether the script was successfully assigned. 1 signifies success and 0 signifies that no empty slot was found. A harmless warning will be printed if the function was unsuccessful.
HANDLE_AUDIO
: install Ogg--Vorbis compressed audio files in a safe and easy manner. This is an ACTION function.
This function supports Windows, OS X, GNU/Linux and all IE games, including BG:EE. If the game is not BG:EE, the audio will be decompressed and the resulting .wav files will be MOVEd to the override. If the game is BG:EE, the .ogg files are copied into the override and renamed into .wav files. Refer to the HANDLE_AUDIO and HANDLE_TILESETS tutorial for usage examples and a more practical explanation.
If the audio files need to be decompressed, this function will use oggdec.exe on Windows, which is by default expected to be found in audio_path
. The most recent version of oggdec can be downloaded here (direct link). On OS X, SoX will be used, which is by default also expected to be found in audio_path
. SoX compiled for OS X can be downloaded here. On GNU/Linux, oggdec will be used, but it is expected to be found on the system path and the user should install it him/herself. If you provide installation instructions for GNU/Linux, just include something to the effect of “install oggdec, which is usually part of the package vorbis-tools”.
If the decompression utility cannot be found, the user is warned that the audio files were not installed.
music
if this variable is not 0 and the game is BG:EE, the audio files are given the extension .acm instead of .wav. This variable has no effect if the game is not BG:EE. By default its value is 0.
audio_path
to the path your .ogg files are stored in. By default this variable is ~%MOD_FOLDER%/audio~
.
oggdec_path
to the path oggdec is located in. By default this variable is ~%audio_path%~
.
sox_path
to the path SoX is located in. By default this variable is ~%audio_path%~
.
output_path
to the path the audio should be installed into. By default this variable is override.
HANDLE_TILESETS
: install TISpack-compressed tilesets in a safe and easy manner. This is an ACTION function.
This function supports Windows, OS X, GNU/Linux and all IE games that support the TIS V1 file format. The program tisunpack, part of TISpack, is used to decompress the provided .tiz files and the resulting .tis files are placed in the override. Refer to the HANDLE_AUDIO and HANDLE_TILESETS tutorial for usage examples and a more practical explanation.
This function expects to find tisunpack for one or more of Windows, OS X or GNU/Linux in a common directory under the subdirectories win32, osx, and unix, respectively. For example, in mymod/tiz/win32, mymod/tiz/osx and mymod/tiz/unix, the common directory is mymod/tiz and mymod/tiz/win32/tisunpack.exe should be a valid file. For GNU/Linux, this function also supports tisunpack being located on the system path (something the user would take care of). If you choose to leave tisunpack to the user, you should mention this requirement in any installation instructions you provide.
If tisunpack for the user’s platform could not be found, the installation fails.
tiz_path
to the path your .tiz files are stored in. By default this variable is ~%MOD_FOLDER%/tiz~
.
tisunpack_path
to the common directory where the subdirectories win32, osx and unix are located. By default this variable is ~%tiz_path%~
.
output_path
to the path the tilesets should be installed into. By default this variable is override.
HANDLE_CHARSETS
: runtime-convert TRA files to/from UTF-8 in a safe and easy manner. This is an ACTION function.
This function supports Windows, OS X and GNU/Linux. This function can run in two modes: let’s call them forward and reverse. The forward mode is used for converting TRA files from legacy “ANSI” character sets into UTF-8 for use by EE-type games. The reverse mode converts UTF-8 into the legacy “ANSI” character sets used by the original game editions. The variable from_utf8
controls whether the function runs in forward or reverse. HANDLE_CHARSETS
needs to be used before any text is installed and is compatible with AUTO_TRA and all other methods of loading TRA files, unless the option out_path
is used.
Conversion is handled by the program iconv. The program is available as part of the base system on OS X and GNU/Linux but a Windows version needs to be included in your mod. A Windows version can be downloaded here.
In order to function, HANDLE_CHARSETS
needs to know a few things. First, you need to specify where you keep your TRA files. You do this with the variable tra_path
. Second, HANDLE_CHARSETS
needs to know where the Windows version of iconv is located. You do this with the variable iconv_path
. Third, HANDLE_CHARSETS
needs to know which character set the TRA files are in and which character set they should be converted into. For this, the function needs to know whether to run in forward or reverse mode; you specify this with the variable from_utf8
. If the variable is 0 (default), files are converted from a language-dependent character set into UTF-8 and cannot already be in UTF-8. If the variable is 1, files are converted from UTF-8 into a language-dependent character set. Which language-dependent character set should be used in the conversion process is specified with charset_table
or, or you can tell HANDLE_CHARSETS
to try to infer this by itself with infer_charsets
. Lastly, HANDLE_CHARSETS
needs to know which TRA files to convert and whether any of them should be reloaded. You can do this with noconvert_array
, convert_array
and reload_array
.
Unless convert_array
is specified, HANDLE_CHARSETS
will recursively convert all TRA files in tra_path
except those listed in noconvert_array
. If convert_array
is specified, only those TRA files listed in the array will be converted. convert_array
may contain references to TRA files in subdirectories of tra_path
.
infer_charsets
to whether HANDLE_CHARSETS
should try to infer which language-dependent character set is used by the language in which the mod is being installed. It uses the contents of the %language% variable (vide infra). If the contents of the variable can be recognised, HANDLE_CHARSETS
will use the character set used by the localised version of BG2 for this language. If the contents of the variable cannot be recognised, or if the TRA files use a different character set than the expected one, HANDLE_CHARSETS
will fail. Refer to the compatibility matrix below for additional information. infer_charsets
overrides charset_table
. Defaults to 0.
from_utf8
to whether the function should assume the source TRA files are in UTF-8 or not. The default value is 0, that is, the function assumes the TRA files use language-dependent charater sets and should be converted into UTF-8 when the mod is installed on EE-type games; the function produces no results on the original editions of the games. If set to 1, the source TRA files are assumed to be in UTF-8 and are to be converted into language-dependent character sets when the mod is installed on original editions of the games; the function produces no results when installed on EE-type games.
verbose
to whether the function should print debug information. Defaults to 0.
language
to the name of the language directory you wish to convert. The default value of this variable is \%LANGUAGE\%
, in other words, the directory corresponding to the language the user selected at the start of the installation. You should never need to alter the value of this variable.
default_language
to the name of the directory used by the default language of your mod, if your mod has one. The default language is typically one for which the translation is always complete and up to date and which you use to guard against incomplete translations. HANDLE_CHARSETS
will convert the TRA files of the default language in addition to those of the user-selected language. Should these two languages be the same, the TRA files will only be converted once. Additionally, any TRA files listed in the reload_array
(vide infra) will be reloaded for the default language before they are reloaded for the user-selected language. The default value of this variable is ~~
(the empty string).
tra_path
to the path where your mod’s language directories are located. %tra_path%/%language%
should be a valid directory containing TRA files.
out_path
to the path to which the converted files should be directed. The default value of this variable is %tra_path%
, that is, the converted files reversibly overwrite the originals and the conversion is transparent to the rest of your mod. Note that if %tra_path
and out_path
are the same directory, the conversion will naturally happen only once regardless of how many times HANDLE_CHARSETS
are invoked, but if the directories are different, the conversion will happen for every invocation (notably, if HANDLE_CHARSETS
is invoked among the ALWAYS actions).
iconv_path
to the path where iconv.exe is located. Defaults to %tra_path%/iconv
.
charset_table
to the name of an associative array where the keys are the names of your language directories and the corresponding values are the character sets used by the respective language. The keys must be entirely in lowercase. charset_table
is not used if you also specify infer_charsets
.
noconvert_array
to the name of an array indexed by monotonically increasing integers starting from 0. The values should be the names of TRA files that should not be converted into UTF-8. All TRA files in the language directory and its subdirectories except the ones listed in noconvert_array
will be converted. The .tra file extension is optional. This variable should not be provided if you also provide convert_array
.
convert_array
to the name of an array indexed by monotonically increasing integers starting from 0. The values should be the names of TRA files that should be converted into UTF-8. Only those TRA files in the language directory or its subdirectories which are listed in convert_array
will be converted. The .tra file extension is optional. If this variable is provided, noconvert_array
will not be used.
reload_array
to the name of an array indexed by monotonically increasing integers starting from 0. The values should be the names of TRA files which should be reloaded after they have been converted. The .tra file extension is optional. You should use this variable for reloading those TRA files loaded by LANGUAGE which should also be converted.
file_regexp
to a regexp expression with the file names that must be converted. Defaults to .+$\backslash$.tra$
.
Compatibility matrix for infer_charsets
.
Language | %LANGUAGE% contains | Inferred character set |
Simplified Chinese | schinese, zh_CN | CP936 |
Traditional Chinese | tchinese | CP950 |
Czech | czech, cs_CZ | CP1250 |
English | english, american, en_US | CP1252 |
French | french, francais, fr_FR | CP1252 |
German | german, deutsch, de_DE | CP1252 |
Italian | italian, italiano, it_IT | CP1252 |
Japanese | japanese, ja_JP | CP932 |
Korean | korean, ko_KR | CP949 |
Polish | polish, polski, pl_PL | CP1250 |
Russian | russian, ru_RU | CP1251 |
Spanish | spanish, castilian, espanol, castellano, es_ES | CP1252 |
DELETE_WORLDMAP_LINKS
: delete all links from one worldmap area to another. This is a PATCH function.
This function will delete links from one specified area to another. Links can be deleted from all four directional nodes (north, east, south, west) or from a specific node. Links connecting the areas in the opposite direction will not be deleted.
from_area
to the area from which the links that are to be deleted originate.
from_node
to the directional node from which the links that are to be deleted originate. Legal values for this variable are “north”, “n”, “east”, “e”, “south”, “s”, “west”, “w”. Values are not case sensitive. Links are deleted from all four nodes by default.
to_area
to the area to which the links that are to be deleted lead.
ADD_WORLDMAP_LINKS
: add links from one worldmap area to another. This is a PATCH function.
This function add links from one specified area to another. Links can be added from all four directional nodes (north, east, south, west) or from a specific node. If a link already exists, its metadata (distance scale, random encounters, etc.) will be overwritten. Links connecting the areas in the opposite direction will not be added. If either of the areas do not exist in the worldmap, a warning is printed and no links are added.
distance_scale
to the distance scale between the two areas. distance_scale
* 4 equals the travel time between the areas (in hours).
default_entry
to the default entry location. Legal values for this variable are 1 (northern side), 2 (eastern), 4 (southern) and 8 (western). The default value is 1.
encounter_probability
to the probability of a random encounter occurring.
from_area
to the area from which the links should originate.
from_node
to the directional node from which the links should originate. Legal values for this variable are “north”, “n”, “east”, “e”, “south”, “s”, “west” and “w”. Values are not case sensitive. Links are added to all four nodes by default.
to_area
to the area to which the links should lead.
entry
to the entry point in to_area
.
random_area1
to the first random-encounter area.
random_area2
to the second random-encounter area.
random_area3
to the third random-encounter area.
random_area4
to the fourth random-encounter area.
random_area5
to the fifth random-encounter area.
UPDATE_PVRZ_INDICES
: update the PVRZ references in a BAM V2 or MOS V2 resource. This is a PATCH function.
This function will update the PVRZ references in a BAM V2 or MOS V2 resource with the next contiguous block of free PVRZ indices. This function is intended to be used in combination with the action function INSTALL_PVRZ.
target_base_index
is an optional parameter. When specified, the function attempts to use a block of free PVRZ indices starting at the specified value. The default value is 1000.
original_base_index
returns the lowest PVRZ index used by the source BAM or MOS. Returns -1 on error.
new_base_index
returns the lowest PVRZ index used by the target BAM or MOS. Returns -1 on error.
index_range
returns the range of reserved PVRZ indices, i.e., the difference between the smallest and biggest PVRZ index, inclusive. Returns 0 on error.
INSTALL_PVRZ
: install a PVRZ file and updates the PVRZ index. This is an ACTION function.
This function copies the specified PVRZ file into the target folder and updates the PVRZ index. This function should be used in conjunction with UPDATE_PVRZ_INDICES.
original_base_index
to the current base index. This value is returned by the function UPDATE_PVRZ_INDICES as original_base_index
.
new_base_index
to the new base index. This value is returned by the function UPDATE_PVRZ_INDICES as new_base_index
.
source_file
to the source file to copy. The filename must match the regular expression ~MOS[0-9]+\.PVRZ~ (e.g., MOS0000.PVRZ, mos1592.pvrz or Mos12345.PVRZ). Case is ignored.
target_folder
to the target folder to copy the source file into. The default value is "override".
success
is set to non-zero if the function returned successfully and is set to zero on error.
FIND_FREE_PVRZ_INDEX
: attempt to find a contiguous block of free PVRZ indices in the game installation. This is a PATCH and an ACTION function.
This function attempts to find the first available free PVRZ index of a contiguous block which guarantees to fit at least num_to_reserve
indices.
num_to_reserve
to the minimum required size of the contiguous block of free indices. This number should be no smaller than 1 nor no bigger than 999. The default value is 1.
start_index
to the index the search should begin at. The default value is 1000.
free_index
returns the first available index matching the specified parameters. Returns -1 on errors.
DIRECTORY_OF_FILESPEC
: returns the directory of a file specification. Compare to SOURCE_DIRECTORY. This is an ACTION and PATCH function.
filespec
to the file specification, which must include an identifiable directory, or a warning will be raised.
directory
returns the directory of the file specification.
FILE_OF_FILESPEC
: returns the file of a file specification. Compare to SOURCE_FILE. This is an ACTION and PATCH function.
filespec
to the file specification, which may or may not include a directory.
file
returns the file of the file specification.
RES_OF_FILESPEC
: returns the resource name of a file specification. Compare to SOURCE_RES. This is an ACTION and PATCH function.
filespec
to the file specification, which must include an identifiable resource name, or a warning will be raised.
res
returns the resource name of the file specification.
EXT_OF_FILESPEC
: returns the resource extension of a file specification. Compare to SOURCE_EXT. This is an ACTION and PATCH function.
filespec
to the file specification, which must include an identifiable resource extension, or a warning will be raised.
ext
returns the resource extension of the file specification.
ADD_STORE_ITEM_EX
: adds an item to the current STO file. This is a PATCH function.
charge1
to the number of charges of the 1st ability or quantity for stackables. (Default: 0)
charge2
to the number of charges of the 2nd ability. (Default: 0)
charge3
to the number of charges of the 3rd ability. (Default: 0)
stack
to the number of item instances the store carries in the stack. (Default: 1)
unlimited
to non-zero if the store should carry an inexhaustible stack of the new item. (Default: 0)
overwrite
to non-zero to overwrite any instances of an existing sale entry of matching item resref when found. (Default: 0)
expiration
to the item’s expiration time, when it will be replaced with the drained item. (Default: 0)
item_name
to the resource name (resref) of the item to add.
position
to the desired position of the item in the list of sale entries. The following syntax is supported:
AFTER resref Will place the new item directly behind the item given by "resref". You can list more than one resref, separated by space. The new item will be added after the entry of the first matching resref. BEFORE resref Will place the new item directly before the item given by "resref". You can list more than one resref, separated by space. The new item will be added before the entry of the first matching resref. LAST Will place the new item after all existing items. FIRST Will place the new item before all existing items. AT value Will place the new item at the position given by the number "value". Use negative values to place the new item relative to the last item position in reverse order.(Default: FIRST)
flags
to numeric values or the following constants: none
, identified
, unstealable
, stolen
. Constants can be combined by using ampersand (&) or space as separators (e.g. identified&unstealable
). (Default: none
)
sale_trigger
Availability trigger (STO V1.1 only). The following syntax is supported:
Trigger string Example: GlobalGT("MyCondition","GLOBAL",0) Strref value Example: #1234 Translation reference Example: @1000(Default: #-1)
index
returns the index of the added item or the last matching index when overwriting items. Returns -1 if the item could not be added or updated.
offset
returns the offset of the added item or the last matching offset when overwriting items. Returns -1 if the item could not be added or updated.
ADD_STORE_DRINK
: adds a drink to the current STO file. This is a PATCH function.
price
to the price of the drink.
rate
to the rate (%) of displaying a rumor.
overwrite
to non-zero to overwrite any instances of an existing drink of matching drink_name
when found. (Default: 0)
drink_name
to the name of the drink.
The following syntax is supported:
Literal string Example: Elminster's Choice Beer Strref value Example: #1234 Translation reference Example: @1000
position
to the desired position in the list of drinks. Refer to the table below for supported expressions. The following syntax is supported:
AFTER name Will place the new drink directly behind the drink given by "name". Name can either be a strref value (e.g. #1234) or a translation reference (e.g. @1000). You can list more than one name, separated by space. The new drink will be added after the entry of the first matching name. BEFORE name Will place the new drink directly before the drink given by "name". Name can either be a strref value (e.g. #1234) or a translation reference (e.g. @1000). You can list more than one name, separated by space. The new drink will be added before the entry of the first matching name. LAST Will place the new drink after all existing drinks. FIRST Will place the new drink before all existing drinks. AT value Will place the new drink at the position given by the number "value". Use negative values to place the new drink relative to the last drink position in reverse order.(Default: FIRST)
index
returns the index of the added drink or the last matching index when overwriting drinks. Returns -1 if the drink could not be added or updated.
offset
returns the offset of the added drink or the last matching offset when overwriting drinks. Returns -1 if the drink could not be added or updated.
ADD_STORE_CURE
: adds a cure to the current STO file. This is a PATCH function.
price
to the spell price.
overwrite
to non-zero to overwrite any instances of an existing cure entry of matching spell resref when found. (Default: 0)
spell_name
to the resource name (resref) of the spell to add.
position
to the desired position in the list of cures. Refer to the table below for supported expressions. The following syntax is supported:
AFTER resref Will place the new spell directly behind the spell given by "resref". You can list more than one resref, separated by space. The new spell will be added after the entry of the first matching resref. BEFORE resref Will place the new spell directly before the spell given by "resref". You can list more than one resref, separated by space. The new spell will be added before the entry of the first matching resref. LAST Will place the new spell after all existing cures. FIRST Will place the new spell before all existing cures. AT value Will place the new spell at the position given by the number "value". Use negative values to place the new spell relative to the last spell position in reverse order.(Default: FIRST)
index
returns the index of the added cure or the last matching index when overwriting cure entries. Returns -1 if the spell could not be added or updated.
offset
returns the offset of the added cure or the last matching offset when overwriting cures. Returns -1 if the spell could not be added or updated.
ADD_STORE_PURCHASE
: adds one or more item categories the store will buy to the current STO file. Existing categories will be skipped.
category
to the item category to add. A nearly complete list of supported item category codes can be found here.
index
returns the index of the added purchase. Returns -1 if the purchase could not be added.
offset
returns the offset of the added purchase. Returns -1 if the purchase could not be added.
REMOVE_STORE_ITEM_EX
: removes all sale instances matching the specified item name from the current STO file. This is a patch function.
item_name
to the resource name (resref) of the item to remove. Regular expression syntax is supported.
index
returns the index of the first removed entry matching the item name, returns -1 otherwise.
offset
returns the offset of the first removed entry matching the item name, returns -1 otherwise.
REMOVE_STORE_DRINK
: removes all drink instances matching the specified drink name from the current STO file. This is a patch function.
drink_name
to the name of the drink to remove. The following syntax is supported:
Literal string Example: Elminster's Choice Beer Strref value Example: #1234 Translation reference Example: @1000Note: Regular expression syntax is supported for literal strings.
index
returns the index of the first removed entry matching the drink name, returns -1 otherwise.
offset
returns the offset of the first removed entry matching the drink name, returns -1 otherwise.
REMOVE_STORE_CURE
: removes all cure instances matching the specified spell name from the current STO file. This is a patch function.
spell_name
to the resource name (resref) of the spell to remove. Regular expression syntax is supported.
index
returns the index of the first removed entry matching the spell name, returns -1 otherwise.
offset
returns the offset of the first removed entry matching the spell name, returns -1 otherwise.
REMOVE_STORE_PURCHASE
: removes the specified item category from the current STO file. This is a patch function.
category
to the item category to remove. A nearly complete list of supported item category codes can be found here.
index
returns the index of the first removed entry matching the category, returns -1 otherwise.
offset
returns the offset of the first removed entry matching the category, returns -1 otherwise.
Adds a structure to an area file. All variables are zero or blank by default unless otherwise indicated. Fields designated by an asterisk are typically required; all others are optional. This is a PATCH function.
Universal structure variables:
fj_structure_type
to the type of area structure to be added (actor, region, spawn, entrance, container, itm, ambient, variable, door, animation, bitmask, songs, interrupts, note or projectile)*;
fj_delete_mode
to the index of the structure to be deleted (if deleting rather than adding a new structure);
fj_debug
to 1 to enable feedback
fj_return_offset
returns the position of the structure in the area file;
Actor structure variables:
fj_name
to the actor’s name*;
fj_loc_x
to the starting X (horizontal) coordinate*;
fj_loc_y
to the starting Y (vertical) coordinate*;
fj_dest_x
to the destination X coordinate (by default the same as fj_loc_x
);
fj_dest_y
to the destination Y coordinate (by default the same as fj_loc_y
);
fj_loading
to whether the .cre file is loaded (0=attached, 1=loaded, default 1);
fj_spawned
to whether the creature has been spawned (0=no, 1=yes, default 0);
fj_animation
to the actor’s animation number (from animate.ids, though the engine uses the animation set on the .cre file);
fj_orientation
to the facing direction (0-15 where 0=south, 4=west, 8=north, 12=east, default 0);
fj_expiry
to the actor removal timer in absolute ticks (default -1 to avoid removal);
fj_wander_dist_actor
to the actor’s random walk distance limit;
fj_mvmt_dist_actor
to the actor’s movement distance limit;
fj_schedule
to the hourly appearance schedule (bits 0-23, default -1 or always);
fj_num_talked
to the NumTimesTalkedTo (in .sav files);
fj_dlg_resref
to the actor’s dialogue file (normally obtained from .cre files);
fj_bcs_override
to the actor’s override script (normally obtained from .cre files);
fj_bcs_general
to the actor’s general script (normally obtained from .cre files);
fj_bcs_class
to the actor’s class script (normally obtained from .cre files);
fj_bcs_race
to the actor’s race script (normally obtained from .cre files);
fj_bcs_default
to the actor’s default script (normally obtained from .cre files);
fj_bcs_specific
to the actor’s specific script (normally obtained from .cre files);
fj_cre_resref
to the actor’s resource reference (creature filename)*;
fj_cre_embedded
to path/to/file.cre if embedding a new creature (defaults to fj_cre_resref
if not set);
Region structure variables:
fj_name
to the region’s name*;
fj_type
to the region type (0=trap, 1=info, 2=travel)*;
fj_box_left
to the leftmost X coordinate of the region’s bounding box*;
fj_box_top
to the topmost Y coordinate of the region’s bounding box*;
fj_box_right
to the rightmost X coordinate of the region’s bounding box*;
fj_box_bottom
to the bottommost Y coordinate of the region’s bounding box*;
fj_cursor_idx
to the region’s mouse cursor index (from cursors.bam)*;
fj_destination_area
to the destination area resource reference (for travel regions);
fj_destination_name
to the entrance name in the destination area (for travel regions);
fj_flags
to the bitwise region flags;
fj_info_point_strref
to the information text string reference (for info points, default -1);
fj_trap_detect
to the trap detection difficulty percentage;
fj_trap_remove
to the trap removal difficulty percentage;
fj_trap_active
to whether the region is trapped (0=no, 1=yes);
fj_trap_status
to whether the trap is detected (0=no, 1=yes);
fj_loc_x
to the trap launch X coordinate*;
fj_loc_y
to the trap launch Y coordinate*;
fj_key_resref
to the filename of the region’s key;
fj_reg_script
to the region script;
fj_alt_x
to the activation point X coordinate;
fj_alt_y
to the activation point Y coordinate;
fj_sound
to the sound resource reference (ONLY for PST);
fj_talk_loc_x
to the talk location point X coordinate (ONLY for PST);
fj_talk_loc_y
to the talk location point Y coordinate (ONLY for PST);
fj_speaker_strref
to the speaker’s name string reference (default -1; ONLY for PST);
fj_dialog
to the region’s dialogue file (ONLY for PST);
fj_vertex_0
to (X coordinate + (Y coordinate << 16)) for each vertex pair*;
Spawn structure variables:
fj_name
to the spawn point’s name*;
fj_loc_x
to the spawning X coordinate*;
fj_loc_y
to the spawning Y coordinate*;
fj_cre_resref0
to the resource reference of each creature spawned (0-9)*;
fj_spawn_num
to the count of spawn creatures*;
fj_difficulty
to the base number of creatures to spawn (encounter difficulty)*;
fj_delay
to the number of seconds between spawning (default 10);
fj_method
to the spawn method;
fj_duration
to the creature duration (default 1000);
fj_wander_dist_spawn
to the creature’s random walk distance limit (default 1000);
fj_mvmt_dist_spawn
to the creature’s movement distance limit (default 1000);
fj_max_num
to the maximum number of creatures to spawn*;
fj_enable
to the spawn point status (0=inactive, 1=active, default 1);
fj_schedule
to the hourly appearance schedule (bits 0-23, default -1 or always);
fj_day_prob
to the spawn point daytime probability (default 100);
fj_night_prob
to the spawn point nighttime probability (default 100);
fj_spawn_freq
to the spawn frequency in seconds (Enhanced Editions only);
fj_countdown
to the spawn countdown (Enhanced Editions only);
fj_weight0...fj_weight9
to the weight of the spawn creatures (Enhanced Editions only);
Entrance structure variables:
fj_name
to the entrance’s name*;
fj_loc_x
to the X coordinate*;
fj_loc_y
to the Y coordinate*;
fj_orientation
to the facing direction (0-15 where 0=south, 4=west, 8=north, 12=east)*;
Container structure variables:
fj_name
to the container’s name*;
fj_loc_x
to the X coordinate*;
fj_loc_y
to the Y coordinate*;
fj_type
to the container type (1=bag, 2=chest, 3=drawer, 4=pile, 5=table, 6=shelf, 7=altar, 8=nonvisible, 9=spellbook, 10=body, 11=barrel, 12=crate)*;
fj_lock_diff
to the lock difficulty (default 100);
fj_flags
to the bitwise container flags (bit0=locked, bit3=trap resets, bit5=disabled);
fj_trap_detect
to the trap detection difficulty percentage;
fj_trap_remove_diff
to the trap removal difficulty percentage (default 100);
fj_trap_active
to whether the container is trapped (0=no, 1=yes);
fj_trap_status
to whether the trap is detected (0=no, 1=yes);
fj_trap_loc_x
to the trap launch X coordinate*;
fj_trap_loc_y
to the trap launch Y coordinate*;
fj_box_left
to the leftmost X coordinate of the trap’s bounding box;
fj_box_top
to the topmost Y coordinate of the trap’s bounding box;
fj_box_right
to the rightmost X coordinate of the trap’s bounding box;
fj_box_bottom
to the bottommost Y coordinate of the trap’s bounding box;
fj_trap_script
to the trap’s script;
fj_vertex_0
to (X coordinate + (Y coordinate << 16)) for each vertex pair*;
fj_key_resref
to the filename of the container’s key;
fj_lockpick_strref
to the lockpick string reference (default -1);
Item structure variables:
fj_name
to the item’s resource reference (filename)*;
fj_con_itm_idx
to the index of the container to which the item is added*;
fj_itm_expiry
to the item expiration time (default 0);
fj_charge0
to the charges of the 1st ability (item quantity for stackables, default 0);
fj_charge1
to the charges of the 2nd ability (default 0);
fj_charge2
to the charges of the 3rd ability (default 0);
fj_flags
to the bitwise item flags (bit0=identified, bit1=unstealable, bit2=stolen, bit3=undroppable, default 0);
Ambient structure variables:
fj_name
to the ambient’s name*;
fj_loc_x
to the X coordinate*;
fj_loc_y
to the Y coordinate*;
fj_radius
to the sound radius (default 500);
fj_loc_z
to the Z coordinate (height);
fj_pitch_variance
to the pitch variance;
fj_volume_variance
to the volume variance;
fj_volume
to the volume percentage (default 80);
fj_wav_resref0
to the resource reference of each sound (0-9)*;
fj_sound_num
to the number of sounds*;
fj_delay
to the base interval in seconds between sounds from this ambient list;
fj_variation
to the base deviation from the base interval;
fj_schedule
to the hourly appearance schedule (bits 0-23, default -1 or always);
fj_flags
to the bitwise ambient flags*;
Variable structure variables:
fj_name
to the variable’s name*;
fj_variable_value
to the variable’s value*;
Door structure variables:
fj_name
to the door’s name*;
fj_door_wed_id
to the door ID linked to the .wed file*;
fj_flags
to the bitwise door flags*;
fj_open_box_left
to the leftmost X coordinate of the open door’s bounding box*;
fj_open_box_top
to the topmost Y coordinate of the open door’s bounding box*;
fj_open_box_right
to the rightmost X coordinate of the open door’s bounding box*;
fj_open_box_bottom
to the bottommost Y coordinate of the open door’s bounding box*;
fj_closed_box_left
to the leftmost X coordinate of the closed door’s bounding box*;
fj_closed_box_top
to the topmost Y coordinate of the closed door’s bounding box*;
fj_closed_box_right
to the rightmost X coordinate of the closed door’s bounding box*;
fj_closed_box_bottom
to the bottommost Y coordinate of the closed door’s bounding box*;
fj_door_open_vert_0
to (X coordinate + (Y coordinate << 16)) for each vertex pair of the open door*;
fj_door_closed_vert_0
to (X coordinate + (Y coordinate << 16)) for each vertex pair of the closed door*;
fj_cell_open_vert_0
to (X coordinate + (Y coordinate << 16)) for each impeded search map cell of the open door*;
fj_cell_closed_vert_0
to (X coordinate + (Y coordinate << 16)) for each impeded search map cell of the closed door*;
fj_door_open_wav
to the door open sound;
fj_door_close_wav
to the door closed sound;
fj_cursor_idx
to the door’s mouse cursor index (from cursors.bam)*;
fj_trap_detect
to the trap detection difficulty percentage;
fj_trap_remove
to the trap removal difficulty percentage;
fj_trap_active
to whether the door is trapped (0=no, 1=yes);
fj_trap_status
to whether the trap is detected (0=no, 1=yes);
fj_trap_loc_x
to the trap launch X coordinate*;
fj_trap_loc_y
to the trap launch Y coordinate*;
fj_key_resref
to the filename of the door’s key;
fj_door_script
to the door script;
fj_detect_diff
to the detection difficulty (for secret doors);
fj_locked_diff
to the lock difficulty;
fj_open_loc_x
to the X coordinate for toggling the door’s open state*;
fj_open_loc_y
to the Y coordinate for toggling the door’s open state*;
fj_closed_loc_x
to the X coordinate for toggling the door’s closed state*;
fj_closed_loc_y
to the Y coordinate for toggling the door’s closed state*;
fj_lockpick_strref
to the lockpick string reference (default -1);
fj_travel_trigger
to the travel region name*;
fj_dlg_strref
to the dialogue string reference (default -1);
fj_dlg_resref
to the door’s dialogue file;
Animation structure variables:
fj_name
to the animation name*;
fj_loc_x
to the X coordinate*;
fj_loc_y
to the Y coordinate*;
fj_schedule
to the hourly appearance schedule (bits 0-23, default -1 or always);
fj_bam_resref
to the animation resource reference (filename)*;
fj_bam_seq
to the BAM sequence number;
fj_bam_frame
to the BAM frame number;
fj_flags
to the bitwise animation flags*;
fj_loc_z
to the height;
fj_transparent
to the BAM transparency;
fj_init_frame
to the starting frame;
fj_loop_chance
to the chance of looping;
fj_skip_cycles
to start delay in frames;
fj_bmp_resref
to the palette bitmap;
fj_width
to the animation width (only required for WBM and PVRZ resources, Enhanced Editions only);
fj_height
to the animation height (only required for WBM and PVRZ resources, Enhanced Editions only);
Bitmask structure variables:
fj_bitmask
to path/to/binary.file *;
Songlist structure variables:
fj_song_day
to the day SONGLIST.2DA entry;
fj_song_night
to the night SONGLIST.2DA entry;
fj_song_victory
to the victory SONGLIST.2DA entry;
fj_song_battle
to the battle SONGLIST.2DA entry;
fj_song_defeat
to the defeat SONGLIST.2DA entry;
fj_song_day_alt
to the alternative day SONGLIST.2DA entry (default -1, Enhanced Editions only);
fj_song_night_alt
to the alternative night SONGLIST.2DA entry (default -1, Enhanced Editions only);
fj_song_victory_alt
to the alternative victory SONGLIST.2DA entry (default -1, Enhanced Editions only);
fj_song_battle_alt
to the alternative battle SONGLIST.2DA entry (default -1, Enhanced Editions only);
fj_song_defeat_alt
to the alternative defeat SONGLIST.2DA entry (default -1, Enhanced Editions only);
fj_song_day0
to the day song WAV resref;
fj_song_day1
to the night song WAV resref;
fj_song_day_vol
to the day songs volume (default 100);
fj_song_night0
to the night song WAV resref;
fj_song_night1
to the second night song WAV resref;
fj_song_night_vol
to the night songs volume (default 100);
Rest interrupt structure variables
fj_name
to the name of the rest interrupts (for editor use only);
fj_cre_strref0...fj_cre_strref9
to string displayed upon party ambush (default -1);
fj_cre_resref0...fj_cre_resref9
to creature resref;
fj_spawn_num
to the number of spawned attackers;
fj_difficulty
to the difficulty of the encounter;
fj_duration
to the creature’s duration (default 1000);
fj_wander_distance
to the creature’s random walk distance limit (default 1000);
fj_mvmt_distance
to the creature’s movement distance limit (default 1000);
fj_max_num
to maximum number of spawned creatures;
fj_enable
to whether rest interrupts are enabled (0=no, 1=yes);
fj_day_prob
to probability of daytime ambush;
fj_night_prob
to probability of nightime ambush;
Map note structure variables:
fj_loc_x
to the X coordinate*;
fj_loc_y
to the Y coordinate*;
fj_note_strref
to the note string reference (default -1, BGII only)*;
fj_note_text
to the note text (PST only)*;
fj_strref_loc
to the strref location (0=external, 1=dialog.tlk, default 1);
fj_color
to the map marker color (0-7);
fj_note_id
to the note ID;
Projectile trap structure variables (not available on PST):
fj_name
to the projectile filename*;
fj_eff_off
to the effect block offset;
fj_eff_size
to the effect block size;
fj_missile_num
to the missile.ids reference (projectl.ids - 1);
fj_frequency
to the explosion length (in frames);
fj_duration
to the number of explosions;
fj_loc_x
to the X coordinate*;
fj_loc_y
to the Y coordinate*;
fj_loc_z
to the height;
fj_target
to the ea.ids value of the target;
fj_creator
to the index of the party member who created this projectile (0-5);
fj_embedded_eff_0
to path/to/v2.eff or eff resref containing to projectile’s effects* Subsequent indices can be used for additional effects;
A few examples best illustrate the use of this function.
Example 1: add an actor to an area
COPY_EXISTING ar0500.are override LPF fj_are_structure INT_VAR fj_loc_x = 2780 fj_loc_y = 1955 fj_dest_x = 2780 fj_dest_y = 1955 fj_animation = 0x6110 //fighter female human fj_orientation = 15 //SSE STR_VAR fj_structure_type = actor fj_name = Aurora fj_cre_resref = agaurora END
Example 2: add a region to an area (in this case, a travel trigger to another area)
LPF fj_are_structure INT_VAR fj_type = 2 //travel fj_box_left = 3415 fj_box_top = 625 fj_box_right = 3450 fj_box_bottom = 700 fj_cursor_idx = 30 //door fj_vertex_0 = 3415 + (625 << 16) fj_vertex_1 = 3450 + (650 << 16) fj_vertex_2 = 3450 + (700 << 16) fj_vertex_3 = 3415 + (676 << 16) STR_VAR fj_structure_type = region fj_name = Tran0540 fj_destination_area = ag0540 fj_destination_name = Exit0500 END
Example 3: add an entrance (from another area) to an area
LPF fj_are_structure INT_VAR fj_loc_x = 3490 fj_loc_y = 655 fj_orientation = 10 //NE STR_VAR fj_structure_type = entrance fj_name = Exit0540 END
Example 4: add a container to an area, then add an item to the new container
LPF fj_are_structure INT_VAR fj_type = 8 //nonvisible fj_loc_x = 4388 fj_loc_y = 2876 fj_box_left = 4372 fj_box_top = 2826 fj_box_right = 4420 fj_box_bottom = 2858 fj_trap_loc_x = 4380 fj_trap_loc_y = 2870 fj_vertex_0 = 4411 + (2858 << 16) fj_vertex_1 = 4372 + (2845 << 16) fj_vertex_2 = 4382 + (2826 << 16) fj_vertex_3 = 4420 + (2839 << 16) STR_VAR fj_structure_type = container fj_name = ~Cornerstone~ END LPF fj_are_structure INT_VAR fj_con_itm_idx = SHORT_AT 0x74 - 1 // a new container will be last in the file fj_flags = 1 // identified STR_VAR fj_name = c6lantho fj_structure_type = itm END
Example 5: add a door to an area
LPF fj_are_structure INT_VAR fj_flags = 0b100000000 fj_open_box_left = 520 fj_open_box_top = 724 fj_open_box_right = 545 fj_open_box_bottom = 830 fj_closed_box_left = 507 fj_closed_box_top = 761 fj_closed_box_right = 562 fj_closed_box_bottom = 869 fj_cursor_idx = 30 fj_trap_loc_x = 500 fj_trap_loc_y = 852 fj_open_loc_x = 517 fj_open_loc_y = 881 fj_closed_loc_x = 562 fj_closed_loc_y = 814 fj_door_open_vert_0 = 520 + (826 << 16) fj_door_open_vert_1 = 527 + (830 << 16) fj_door_open_vert_2 = 545 + (798 << 16) fj_door_open_vert_3 = 545 + (727 << 16) fj_door_open_vert_4 = 539 + (724 << 16) fj_door_open_vert_5 = 520 + (750 << 16) fj_door_closed_vert_0 = 507 + (831 << 16) fj_door_closed_vert_1 = 562 + (869 << 16) fj_door_closed_vert_2 = 562 + (799 << 16) fj_door_closed_vert_3 = 507 + (761 << 16) fj_cell_open_vert_0 = 32 + (68 << 16) fj_cell_open_vert_1 = 33 + (67 << 16) fj_cell_open_vert_2 = 32 + (67 << 16) fj_cell_closed_vert_0 = 32 + (70 << 16) fj_cell_closed_vert_1 = 33 + (71 << 16) fj_cell_closed_vert_2 = 34 + (72 << 16) fj_cell_closed_vert_3 = 34 + (71 << 16) fj_cell_closed_vert_4 = 33 + (70 << 16) fj_cell_closed_vert_5 = 32 + (69 << 16) fj_cell_closed_vert_6 = 32 + (68 << 16) fj_cell_closed_vert_7 = 33 + (69 << 16) fj_cell_closed_vert_8 = 34 + (70 << 16) STR_VAR fj_structure_type = door fj_name = Door10 fj_door_wed_id = DOOR10 END
Example 6: add an animation to an area
LPF fj_are_structure INT_VAR fj_loc_x = 2785 fj_loc_y = 949 fj_flags = 0b00000000000000000001000110000101 //visible, not illuminated, invisible in dark, covered by actors, shown in combat STR_VAR fj_structure_type = animation fj_name = Cave1 fj_bam_resref = ag1100c1 END
Example 7: delete all ambients from an area
FOR( i = SHORT_AT 0x82 ; i ; --i )BEGIN LPF fj_are_structure INT_VAR fj_delete_mode = i - 1 STR_VAR fj_structure_type = ambient END END
Adds an area to the worldmap. All variables are zero or blank by default unless otherwise indicated. This is an ACTION function.
toNewArea
to the area references that lead to the new area;
fromNewArea
to the area references that lead away from the new area;
areName
to the resource reference of the area to add (like "ar0700");
strName
to the descriptive name of the area (like "Waukeen’s Promenade");
strDesc
to the area description that will show up when hovering the cursor over the area on the worldmap;
worldmap
to the name of the .wmp file you want patched (default is "worldmap");
mapIcon
to the area’s map icon index (from mapicons.bam);
xCoord
to the area’s X (east-west) coordinate;
yCoord
to the area’s Y (north-south) coordinate;
tTime
to the area’s travel time in hours * 4 (so 2 = 8 hours);
inclSv
to 1 if you want to patch saved game worldmaps as well as the master worldmap. NB: changes to saved games are uninstallable;
visible
to 1 if you want the ’visible’ flag to be set;
visibleAdjacent
to 1 if you want the ’visible from adjacent’ flag to be set;
reachable
to 1 if you want the ’reachable’ flag to be set;
visited
to 1 if you want the ’visited’ flag to be set;
areNum
returns the worldmap entry number for the new area;
An example best illustrates the use of this function.
// add links from these area(s) to the new area ACTION_DEFINE_ASSOCIATIVE_ARRAY toNewArea BEGIN "AR0020" => "Exitwmp" // City Gates "AR0300" => "Exitwmp" // Docks "AR0400" => "Exitwmp" // Slums "AR0500" => "Exitwmp" // Bridge District "AR0700" => "Exitwmp" // Waukeen's Promenade "AR0800" => "Exitwmp" // Graveyard "AR0900" => "Exitwmp" // Temple District "AR1000" => "Exitwmp" // Government "AR1100" => "Exitwmp" // Umar Hills "AR1300" => "Exitwmp" // d'Arnise Keep "AR1304" => "Exitwmp" // d'Arnise Keep "AR2000" => "Exitwmp" // Trademeet "AR1200" => "Exitwmp" // Windspear Hills END // add links from the new area to these area(s) ACTION_DEFINE_ASSOCIATIVE_ARRAY fromNewArea BEGIN "AR0020" => "ExitNE" // City Gates "AR0300" => "ExitN" // Docks "AR0400" => "ExitN" // Slums "AR0500" => "ExitNE" // Bridge District "AR0700" => "ExitE" // Waukeen's Promenade "AR0800" => "ExitS" // Graveyard "AR0900" => "ExitNW" // Temple District "AR1000" => "ExitN" // Government "AR1100" => "ExitSE" // Umar Hills "AR1300" => "ExitSE" // d'Arnise Keep "AR1304" => "ExitSE" // d'Arnise Keep "AR2000" => "ExitSW" // Trademeet "AR1200" => "ExitW" // Windspear Hills END // calls the function and returns the worldmap entry number for the new area LAUNCH_ACTION_FUNCTION sc#addWmpAre INT_VAR mapIcon = 21 // map icon xCoord = 240 // x coordinate yCoord = 130 // y coordinate tTime = 0 // travel time *4, so two means eight hours STR_VAR areName = "YS0390" // area reference, like "AR0700" strName = "Govt. West" // area name, like "Waukeen's Promenade" strDesc = "Govt. West" // area description which will show up when hovering the area on the worldmap END
If you rename WeiDU.EXE to something of the form Setup-MyMod.exe, it will behave as if the following arguments were present:
To distribute your mod, rename WeiDU.EXE to Setup-MyMod.EXE (or whatever), put Setup-MyMod.TP2 file in the same directory and go! Typically mods are distributed a ZIP files or self-extracting archives that put the EXE, TP2 and module data files in the main BGII directory.
If your mod adds new resources to the game (e.g., via COPY) you should be careful to pick a unique name so that you resource will not conflict with one created by another mod. For example (and I speak from personal experience here), naming a creature BOO2.CRE is just asking for trouble. One common approach here is to pick a special prefix that uses a character not found in normal game resources. For example, I might use the prefix W# and call a sword W#SWORD.ITM and a new spell W#SPELL.SPL. However, it it still possible to run into conflicts, so I recommend that you check out the Community Mod Filename Prefix Reservations project at http://forums.blackwyrmlair.net/index.php?showtopic=113 and register your own unique prefix.
The WeiDU.exe (or Setup-MyMod.exe) process will terminate with a “return value” (or “exit code” or “status code” or “error level”) based on the success or failure of certain operations. Depending on your operating system you can use this value to guide shell scripts or batch files that include WeiDU. In general, WeiDU returns 0 on success and non-zero on failure. Here is a concrete list of return values.
A regular expression or regexp is "somewhat" like a DOS wildcard but not quite. The big difference is that if you would say * in DOS you say .* in regexp-land. Here’s a definition:
The syntax for regular expressions is the same as in Gnu Emacs. The special characters are:
$^.*+?[]\
The following constructs are recognized:
. matches any character except newline * (postfix) matches the previous expression zero, one or several times + (postfix) matches the previous expression one or several times ? (postfix) matches the previous expression once or not at all [..] character set; ranges are denoted with -, as in [a-z]; an initial ^, as in [^0-9], complements the set ^ matches at beginning of line $ matches at end of line \| (infix) alternative between two expressions \(..\) grouping and naming of the enclosed expression \1 the text matched by the first \(...\) expression (\2 for the second expression, etc) \b matches word boundaries \ quotes special characters.
So spe.* matches "sper01.itm" and "sper.eff" and "special".
Hopefully this is understandable to most people.
The following keywords can be used in values and evaluate to the given integer (usually an offset in a CRE file).
// KEYWORD VALUE AREA_CITY 0252 AREA_DAY 0260 AREA_DUNGEON 0256 AREA_FOREST 0248 AREA_NIGHT 0264 ATTACK1 0220 ATTACK2 0224 ATTACK3 0228 ATTACK4 0232 BATTLE_CRY1 0200 BATTLE_CRY2 0204 BATTLE_CRY3 0208 BATTLE_CRY4 0212 BATTLE_CRY5 0216 BIO 0x1cc BORED 0196 COMPLIMENT1 0352 COMPLIMENT2 0356 COMPLIMENT3 0360 CRITICAL_HIT 0424 CRITICAL_MISS 0428 DAMAGE 0236 DESC 0x54 DIALOGUE_DEFAULT 0412 DIALOGUE_HOSTILE 0408 DYING 0240 EXISTANCE1 0444 EXISTANCE2 0448 EXISTANCE3 0452 EXISTANCE4 0456 EXISTANCE5 0460 HAPPY 0172 HURT 0244 IDENTIFIED_DESC 0x54 INITIAL_MEETING 0164 INSULT 0340 INTERACTION1 0320 INTERACTION2 0324 INTERACTION3 0328 INTERACTION4 0332 INTERACTION5 0336 INVENTORY_FULL 0436 LEADER 0188 MISCELLANEOUS 0384 MORALE 0168 NAME1 8 NAME2 12 PICKED_POCKET 0440 REACT_TO_DIE_GENERAL 0376 REACT_TO_DIE_SPECIFIC 0380 RESPONSE_TO_COMPLIMENT2 0388 RESPONSE_TO_COMPLIMENT3 0392 RESPONSE_TO_INSULT1 0396 RESPONSE_TO_INSULT2 0400 RESPONSE_TO_INSULT3 0404 SELECT_ACTION1 0292 SELECT_ACTION2 0296 SELECT_ACTION3 0300 SELECT_ACTION4 0304 SELECT_ACTION5 0308 SELECT_ACTION6 0312 SELECT_ACTION7 0316 SELECT_COMMON1 0268 SELECT_COMMON2 0272 SELECT_COMMON3 0276 SELECT_COMMON4 0280 SELECT_COMMON5 0284 SELECT_COMMON6 0288 SELECT_RARE1 0416 SELECT_RARE2 0420 SPECIAL1 0364 SPECIAL2 0368 SPECIAL3 0372 TARGET_IMMUNE 0432 TIRED 0192 UNHAPPY_ANNOYED 0176 UNHAPPY_BREAKING 0184 UNHAPPY_SERIOUS 0180 UNIDENTIFIED_DESC 0080 HIDDEN_IN_SHADOWS 0444 SPELL_DISRUPTED 0448 SET_A_TRAP 0452 STORE_NAME 12 SCRIPT_OVERRIDE 0x248 SCRIPT_CLASS 0x250 SCRIPT_RACE 0x258 SCRIPT_GENERAL 0x260 SCRIPT_DEFAULT 0x268 DEATHVAR 0x280 DIALOG 0x2cc AREA_SCRIPT 0x94 BIT0 0b00000000000000000000000000000001 BIT1 0b00000000000000000000000000000010 <snip> BIT31 0b10000000000000000000000000000000
WNL "\r\n"
// Windows new line
MNL "\r"
// (pre-OSX) Macintosh new line
LNL "\n"
// Linux/Unix/OSX new line
TAB "\t"
// tabulation character
REGISTRY_BG1_PATH
the path of BG1 (as read from the registry),
or empty if not available.
REGISTRY_BG2_PATH
ditto for BG2.
REGISTRY_PST_PATH
ditto for PST.
REGISTRY_IWD1_PATH
ditto for IWD1.
REGISTRY_IWD2_PATH
ditto for IWD2.
USER_DIRECTORY
evaluates to the directory in which user files
are kept. This is the game path on non-EE-type games. On EE-type
games, the USER_DIRECTORY
variable is constructed from two
parts, the user directory and a game-specific directory. If the file
engine.lua exists, WeiDU will attempt to read the game-specific
directory from it. If the engine.lua does not exist or does not
contain the necessary information, WeiDU will use default paths. On
Windows, the user directory is the user’s personal directory, as read
from the Windows registry. On macOS, the user directory is
"$HOME/Documents". On Linux, the user directory is
"$HOME/.local/share". In both cases, $HOME is the user’s home
directory, as read from the pwd database. The default game-specific
path for BG: EE is "Baldur’s Gate - Enhanced Edition"; for BGII: EE it
is "Baldur’s Gate II - Enhanced Edition"; for IWD: EE it is
"Icewind Dale - Enhanced Edition"; and for PST: EE it is
"Planescape Torment - Enhanced Edition".
SAVE_DIRECTORY
evaluates to the directory in which the current
game type stores its saved games. This is
"%USER_DIRECTORY
%/save".
MPSAVE_DIRECTORY
evaluates to the directory in which the
current game type stores its multiplayer saved games. The
determination is done the same way as with the save directory, but for
the directory mpsave instead.
EE_LANGUAGE
evaluates to the Enhanced Edition language
directory the user has selected, for example, en_US
. This
variable is undefined on non-EE games.
This section briefly explains some common file formats. The definitive reference is http://gibberlings3.net/iesdp/.
The source code to WeiDU is available under the GNU General Public License, as detailed in the file COPYING. If for some reason you are unable to obtain a copy of the GPL, merely announce that fact on some public forum and your mailbox will be full of copies of it for life. It’s a great way to meet new people.
Since this is the world of Windows, I distribute a pre-compiled binary. WeiDU is written in OCaml, a functional programming language that includes automatic memory management, higher-order functions and efficient native code generation. If these terms mean nothing to you, you probably won’t be able to modify the source code.
Building WeiDU on Windows requires OCaml 4.01 or more recent, Cygwin and the elkhound program. The OCaml installer can install the required Cygwin packages.
WeiDU can also be built on x86/x86-64 GNU/Linux and PPC OS X. The exact status on building WeiDU on x86/x86-64 OS X is not known, but there have been reports of success.
WeiDU uses a Make system that aims to correctly auto-configure itself, through the file Configuration.
WeiDU uses the GPL’d zlib and xdiff libraries, as well as portions from the GPL’d ocaml, batteries included library.
Elkhound is available as source code under a BSD licence and as pre-compiled x86 executables for Windows and GNU/Linux (built on Debian Squeeze).
For more information, please refer to the forum.
Weimer’s thank you: I would like to thank the fine folks at the Infinity Engine File Format Hacking Project, without which this would not have been possible: http://gibberlings3.net/iesdp/.
In addition, I make heavy use of Near Infinity for general IE mods: http://www.idi.ntnu.no/~joh/ni/.
The Infinity Engine Editor Pro was very good at changing ITMs and SPLs, but DLTCEP is now a better choice: http://gibberlings3.net/tools/dltcep.php.
Kudos to my main out-of-house developer:
Special thanks to my main bug-finders *cough* I mean beta-testers:
Special thanks to Greg Henry for being the first person to mention WeiDU to me in a face-to-face conversation. I was quite impressed. Jason Compton is the first person to mention WeiDU to me in a telephone conversation. Scott McPeak is the first person to discuss my modding hobby with me in a face-to-face conversation (and he was quite well-informed).
The Bigg would additionally like to thank the following contributors:
and additionally
Wisp would like to extend further thanks to the following people:
and also The Bigg, for having developed and maintained BiggDU for many years and, while doing so, having significantly improved upon its workings and functionality.
WeiDU was originally made by Westley Weimer. It was subsequently significantly improved upon in accordance with the GPL terms by Valerio Bigiani, AKA The Bigg, starting from version 186 (which was a closed beta, while the first public release was the 187 one). Starting from version 232 (version 231.06 to be more precise), maintenance and development was assumed by Fredrik Lindgren, a.k.a Wisp.
What this means is that all praise and credits should go to Weimer and Bigiani, but all bug reports and feature requests should go to Fredrik Lindgren (a.k.a Wisp).
Since WeiDU was released under the GPL, with an added notice that you are also allowed to distribute unmodified binaries, I assume the same holds true for mods distributed with this version of WeiDU. However, if you feel like being nice add a line in your readme stating that the current WeiDU may be downloaded from <insert link>. Current authorized mirrors include (but may not be limited to) all those from IEGMC, the Dragon’s Hoard Download Centre, and the SHS Download Centre.
Currently undocumented:
|
|
See the file README-WeiDU-Changes.txt for a description of how WeiDU has changed over time.
This document was translated from LATEX by HEVEA.