WeiDU Documentation

Westley Weimer
weimer@cs.berkeley.edu




Table of Contents
1 About WeiDU

The main home page for WeiDU is: https://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.

2 Don't Panic!

Step-By-Step Beginner's Guide to WeiDU:
  1. Don't Panic. Many of you are children of the GUI era. But programs that run from the command line can be your friend, and in the long run are often much faster and, yes, easier to use.

  2. The Best Way To Learn How To Write Code In WeiDU's D Format Is To Read Code Written In WeiDU's D Format. Start by decompiling existing in-game DLGs that you understand and read through them. Compare how they appear in WeiDU to how they appear in other BG2 editing tools you may be more comfortable with, such as Near Infinity or Infinity Explorer.

  3. The Best Way To Learn How To Write Code In WeiDU's D Format Is To Read Code Written In WeiDU's D Format, Part 2. A growing number of BG2 add-on packs are being created using WeiDU. A list is available at http://www.pocketplane.net/modlist/. These can help you understand how WeiDU's advanced features, such as dialogue appending, script and 2DA patching, and item/spell/creature patching work in a ``real-world'' setting. Make it a point to download some of them and understand HOW they work.

  4. Take a look at some of the examples in this document. There is a lovely WeiDU tutorial (written by Japheth) available at http://forums.pocketplane.net/index.php?topic=55.0. If you are feeling overwhelmed, start there first. It also covers installation. Ghreyfain also has a ``how to create an NPC with WeiDU'' tutorial at http://forums.pocketplane.net/index.php?topic=52.0.

  5. There is a WeiDU discussion board at http://forums.pocketplane.net/index.php?board=50.0. The discussion board is the best place to have your WeiDU (and mod-making) questions answered.

  6. Finally, if you are using a Mac and you want to play around with WeiDU, check out https://weidu.org/Mac.html for more information on obtaining a copy of WeiDU that works under OS X.
3 D and DLG File Concepts

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
END
Dialogues 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
  END
represents 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 EXIT
Replies 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.1
This 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 1
This 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 label
During 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.

4 D Dialogue File Format

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 overwriten. 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 filename state list END This tells WeiDU to place the given states at the end of the already-existing dialogue filename.DLG.
or APPEND_EARLY 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 ] 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.
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 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 for more information about this idea.
or INTERJECT_COPY_TRANS2 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). We are expecting more documentation on this feature in the future.
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 ] 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 ] 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 This instructs WeiDU to add the transActionString to 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. The transActionString is prepended to any existing action text on a per-transition, per-state basis.
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 instructcs 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 sayString This instructs WeiDU to destructively change the sayString 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 ] 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 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 Just like REPLACE_TRIGGER_TEXT but the filename is a regexp. The .DLG is implied. Do not use this.
or REPLACE_ACTION_TEXT filename oldText newText [ moreFilenames ] 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 ] Just like REPLACE_ACTION_TEXT but the filenames are regexps. The .DLG is implied, do not include it in your regexps. Do not use this.
or REPLACE_ACTION_TEXT_PROCESS filename oldText newText [ moreFilenames ] This instructs WeiDU to destruveily 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 REPLACE_ACTION_TEXT_PROCESS_REGEXP filenameRegexp oldText newText [ moreFilenameRegexps ] Just like REPLACE_ACTION_TEXT_PROCESS, but the filenames are regexps. The .DLG is implied. Do not use this.
 
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 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).
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 evaluted 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 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 55
 
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. Do not use this!
 
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 filename stateLabel The dialogue continues at the state with label stateLabel in the file filename.DLG.
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 [ == 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.
 
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 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 !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 feature.
 
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 consequtive 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".

5 Command Line Options

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.


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 BG2 or PST or IWD 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 promtp for a key to be pressed at the end of TP2 processing.
--ask-every Behave as if ASK_EVERY_COMPONENT were present for all TP2 components.
--continue Continue TP2 processing despite TP2 Action errors.
--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.
 
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.
--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.
--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
--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-biff, --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 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.
--ftlkout X If any strings were added to or changed in the loaded DIALOGF.TLK, emit X as an updated version that reflects those changes.
 
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.
--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 preceeded by --biff-get. 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. Do not even think of using this feature without backing up CHITIN.KEY.
--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.
--extract-kits X Extract all kits starting with kit #X and create TP2 actions to install those kits as part of a module.
 
Resouce Exploration Options
--list-eff X List effects in resource X. See --out.
F.ITM or F.EFF or F.SPL Equivalent to --list-eff F.EXT.
 
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).

6 Example Uses
7 WeiDU Tutorials

This section includes tutorials on specific parts of WeiDU. Many of them were contributed by users like you.

7.1 Multisay
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
END 
This 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
END
This 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 almsot 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#KLSYJ
However, 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.

7.2 CHAIN
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)~
EXIT 
Note 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)~
EXIT
In 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.

7.3 COPY_TRANS

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
END
This 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
END 
Instead 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
END
This 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 inHell1
If 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.

7.4 INTERJECT
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
END
This 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
END
Minsc 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 1
Here'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 1
Note that we repeat the
IF ~IsValidForPartyDialog("Minsc")~ THEN
check 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).

7.5 INTERJECT_COPY_TRANS
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?~
END
So, 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.

7.6 INTERJECT_COPY_TRANS2
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.

7.7 state WEIGHTs

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~ END
If 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~ END
With 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
END
to 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 
END
Since 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.

7.8 TRA Translation Files

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
END 
Note 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


7.9 Converting a ``hard-coded'' D to a D/TRA pair

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
END 
into fwki-new.d's
APPEND J#KLSYJ
  IF ~~ THEN BEGIN KelseySAHPR4
    SAY @0
    IF ~~ THEN EXTERN SAHPR2 10
  END
END 
and 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# 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# argument to specify the starting number (rather than 0) for new @x entries.

C:\Program Files\Black Isle\BGII - SoA\> weidu --traify fwki.d --traify# 1000 --out fwki-1000.d


will create new @x references in fwki-1000.tra that begin at @1000, instead of @0. --traify# may also be used on TP2 files.

Finally, note that --traify works for BAF files as well.

7.10 REPLACE_ACTION_TEXT
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.

8 Module Packaging: TP2 Files

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
AUTHOR emailAddress
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. The AUTHOR directive gives an email address for users to send bugs to if there are problems during the installation. The TP2!AUTHOR variable is set to the ``emailAddress'' value. 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.
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 just before any normal TP2 Action in the file is installed.
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), IWD2, and PST. See the Scripting Styles tutorial.
 
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 "Traducción al Español" 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 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). Do not use this flag.
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.
 
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. 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, DEST_DIRECTORY, DEST_FILESPEC, DEST_FILE, and DEST_RES 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 and SOURCE_RES is bigboss. 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 ... patch list when list 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 toDir ... patch list when list Behaves like COPY_EXISTING except that fromFileRegexp may contain regexp regular exprsesions. All matching files in the game BIFFs will be copied to the directory specified by toDir.

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_RANDOM ( file1 list ) [ ( fileN list ) list ] patch list when list This command works like COPY_EXISTING but the destination for any given souce 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 COMPILE sourceFile 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.

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 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 APPEND filename newText when list 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.
or APPEND_COL filename newText 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. If filename was:
A B C
D E F
X Y Z
P Q R
and 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 3
You must have the same number of whitespace-separated words in newText as there are columns in filename.
or EXTEND_TOP existingBCS newFile patch 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 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 As EXTEND_TOP, but the newFile file is put at the bottom of the every BCS file that matches the regexp existingBCSregexp.
or EXTEND_BOTTOM_REGEXP existingBCSregexp newFile patch list See EXTEND_TOP_REGEXP.
or ACTION_IF value THEN BEGIN TP2 Action list END [ ELSE BEGIN TP2 Actoin 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 AT_EXIT commandToRun Whenever this component attemps 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.
  • If commandToRun consists of a single TP2 filename, WeiDU will enqueue that TP2 file and run it when the current one is done (asking the user all the standard questions about languages and which components to install).
  • If commandToRun consists of the word VIEW followed by a file, a system-specific viewer will be used to present the file to the user. For example, on Windows systems notepad will be used to view txt files and a web browser will be used to view html files.
  • Otherwise, commandToRun is executed by the underlying operating system (and is thus system dependant). If you want to do something that WeiDU doesn't handle, like extracting WAVs from an MP3, make a batch file and run it from here.
In any case, slashes and backslashes will be converted appropriately for the underlying operating system. The most common use 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 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 As AT_EXIT, but when this component is removed, commandToRun is executed.
or AT_INTERACTIVE_UNINSTALL commandToRun 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 UNINSTALL modToUninstall modComponent If the given component of the given mod is currently installed, uninstall it before proceeding. Do not use this action. 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 ADD_KIT internalKitName manyComplexArguments This command allows you to add new kits to the BGII. See the example file mymod.tp2 or the tutorial at http://forums.gibberlings3.net/index.php?showtopic=584 for information on how to do this.
or ADD_MUSIC internalMusicName newMUSFile No documentation yet!
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 STRING_SET indexOrString newValue list [ USING traFile ] This command replaces each given string in the user's TLK file with the associated newValue. Do not use this command. If a traFile is given, that file is is read once before all of the replacements take place and its contents are forgotten after. Do not use this command.
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. Do not use this command.
or REQUIRE_FILE filename warningString If filename does not exist, 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, 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 execution, warningString is displayed and the component fails to 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 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 <<<<<<<< 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 criticially 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).
 
optNoBackup   A COPY 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 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 will not be undone if the mod is uninstalled. Do not use this feature.
 
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   Do not use local filesystem globbing. This is the default.
or GLOB Use local filesystem globbing. Globbing is generally architecture specific! Do not use globbing if you can help it.
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
  
 
patch   A patch tells WeiDU how to modify a file.
is SAY offset String The string-ref associated with String is written at offset. This is commonly used to change the name or description of a spell or item.
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 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%~
  
Do not use this feature.
or 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. This works somewhat like sprintf() but it not as cool. Currently this is the only way to assign a string value to a variable.
or SNPRINT value variable stringWithVars As SPRINT, but only the first N characters are stored in the variable, where N is the result of evaluating the value. This works somewhat like snprintf(). Thus:
  SPRINT "author" "Jason"
  SNPRINT 3 "myvar" "1:%author%"
  
... assigns 1:J to myvar.
or REPLACE regexp text All occurences 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 regexp string All occurences 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.
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:
<<<<<<<< .../script.baf
IF
  See(%myvar%)
THEN
  RESPONSE #100
    Kill(%myvar%)
END
>>>>>>>>
EXTEND_TOP ~sola.bcs~ ~.../script.baf~ 
  SPRINT myvar = ~"Anomen"~
  EVALUATE_BUFFER
Those 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.
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.
or WRITE_SHORT offset value The first argument is the offset at which the second argument (a 16-bit short value) is written.
or WRITE_LONG offset value The first argument is the offset at which the second argument (a 32-bit long word value) is written.
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_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.
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 filename Just like INSERT_FILE except that the entire contents of ``filename'' are appended to the end of the current file.
or REPLACE_BCS_BLOCK oldFile newFile If the current file is a BCS file, the segment of it corresponding to oldFile is replaced with the contents of newFile. oldFile and newFile may be BCS or BAF files. If they are BAF files they will not get the benefit of AUTO_TRA.
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 signed 32-bit value is read from the file at the given offset and is stored in the given variable. See READ_BYTE.
or READ_ASCII offset variable [ ELSE string ] [ ( sizeValue ) ] 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. If an explicit sizeValue is specified than that many bytes are read into the variable, even of some of them are nuls. 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 DIALOG.TLK string reference is read from the file at the give offset. The string reference is looked up in DIALOG.TLK and the (male) string value for it (without any quotes) is stored in the variable. In some sense this is the opposite of SAY_EVALUATED.
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_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 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 tutorail for more information.
or PATCH_RANDOM_SEED value See RANDOM_SEED.
or ADD_STORE_ITEM [ + ] itemName numCharges ext2 ext3 itemFlag maxInStack [ unlimited ] See the ADD_STORE_ITEM tutorial for more information.
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 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 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 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~ 77777
  
Both foo and bar are CLERIC_BLESS while baz is 777777.
or COMPILE_BAF_TO_BCS 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 The current file, which must be a valid BCS script, is decompiled to a BAF.
or DECOMPILE_DLG_TO_D 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 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 REPLACE_EVALUATE 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 substitute is performed on replaceRegexp, and then findRegexp is replaced by replaceRegexp. Any writes dones 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).
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 spell
Special thanks to Japh for coding this feature.
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_KNOWN ~sppr101~ ~sppr102~
Special thanks to Japh for coding this feature.
or ADD_CRE_ITEM itmName #charge1 #charge2 #charge3 flags slot [ EQUIP ] [ TWOHANDED ] See the ADD_CRE_ITEM tutorial.
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_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.
 
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.
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.
 
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 ** 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 exponentation. 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 value != value Integer Disequality. 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 expressione valuates to 0 if and only if its two string arguments are equal (have the same length and the same contents). 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.
or String STRING_COMPARE_CASE String As STRING_COMPARE, but the comparison ignores case. That is, "ANOMEN" and "aNoMeN" are considered equal.
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'').
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 FILE_CONTAINS fileName regexp Evaluates to 1 if the file fileName contains the regular expression regexp and 0 otherwise. Case is ignored.
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. Example:
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.
is FILE_EXISTS filename Evaluates to 1 if the file exists in the filesystem and has non-zero size 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_SIZE fileName fileSize Evaluates to 1 if the size of the file fileName is exactly fileSize. Evaluates to 0 otherwise.
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 %WEIDU_ARCH% The special variable WEIDU_ARCH is set to either "x86" or "mac" at WeiDU startup and can be used to determine the underlying system architecture.
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 underlying operating system.
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.

9 WeiDU TP2 Tutorials

9.1 COPY_EXISTING_REGEXP
This tutorial was thoughtfully provided by Japheth.

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/sw1h01.itm~
              ~sw1h02.itm~ ~override/sw1h02.itm~
              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~
  WRITE_LONG 0x88 "6"
  WRITE_LONG 0x8a "1" 
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 because of that, the offsets of 0x88 and 0x8a won't work because they don't exist in those items.

To find which offset to WRITE_LONG at I opened up Near Infinity, (found at http://www.idi.ntnu.no/~joh/ni/) and looked up the offset with it. Fortunately for us, the offset is the same in all items, so we can make the changes in one shot. WRITE_LONG 0x88 "6" is telling WeiDU to write the value of 6 at offset 88 hex. Similarly, WRITE_LONG 0x8a "1" is telling WeiDU to write the value 1 at offset 8a hex.

The 6 is the dice size and the 1 is the number of dice.

9.2 EXTEND_TOP_REGEXP
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 he 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, infact, 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.

9.3 READ_BYTE and IF_EVAL
This tutorial was thoughtfully provided by Japheth.

READ_BYTE, READ_SHORT and READ_LONG can be potentially powerful functions when used in conjunction with IF_EVAL.

Here's a brief demonstration.

The scenario: I want to make all longswords in the game do 1D6 damage, rather than their normal 1D8. 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 item category offset into a variable. We'll use the variable name "type".
READ_BYTE "0x1c" "type"
I found the offset by looking at an item file in Near Infinity. The variable "type" can really be anything, it's just what I've chosen.

Now that we're done reading that into a variable, we can now WRITE_SHORT the values we want to give our new longswords. Namely, 6 for damage and 1 for dice size. (Or, 1D6 for simplicity sake.)
WRITE_SHORT 0x88 6
WRITE_SHORT 0x8a 1
0x88 is the offset for dicesize and 0x8a is the offset for the number of dice. Again, I found these offsets using Near Infinity beforehand.

Finally, we tell WeiDU that we only want to do this if the item file is infact a longsword.
IF_EVAL ("%type%" = "0x14")
So, to call back our variable, you just wrap it with % signs. 0x14 is just the value for longsword. If you look at the Category offset in Near Infinity (which is 0x1c), and scroll down in the box below, you'll see Longsword (20). The 20 just represents the decimal value. 0x14 is just 20 in hexadecimal.

So, to loop through the logic again, this is what we're saying:

If the category is longsword, make the dice size 6 and the dice damage 1. If it's not a longsword, ignore it and don't copy it over.

And that's it. Now all items with the category Longsword will have 1D6 as their dice damage. 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_BYTE     "0x1c" "type"

  WRITE_SHORT   0x88 6
  WRITE_SHORT   0x8a 1

  IF_EVAL       ("%type%" = "0x14")
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 NI 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 death variable, dialogues, scripts, etc. Note, that if you're WRITE_ASCIIing to say, the death variable, and the previous death variable entry is longer than the one you're writing, then you'll have spillover after WRITE_ASCII. To avoid this, embed a null character at the end of your string. So, if I have
WRITE_ASCII 0x280 ~Guy~
I would want to put a null character right after the y in Guy. You can do this by using a hex editor and inserting 00 after the y.

These are by no means "hard and fast" rules, but generally they ring true. However, there could be times when you need to WRITE_LONG twice if the field is 8 bytes long, etc.

And that, as they say, is that.

9.4 values and expressions
This tutorial was thoughtfully provided by Japheth.

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 IF_EVAL statement.)

Anyways, on to the example. I'll use acolyte1.cre as my example creature.

The first thing we want to do is copy the creature over so it's ready for patching. That's easy to do using a simple COPY_EXISTING statement:
COPY_EXISTING ~acolyte1.cre~ ~override/acolyte1.cre~
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" "#items"
"itemsoffset", "itemslot" and "#items" 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_SHORT ("%itemslot%" + 0x04) "shield"
Note: To access your variables after they've been read, you always have to wrap them in percent signs.

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) "%#items%"
Now we have to update the itemslot offset value to reflect the fact that we've added 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 #items 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 "%#items%" so this is dead easy:
WRITE_LONG 0x2c0 ("%#items%" + 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%" + "%#items%" * 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%" + "%#items%" * 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%" + ("%#items%" * 0x14) + 0x10) 1
Finally, we want to tell WeiDU only to do this if the shield slot is *empty*. This is where IF_EVAL 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 doesn't read unsigned bytes, so if the value is indeed -1, it will come out as 65535 as the value. So, this is the statement we use to tell WeiDU only to execute all of the above code if "%shield%" is -1:
IF_EVAL ("%shield%" = "65535")
Here's the full code:
COPY_EXISTING ~acolyte1.cre~ ~override/acolyte1.cre~
READ_LONG "0x2bc" "itemsoffset"
READ_LONG "0x2b8" "itemslot"
READ_LONG "0x2c0" "#items"
READ_SHORT ("%itemslot%" + 0x04) "shield"

WRITE_SHORT ("%itemslot%" + 0x04) "%#items%"
WRITE_LONG 0x2b8 ("%itemslot%" + 0x14)
WRITE_LONG 0x2c0 ("%#items%" + 1)

INSERT_BYTES ("%itemsoffset%" + "%#items%" * 0x14) 0x14

WRITE_ASCII ("%itemsoffset%" + "%#items%" * 0x14) ~shld01~

IF_EVAL ("%shield%" = "65535")
Note that you can use the PRINT action in a tp2 to debug your code as well. Like this:
COPY_EXISTING ~acolyte1.cre~ ~override/acolyte1.cre~
  READ_LONG "0x2bc" "itemsoffset"
  READ_LONG "0x2b8" "itemslot"
  READ_LONG "0x2c0" "#items"
  READ_SHORT ("%itemslot%" + 0x04) "shield"

  WRITE_SHORT ("%itemslot%" + 0x04) "%#items%"
  WRITE_LONG 0x2b8 ("%itemslot%" + 0x14)
  WRITE_LONG 0x2c0 ("%#items%" + 1)

  INSERT_BYTES ("%itemsoffset%" + "%#items%" * 0x14) 0x14

  WRITE_ASCII ("%itemsoffset%" + "%#items%" * 0x14) ~shld01~

  IF_EVAL ("%shield%" = "65535")

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:
  1. Change acolyte1.cre to the actual creature that you want to modify
  2. You'll have to change ("%itemslot%" + 0x04) to something else if you are adding anything else but a shield. You can figure this out by subtracting the slot offset of the slot you want to modify from the item slots offset.
  3. You would need to change  shld01  to the item you are going to add.
  4. You'd have to add your own flags if they need adding (i.e. Identified, Not Stealable, etc.).
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.

9.5 ADD_STORE_ITEM
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.
  1. I could add the item to the store beforehand and then simply copy over the new store file upon installing my mod.

  2. I could use WeiDU's READ_BYTE and WRITE_BYTE patch expressions to patch the item into the store while installing.
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/ribald.sto~
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. Here are the flags that you can use:
  IDENTIFIED
  UNSTEALABLE
  STOLEN
  IDENTIFIED&STOLEN
  IDENTIFIED&UNSTEALABLE
#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/ribald.sto~
  ADD_STORE_ITEM ~myitem~ #10 #0 #0 ~IDENTIFIED~ #5

  COPY ~mymod/myitem.itm~ ~override/myitem.itm~
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~
That's about all there is to it.

9.6 ADD_GAME_NPC
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_GAME_NPC comes in handy for all you BG1 modders out there.

By using ADD_GAME_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_GAME_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/mynpc.cre~
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_GAME_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/mynpc.cre~
SAY NAME1 ~Japh~
SAY NAME2 ~Japh~
So, after doing all that, we would issue ADD_GAME_NPC with the following arguments:
ADD_GAME_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/mynpc.cre~
SAY NAME1 ~Japh~
SAY NAME2 ~Japh~
// Do any other patching stuff here
ADD_GAME_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.

9.7 SET_2DA_ENTRY

This tutorial was thoughtfully provided by Idobek.

There have been some queiries 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.

9.8 WHILE Loops

This tutorial was thoughtfully provided by Idobek.

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:
COPY_EXISTING_REGEXP ~.*\.itm~ ~override~
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 IF_EVAL to tell WeiDU to only patch axes:
IF_EVAL ("%category%" = 25)
Now, we need to find out how many abilities there are and where they are located:
READ_LONG 0x64 "abilitiesoffset"
READ_SHORT 0x68 "#abilities"
So far, so good. Here's where the fun begins. We want to patch examine every ability in the item. We need to use a WHILE loop to patch the abilities one by one. We open the loop like so:
WHILE ("%#abilities%" > 0) BEGIN
For the WHILE loop to progress and close we need to modify the "#abilities" variable. We use the SET command to do this:
SET "#abilities" = ("%#abilities%" - 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 "#abilities" variable by one each time. Once the "#abilities" 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%" + ("%#abilities%" - 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 another WHILE loop to do this and we are going to need a method to tell the loop to stop patching. So the first thing we need to do is define a new variable.
SET "patchmelee" = 0
Having done this we can open a new loop:
WHILE ("%abilitytype%" = 1)
  AND ("%patchmelee%" = 0) BEGIN
So we are patching when the ability type is 1 (melee) and when our melee patching variable is 0. Time for the actual patch:
WRITE_SHORT ("%abilitiesoffset%" + ("%#abilities%" - 1) * 0x38 + 0x16) 6
WRITE_SHORT ("%abilitiesoffset%" + ("%#abilities%" - 1) * 0x38 + 0x18) 2
Time to tell this loop to stop. This is simply a case of setting our melee patching variable to 1.
SET "patchmelee" = 1
END
We use exactly the same method for ranged abilities:

SET "patchranged" = 0
WHILE ("%abilitytype%" = 2)
  AND ("%patchranged%" = 0) BEGIN
   WRITE_SHORT ("%abilitiesoffset%" + ("%#abilities%" - 1) * 0x38 + 0x16) 12
   WRITE_SHORT ("%abilitiesoffset%" + ("%#abilities%" - 1) * 0x38 + 0x18) 1
SET "patchranged" = 1
END
We can now close the main loop (using the aforementioned method):
SET "#abilities" = ("%#abilities%" - 1)
END
We are done with our patches so we can add our IF_EVAL statement here.
IF_EVAL ("%category%" = 25)
The full code is:
COPY_EXISTING_REGEXP ~.*\.itm~ ~override~
   READ_BYTE 0x1c "category"
   READ_LONG 0x64 "abilitiesoffset"
   READ_SHORT 0x68 "#abilities"
   WHILE ("%#abilities%" > 0) BEGIN
      READ_BYTE ("%abilitiesoffset%" + ("%#abilities%" - 1) * 0x38) "abilitytype"
         SET "patchmelee" = 0
         WHILE ("%abilitytype%" = 1)
           AND ("%patchmelee%" = 0) BEGIN
            WRITE_SHORT ("%abilitiesoffset%" + ("%#abilities%" - 1) * 0x38 + 0x16) 6
            WRITE_SHORT ("%abilitiesoffset%" + ("%#abilities%" - 1) * 0x38 + 0x18) 2
         SET "patchmelee" = 1
         END
         SET "patchranged" = 0
         WHILE ("%abilitytype%" = 2)
           AND ("%patchranged%" = 0) BEGIN
            WRITE_SHORT ("%abilitiesoffset%" + ("%#abilities%" - 1) * 0x38 + 0x16) 12
            WRITE_SHORT ("%abilitiesoffset%" + ("%#abilities%" - 1) * 0x38 + 0x18) 1
         SET "patchranged" = 1
         END
   SET "#abilities" = ("%#abilities%" - 1)
   END
   IF_EVAL ("%category%" = 25)
So what have we done? Let's list the steps involved:
  1. First off we copied all the item files ready for patching.
  2. Then we the read in the item category.
  3. We also read in the number of abilities and the file location of those abilities.
  4. We open a WHILE loop based upon the number of abilities.
  5. Within the WHILE loop, we read in the ability type.
  6. Next we defined a new variable to indicate that we had not yet patched any melee type abilities.
  7. We opened another WHILE loop based upon ability type and our patch melee variable.
  8. We patched melee abilities to do 2D6 damage.
  9. We closed this WHILE loop by indicating the the melee ability had been patched.
  10. We repeated steps 6-9 to give the ranged abilities 1D12 damage.
  11. We closed our main WHILE loop by reducing the number of abilities by 1 and indicating that it should stop if the number of abilities reached 0.
  12. We used an IF_EVAL statement to ensure the patch is only applied to axes.
It is important to note that this method has assumed that the item only has one (or none) ranged ability and one (or none) melee ability. If an item has two melee abilites (or two ranged abilities) this code will only patch one of them.

That's the end of this tutorial.

9.9 Bitwise Operators

This tutorial was thoughtfully provided by CamDawg.

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:
  1. Searches through all item files in the game
  2. Reads selected data from an item (type, usability, etc)
  3. Alters specific data on an item without changing anything else
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 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
  READ_BYTE    "0x1E" "bard" //reads the byte containing bard usability flag
  READ_BYTE    "0x20" "mage" //reads the byte containing the mage usability flag
  READ_SHORT    "0x1C" "type" //reads the byte containing item type
  WRITE_BYTE    "0x1E" ("%bard%" BAND "0b10111111")  // makes usable by bards
  IF_EVAL (("%mage%" BAND "0b00000100") = "0b00000000") // if it is usable by mages
  IF_EVAL
        ("%type%" = "67") OR  // and if it is a robe
        ("%type%" = "2")        // or armor
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
  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
  WRITE_BYTE    "0x21" ("%race%" BOR "0b00010101")  // makes unusable by dwarves, halflings, and gnomes
  IF_EVAL
        ("%prof%" = "93") OR  // two-handed sword
        ("%prof%" = "99")        // or halberd
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 proficeiency 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.

9.10 Scripting Styles

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 FACTION.IDS is present, the scripting style is autodetected as PST. Otherwise the scripting style is set to BG. 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.

9.11 TP2 SUBCOMPONENT Groups

This tutorial was thoughtfully provided by CamDawg.

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]
[Y]es, [N]o, or [Q]uit 

Install Component [Delainy Portrait 2 by Fred]
[Y]es, [N]o, or [Q]uit 

Install Component [Delainy Portrait 3 by Wilhelmus]
[Y]es, [N]o, 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.

9.12 ADD_CRE_ITEM
This tutorial was thoughtfully provided by Japheth.

This command is a little bit more complex. First I'll list all the valid flags and inventory slots.

Flags: Inventory Slots: Now here is the syntax:
COPY_EXISTING ~alynar.cre~ ~override/alynar.cre~
  ADD_CRE_ITEM ~itemname~ #charge1 #charge2 #charge3 ~Flags~ ~Inventory Slots~ [EQUIP] [TWOHANDED]
EQUIP and TWOHANDED are optional and are only needed when dealing with weapons. 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/alynar.cre~
  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/alynar.cre~
  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/alynar.cre~
  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/alynar.cre~
  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.

9.13 Prompt Customization
This tutorial was thoughtfully provided by Rastor.

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 https://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.

Upon loading, weidu always looks to see if a prompts.tra file exists in the folder that corresponds to the language that the user selects. If so, then weidu automatically loads the file and uses it to display your strings instead of the ones that are normally coded into it. In order to change what the setup-mymod.exe file displays to the user, you need to edit this file and then place it into the relevant language folder. Your translators should then translate this into whatever language and include it in that language folder.

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 carriage return (or ``new line'') character. It simply indicates that you want any text following it to be inserted below the text that precedes the carriage return.

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 [Y]es or [N]o 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 carriage return. 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.

9.14 INNER_ACTION

Normally I would start off with don't 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 bouns +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.

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.

10 Module Distribution: Setup-MyMod.exe

If you rename WeiDU.EXE to something of the form Setup-MyMod.exe, it will behave as if the following arguments were present:
10.1 Module Distribution Conventions

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.

10.2 WeiDU Return Values

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.
11 Regular Expressions

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. 
   '      interpret the characters inside '' literally
   "      interpret the characters inside "" literally
So spe.* matches "sper01.itm" and "sper.eff" and "special".

Hopefully this is understandable to most people.

12 WeiDU constants

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  
13 Common File Formats

This section briefly explains some common file formats. The definitive reference is http://iesdp.gibberlings3.net/.
14 The Source Code
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 function 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.

However, if you do want to modify the source and then recompile WeiDU, it's quite easy. Make sure that you have OCaml 3.06 (or newer), sed (or Perl 5.6), make and either gcc or (optionally) cl (the Microsoft Visual C Compiler).

Edit Makefile and pick your configuration. If you're not x86/windows/cygwin or x86/linux, you'll have to do some tweaking. Then just type make clean and then make. Presto, you've recompiled it.

WeiDU uses the GPL'd zlib and xdiff libraries.

15 Special Thanks
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://iesdp.gibberlings3.net/.

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://www.dragonlancetc.com/forums/index.php?board=9.

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).

16 Undocumented Features

Currently undocumented:
17 Index of Terms

Index
  • **, 8, 8

  • --append, 5
  • --ask-every, 5
  • --autolog, 5
  • --automate, 5
  • --automate-min, 5
  • --backup, 5
  • --bcmp-from, 5
  • --bcmp-orig, 5
  • --bcmp-patch, 5
  • --bcmp-to, 5
  • --biff, 5
  • --biff-get, 5
  • --biff-get-rest, 5
  • --biff-name, 5
  • --biff-str, 5
  • --biff-type, 5
  • --biff-value, 5
  • --biff-value-at, 5
  • --cmp-from, 5
  • --cmp-to, 5
  • --continue, 5
  • --dcmp-from, 5
  • --dcmp-to, 5
  • --debug-assign, 5
  • --debug-value, 5
  • --extract-kits, 5
  • --forceify, 5
  • --ftlkin, 5
  • --ftlkout, 5
  • --full-from, 5
  • --game, 5
  • --list-biffs, 5
  • --list-eff, 5
  • --list-files, 5
  • --log, 5
  • --logapp, 5
  • --make-biff, 5, 6
  • --make-tlk, 5
  • --max, 5
  • --min, 5
  • --noautoupdate, 5
  • --nocom, 5
  • --nofrom, 5
  • --nogame, 5
  • --noheader, 5
  • --noselfupdatemsg, 5
  • --out, 5
  • --reinstall, 5
  • --remove-biff, 5, 6
  • --script-style, 5
  • --search, 5
  • --strapp, 5
  • --strfind, 5
  • --string, 5
  • --tcmp-from, 5
  • --tcmp-to, 5
  • --testtrans, 5
  • --text, 5
  • --tlkcmp-from, 5
  • --tlkcmp-to, 5
  • --tlkcmp-use-strings, 5
  • --tlkin, 5
  • --tlkmerge, 5
  • --tlkout, 5
  • --toplevel, 5
  • --traify, 5
  • --traify-tlk, 5
  • --trans, 5
  • --transin, 5
  • --transitive, 6, 6
  • --transref, 5
  • --uninstall, 5
  • --update-all, 5
  • --yes, 5

  • 2DA, 13

  • <<, 8

  • >>, 8

  • ?, 8

  • ACTION_IF, 8
  • ADD_CRE_ITEM, 9.12
  • ADD_GAME_NPC, 9.6
  • ADD_KIT, 8
  • ADD_KNOWN_SPELL, 8
  • ADD_MAP_NOTE, 8
  • ADD_MUSIC, 8
  • ADD_PROJECTILE, 8
  • ADD_STATE_TRIGGER, 4
  • ADD_STORE_ITEM, 9.5
  • ADD_TRANS_ACTION, 4
  • ADD_TRANS_TRIGGER, 4
  • ALLOW_MISSING, 8
  • ALWAYS, 8
  • AND, 8
  • APPEND, 4
  • APPEND_COL, 8
  • APPEND_EARLY, 4
  • APPEND_FILE, 8
  • APPENDI, 4
  • APPLY_BCS_PATCH, 8
  • APPLY_BCS_PATCH_OR_COPY, 8
  • ASK_EVERY_COMPONENT, 8
  • AT_EXIT, 8
  • AT_INTERACTIVE_EXIT, 8
  • AT_INTERACTIVE_UNINSTALL, 8
  • AT_UNINSTALL, 8
  • AUTHOR, 8
  • AUTO_TRA, 8

  • BACKUP, 8
  • BAND, 8
  • BASR, 8
  • BCS, 13
  • BEGIN, 4
  • BIFF, 13
  • BLSL, 8
  • BLSR, 8
  • BNOT, 8
  • BOR, 8
  • BUT_ONLY_IF_IT_CHANGES, 8
  • BXOR, 8

  • CHAIN, 4
  • CHAIN2, 4
  • COMPILE, 8
  • COMPILE_BAF_TO_BCS, 8
  • COMPILE_D_TO_DLG, 8
  • COPY, 8
  • COPY_EXISTING, 8
  • COPY_EXISTING_REGEXP, 8
  • COPY_RANDOM, 8
  • COPY_TRANS, 4
  • COUNT_2DA_ROWS, 8
  • Component, 8
  • Component Flag, 8
  • chainEpilogue, 4
  • chainText, 4
  • constant, 12

  • D Action, 4
  • D File, 4
  • DECOMPILE_BCS_TO_BAF, 8
  • DECOMPILE_DLG_TO_D, 8
  • DELETE_BYTES, 8
  • DEPRECATED, 8
  • DESIGNATED, 8
  • DEST_DIRECTORY, 8
  • DEST_FILE, 8
  • DEST_FILESPEC, 8
  • DEST_RES, 8
  • DLG, 3
  • DO, 4
  • EFF, 13
  • EVALUATE_BUFFER, 8
  • EXIT, 4
  • EXTEND_BOTTOM, 4
  • EXTEND_BOTTOM_REGEXP, 8
  • EXTEND_TOP, 4
  • EXTEND_TOP_REGEXP, 8
  • EXTERN, 4

  • FAIL, 8
  • FILE_CONTAINS, 8
  • FILE_CONTAINS_EVALUATED, 8
  • FILE_EXISTS, 8
  • FILE_EXISTS_IN_GAME, 8
  • FILE_MD5, 8
  • FILE_SIZE, 8
  • FLAGS, 4
  • FOR, 8
  • FORBID_COMPONENT, 8
  • FORBID_FILE, 8
  • Forced String Reference, 4

  • GLOB, 8
  • GOTO, 4

  • IF_SIZE_IS, 8
  • INNER_ACTION, 9.14
  • INNER_PATCH, 8
  • INNER_PATCH_FILE, 8
  • INSERT_BYTES, 8
  • INSERT_FILE, 8
  • INSTALL_BY_DEFAULT, 8
  • INTERJECT, 4
  • INTERJECT_COPY_TRANS, 7.5
  • INTERJECT_COPY_TRANS2, 7.6
  • ITM, 13
  • inlined, 8

  • JOURNAL, 4

  • KEY, 13

  • LANGUAGE, 8
  • Language, 8
  • LOOKUP_IDS_SYMBOL_OF_INT, 8

  • MKDIR, 8
  • Module Distribution, 10

  • NO_LOG_RECORD, 8
  • nonPausing, 4

  • OR, 8
  • optGlob, 8
  • optNoBackup, 8

  • PATCH_IF, 8
  • PATCH_RANDOM_SEED, 8
  • PRINT, 8
  • Prompt Customization, 9.13
  • patch, 8
  • prompts.tra, 9.13

  • RANDOM, 8
  • RANDOM_SEED, 8
  • READ_2DA_ENTRY, 8
  • READ_ASCII, 8
  • READ_BYTE, 8
  • READ_LONG, 8
  • READ_SBYTE, 8
  • READ_SHORT, 8
  • READ_SSHORT, 8
  • READ_STRREF, 8
  • REMOVE_KNOWN_SPELL, 8
  • REPLACE, 4
  • REPLACE_ACTION_TEXT, 4
  • REPLACE_ACTION_TEXT_PROCESS, 4
  • REPLACE_ACTION_TEXT_PROCESS_REGEXP, 4
  • REPLACE_ACTION_TEXT_REGEXP, 4
  • REPLACE_BCS_BLOCK, 8
  • REPLACE_EVALUATE, 8
  • REPLACE_SAY, 4
  • REPLACE_STATE_TRIGGER, 4
  • REPLACE_TEXTUALLY, 8
  • REPLACE_TRIGGER_TEXT, 4
  • REPLACE_TRIGGER_TEXT_REGEXP, 4
  • REPLY, 4
  • REQUIRE_COMPONENT, 8
  • REQUIRE_FILE, 8
  • REQUIRE_PREDICATE, 8
  • regexp, 11
  • replyText, 4, 4

  • SAY, 4
  • SAY_EVALUATED, 8
  • SCRIPT_STYLE, 8
  • SET, 8
  • SET_2DA_ENTRY, 9.7
  • SET_WEIGHT, 4
  • SNPRINT, 8
  • SOLVED_JOURNAL, 4
  • SOURCE_DIRECTORY, 8
  • SOURCE_FILE, 8
  • SOURCE_FILESPEC, 8
  • SOURCE_RES, 8
  • SOURCE_SIZE, 8
  • SPL, 13
  • SPRINT, 8
  • STR_CMP, 8
  • STRING_COMPARE, 8
  • STRING_COMPARE_CASE, 8
  • STRING_COMPARE_REGEXP, 8
  • STRING_CONTAINS_REGEXP, 8
  • STRING_MATCHES_REGEXP, 8
  • STRING_SET, 8
  • STRING_SET_RANGE, 8
  • String, 4
  • SUBCOMPONENT, 9.11
  • sayText, 4
  • state, 4
  • stateActionString, 4
  • stateLabel, 4
  • stateNumber, 4
  • stateTriggerString, 4

  • TLK, 13
  • TP2, 8
    • AUTHOR, 8
  • TP2 Action, 8
  • TP2 File, 8
  • TP2 Flag, 8
  • TRA, 7.8
  • text, 4
  • transFeature, 4
  • transition, 4
  • transNext, 4
  • transTriggerString, 4

  • UNINSTALL, 8
  • UNLESS, 8
  • UNSOLVED_JOURNAL, 4
  • USING, 8

  • value, 8
  • variable, 8

  • WEIDU_ARCH, 8
  • WEIDU_OS, 8
  • WEIGHT, 4
  • WHILE, 8
  • WRITE_ASCII, 8
  • WRITE_BYTE, 8
  • WRITE_EVALUATED_ASCII, 8
  • WRITE_FILE, 8
  • WRITE_LONG, 8
  • WRITE_SHORT, 8
  • when, 8

18 Changes
See the file https://weidu.org/WeiDU/README-WeiDU-Changes.txt for a description of how WeiDU has changed over time.
This document was translated from LATEX by HEVEA.