AddKit() requires a kit-class match in order to remove the old kit - otherwise it removes the default CLAB file for the current class(es).
At this stage:
ChangeClass(Myself,BARD)
AddKit(BLADE)
It's going to remove the effects in CLABBA01, because the current bard "kit" is "trueclass" - if you level up, that is the CLAB file that will be applied.
It works this way because of Multi/Dual classes - it needs to know which CLAB file to apply/remove for each class, and uses the trueclass CLAB file for any class without a matching kit.
Only script actions work:
AddKit(KIT) // Remove existing kit CLAB features and apply new kits CLAB level by level.
AddSuperKit(KIT) // Retain existing kit CLAB features and apply new kits CLAB level by level.
ChangeStat(Myself,KIT,#,ADD/SET) // Retain existing kit CLAB features, does not apply new kits CLAB.
for a moment I had an idea about implementing 3rd ed multiclass system in EE engine using AddKit() / AddSuperKit(). Storing each class level in a local variable or EEex stat (either of them assigned via custom level-up GUI screen coded in Lua) and implementing class specific features after each level using AddKit, with a help of ChangeClass (just to be able to use AddKit) and ChangeStat (to temporary assign LEVEL based on local variable value, so that correct CLAB columns are used). This would make the system highly customizable and easy to work with, but the idea fell flat due to problems with CLAB cleaning.
@kjeron, I assume there is no way to add multiple kits via AddKit and later remove those kits CLAB features one by one, right? I've experimented with changing classes before AddKit usage and created dummy kits with empty CLABS for each class, but even with this I can't make the AddKit CLAB cleaning work reliably for multiple kits. Too bad there is no RemoveKit action.
yep, the implementation that I came up with above after reading the quoted kjeron's post was more like a cosmetic/usability improvement, not something strictly needed to implement 3rd style multiclassing system. With EEex that allows setting and reading local variables from within GUI, as well as adjusting the buttons available for the class on the fly, the only thing crucial for a robust system without awkward workarounds is the arcane spells, divine spells, and thief abilities availability (and from what I read @Bubb has been experimenting with it too).
I need to make a slight tweak to the WEAPPROF.2da. All I want to do is let Archers get Grandmastery in slings and darts, so I need to change a 2 to a 5 on lines 30 and 31.
Hi I'm trying to figure out if I should use "Replace" or "Replace_Say"
I have a unique .DLG I created for an Innkeeper's rumors. I've set up all the logic for the states and responses, so I want to keep that intact. I have 10 states that use string 33999, because those are the states I want to put unique/new text into.
The WEIDU readme says that REPLACE will replace the whole state, but I want to keep the logic I set up. It also says that REPLACE_SAY will "destructively" replace the text. What does "destructively" mean, exactly?
I hope somebody can help me out with the following:
I've created a new 'display portrait icon' for a new ability (spell) that I'd like to add to STATDESC.2DA.
Now, I can just do this - that adds the BAM file (ULTRA4D.BAM) I created to an empty row.
This is all fine but I just can't figure out how to add a description string to it.
I would basically need to add a new string to dialog.tlk, and then use its reference number (XX) to add it to STATDESC.2DA like this:
SET_2DA_ENTRY 188 1 2 ~XX~
I don't know how to do this properly so it would be compatible with both BG1 and BG2 since the reference number would need to be different. How do I do this? @subtledoctor
I think I'll try to do this in a simple list/recipe style. We'll see how it goes. The example was, how to apply an effect like extra spells based on INT score. But, understand this: opcode 326 does no handle dynamic effects very well. You simply can't do something like DEX-based thac0 bonuses that change whenever your DEX score changes, the way the vanilla DEX AC bonuses work. Sorry, too bad, so sad. I have tried and tried and tried to make a 'Finesse' effect, and it has never worked to my satisfaction. So I suggest you think in terms of permanent effects. Instead of INT-based spells, I'll deal with something simpler that I'm working on right now: extra proficiencies at level 1 for high INT.
1. First, make your effect spells. Opcode 326 triggers a spell, so you need to make a .spl file. Let's put aside the details of how this spell works - it uses my new feat system and it's based on the idea of reducing your initial proficiency cap from 2 pips to 1 pip, and then letting you get back up to 2 pips, in certain weapons, only if you are smart enough. So smart warriors will have an advantage over dumb ones. I'll cover the feat system thing in a future post, so for now let's just say this: we'll make a spell that uses opcode 233 to increment proficiency in long swords by one point. Put this .spl file somewhere in your mod folder and write a COPY line in your .tp2 file to copy it into the game. Let's call it "d5LSprof.spl."
COPY ~IntMod/files/d5LSprof.spl~ ~override~
2. Next, get your stat ready to be analyzed. Opcode 326 reads SPLPROT.2da. That file already has a line for "INT>=[value]" so if we only want to make a single effect for an INT score greater than or equal to 12, say, that would be easy. But what if we want a different effect for each score - zero bonuses at INT=12, and six bonuses for INT=18? The "greater-than-or-equal" relation doesn't work well here, we just want a simple "=" relation. So we'll have to add our own.
I add a new line with four columns: a simple identifier (prefaced with my modding prefix, for safety), then "38" in the STAT cloumn (the line # in STATS.ids for INT), then "-1" in the VALUE column (-1 means, read the value from parameter1 of your 326 effect), then "1" in the RELATION column (1 means "equal" - read more about the RELATION values here). I separate them by the Weidu variable %TAB% so that they make nice neat columns in the .2da file.
Now I have a "INT=" line in SPLPROT.2da, so I am ready to give INT-based bonuses, right? Not so fast. I have my "INT=" line in the .2da file, but I have no way, in advance of the mod installation, to make a spell with a 326 effect that can reference that line. So I have to build it in Weidu.
3. Now, make your opcode 326 spell with dummy values. I make a spell with multiple effects. Each of these effects uses opcode 326. In the "parameter2" field (labeled "creature type" in Near Infinity) I'll choose a pre-existing one... let's use that "STAT INT >= value" that I mentioned earlier. We will change it later. But this lets me put a value into the parameter1 field (labeled "creature value"). (Remember, our new SPLPROT.2da entry has "-1" for VALUE so it will get the information from whet we put here.) I'll put a different INT score in parameter1 in each 326 effect. If I want a bonus for every point above 12, then I'll make six effects and populate parameter1 with 13, 14, 15, 16, 17, and 18. (or you could make 13 effects, and go all the way up to 25. The point is, since we're looking at a "=" relation, we need a separate 326 effect for each score.
Put this .spl file into your mod folder - let's call it "d5intspl.spl" - and add a COPY line to your .tp2 file.
COPY ~IntMod/files/d5intspl.spl~ ~override~
4. Now you need to know how to find the line you APPENDed to SPLPROT.2da. I don't know what this file looks like; each player might have used many other mods that might have changed the file. So I have to find the line I added to it, at install-time. This is kind of the point of this thread: here is some code that you can adapt pretty easily to your own mod:
COPY_EXISTING ~splprot.2da~ ~override~ COUNT_2DA_COLS cols // amount of columns READ_2DA_ENTRIES_NOW rows cols // read all file into memory FOR (row = 1; row < rows; ++row) BEGIN // iterate over rows READ_2DA_ENTRY_FORMER rows row 0 ~stat~ // read column value PATCH_IF ~%stat%~ STRING_EQUAL_CASE ~D5_INT_EQ~ BEGIN SET int_eq_row = %row% END END BUT_ONLY
This reads every entry in the first column ("column 0"), in every row in SPLPROT.2da, assigning what it reads there to the variable %stat%. If and when %stat% matches the string "D5_INT_EQ" (that's the first entry we APPENDed up in step 2) then it sets that row number into the variable %int_eq_row% for later use. Put this before the COPY commands so that the variable is ready to be used by them.
5. Now insert that variable into your 326 spell. Add this under the COPY line for the 326 spell:
LPF ALTER_EFFECT INT_VAR match_opcode = 326 parameter2 = %int_eq_row% END
Remember I originally used a dummy value in parameter2 of the 326 spell, well now, at install-time, I am changing it to refer to the new row of SPLPROT.2da that I added.
6. Finally, let's add this to the Trueclass fighter CLAB table, to give them a bit of a boost over their superior kits:
COPY_EXISTING ~splprot.2da~ ~override~ COUNT_2DA_COLS cols // amount of columns READ_2DA_ENTRIES_NOW rows cols // read all file into memory FOR (row = 1; row < rows; ++row) BEGIN // iterate over rows READ_2DA_ENTRY_FORMER rows row 0 ~stat~ // read column value PATCH_IF ~%stat%~ STRING_EQUAL_CASE ~D5_INT_EQ~ BEGIN SET int_eq_row = %row% END END BUT_ONLY
You'll need to make the spell files yourself, but this can be adapted pretty easily to all sort of stuff. You can check any stat in STATS.ids, so ability scores, proficiency levels, saving throws, thief skills, spell effects, etc. You would only need to change that "38" to some other value in the APPEND line for SPLPROT.2da.
Okay that's it for now. Hopefully someday in the future I'll talk about how to make these effects dynamic (it involves what I call "auras," i.e. repeating self-cancelling effects).
Next up: HOW TO GIVE KIT EFFECTS TO ALL KITS (including mod-added kits)
Okay. Now we have applied a spell to a trueclass fighter that gives him or her some bonus based on INT score. What if we want this to apply to ALL fighters, of any kit? Not just the four vanilla kits, but all fighter kits from any source? The tricky thing is that we cannot control how each player mods their game. Some might stick with the four vanilla fighter kits but others might install tons of kits. How do we cover all bases?
We do that with the help of an associative array. My current method for this involving using arrays in a way that "they are not really meant to be used," according to one elder statesman of the modding scene. But it works! So to heck with it. Let's begin.
1. First, you make an array containing every kit and linking it to its base class. We will populate the array with information from kitlist.2da.
COPY_EXISTING ~kitlist.2da~ ~override~ COUNT_2DA_COLS cols COUNT_2DA_ROWS cols rows FOR ( row = 1 ; row < rows ; row = row + 1 ) BEGIN READ_2DA_ENTRY %row% 5 10 clab READ_2DA_ENTRY %row% 8 10 class DEFINE_ASSOCIATIVE_ARRAY d5_kit_clabs BEGIN "%clab%" => "%class%" END END BUT_ONLY
This creates an array in memory that consists of a simple list of the clab table of every kit in kitlist.2da, linked to that kit's class number. (Mage = 1, fighter = 2, cleric = 3, thief = 4, paladin = 6, ranger = 12, etc.) The array is called d5_kit_clabs.
I never totally understood how arrays work, especially because it is not something that you can seen in front of your eyes. But all you need to know is what I just said: we have now linked each kit's clab table with its underlying class, in a way that Weidu can read and act upon, using ACTION_PHP_EACH.
2. Read and act upon the array using ACTION_PHP_EACH. Now the task is rather simple: tell Weidu to do something to every clab table that belongs to a fighter kit. First, we need to tell Weidu to read the array. (We are doing this as a fresh command, so it will be an ACTION operation; but you can also use PATCH_PHP_EACH if you want to do stuff in the middle of a PATCH operation.)
ACTION_PHP_EACH d5_kit_clabs AS yoo => hoo BEGIN
"Yoo" and "hoo" are meaningless; they are just terms to help Weidu read the array. Remember above when we did DEFINE_ASSOCIATIVE_ARRAY we defined it as "clab => class." So now, "Yoo" equals clab, and "hoo" equals class. Now we will have Weidu do a command, but only for kits in the fighter class:
ACTION_IF (%hoo% = 2) AND (FILE_EXISTS_IN_GAME ~%yoo%.2da~) BEGIN
The FILE_EXISTS check is just for safety's sake, to make sure the clab file listed in kitlist.2da hasn't disappeared somehow. If it has, this operation will gracefully skip over it and keep going.
This is simply the patch we did above to the trueclass fighter clab table; only now instead of patching just the one table, I am patching every table that equates to the %yoo%.2da variable - i.e. every fighter kit.
Well, every fighter kit EXCEPT the trueclass - trueclass kits aren't listed in kitlist.2da. So we will keep the original APPEND line as well. Now we can put all that together, and we can combine it with the 326 code I posted above:
COPY_EXISTING ~splprot.2da~ ~override~ COUNT_2DA_COLS cols // amount of columns READ_2DA_ENTRIES_NOW rows cols // read all file into memory FOR (row = 1; row < rows; ++row) BEGIN // iterate over rows READ_2DA_ENTRY_FORMER rows row 0 ~stat~ // read column value PATCH_IF ~%stat%~ STRING_EQUAL_CASE ~D5_INT_EQ~ BEGIN SET int_eq_row = %row% END END BUT_ONLY
Again, you can pull out this bit of code (everything from the COPY_EXISTING ~kitlist and below) and adapt it for all sorts of uses. I use this to give my "Quickstride" increased movement rate ability to all rangers. The sky is the limit.
BUT: install order matters. Like I said, this will patch every fighter kit's clab table, including those added by other mods... but ONLY if this is installed after those other mods. (Naturally, those mod kits can only be patched if they exists in your game at the time this patch is installed.) So this wants to be installed late in the install order. But so many mods want to be installed late in the order! How to deal with it?
My simple rule of thumb for install order, is this: - Ascension - quests - NPCs - items - spells - kits - tweaks - SCS
This sort of thing - patching abilities or effects into kits - is very much a tweak, so your mod should advertise it as such. Just tell players that it must be installed after all other mods that add kits to the game. Six or twelve different mods can do this sort of thing, and as long as they are all installed after all kit mods, they should work fine together.
Finally, PLEASE NOTE: there are better ways to do this. I mean, all of the info we put in the array (clab table and associated class number) is already right there in kitlist.2da, already associated with each other. We could forget the array and instead adapt the method from the post just above this one: read through each row of kitlist, noting the value in column 8 (class), check for a match against "2," and then note the row number, then read the value in column 5 in that row (clab table), then perform some action on that .2da file (using INNER_ACTION). Those skilled with Weidu will find a way to do this elegantly. But for enthusiastic amateurs like me, here's the important thing: the array method works, reliably. Variations of that code appear probably 30 or 40 times in my mods. So if you don't want to mess around with the code, but just want something to drop into your mod so you can focus on the creative decisions, this is for you.
Let's say you are making a kit mod, and you want to target your kit with opcode 177 "use .eff" effects, or opcode 326 "apply effects list" effects. Here's an example:
Say you make a sorcerer kit, and the theme is that it channels magical energy better than most sorcerers. Call it a "Channeler." Of course you will have read the excellent tutorial on creating KIT mods, so you know the first line of your ADD_KIT routine will look like this:
ADD_KIT ~D5_CHANNELER~
That name is how the engine recognizes your kit... sometimes. Other times, it refers to the kit's value in the KIT.IDS file.
Say you want to add a special disadvantage, that every time a Channeler is struck by Magic Missile, they take an extra 2d4 magic damage. Because magic just flows through them too easily. The way to do this is to add an effect to the Magic Missile, using opcode 177 and targeting your kit, and making an .eff file that applies the damage. Opcode 177 uses the kit's value in KIT.IDS. For vanilla kits this is easy, they are listed in KIT.IDS and you can look up their value there and plug it into the 177 effect.
But mod-added kits are added to KIT.IDS sequentially, and you have no idea how many kit mods a player might install, or in what order. So how do you add that 177 effect to Magic Missile?
Well, each kit is listed in KITLIST.2DA, with its name and .2da clab table and description strings, etc. And in the EEs there is an extra column that contains the kit's IDS value. So with this file we can link the kit's name (which you will know, because you named it) to the kit's IDS value (which you will never know ahead of time. The following code will read the contents of that .2da file, find the line with your kit's name, and spit out the associated IDS value as a variable that you can use elsewhere.
COPY_EXISTING ~kit.ids~ ~override~ COUNT_2DA_COLS cols READ_2DA_ENTRIES_NOW rows cols FOR (row = 1; row < rows; ++row) BEGIN READ_2DA_ENTRY_FORMER rows row 1 ~this_kit~ PATCH_IF ~%this_kit%~ STRING_EQUAL_CASE ~D5_CHANNELER~ BEGIN SET this_row = %row% READ_2DA_ENTRY_FORMER rows this_row 0 channeler_code END END BUT_ONLY
That is a bit hard to digest; I find the READ_2DA_FORMER/NOW stuff difficult to understand. But the nice thing is, there is no need to actually understand this. You can copy that and drop it directly into your own .tp2 code. The only thing you need to worry about is where it says: "...STRING_EQUAL_CASE ~D5_CHANNELER~..." J just put your own kit's name between those two tildes: ~...~. The IDS value for your kit will be encoded into the %channeler_code% variable at the end there. (You can rename that variable if you like.)
Now, armed with that variable, we can edit Magic Missile:
(Where "D5_MDMG.EFF" is the name of the .eff file you created that does the extra magic damage.)
You could also edit items to have unique characteristics with your kit, using ADD_ITEM_EFFECT or ADD_ITEM_EQEFFECT. Maybe the Staff of Power's thunderbolt ability does extra damage when used by a Channneler. Maybe a fleet-of-foot thief kit gets 2 extra APR when using Belm, instead of just one. The sky is the limit; you just need to know your kit's IDS value in order to make effects like that, and this code gives you that.
HOW TO ALTER CLASS/KIT DESCRIPTIONS AND NOT WORRY ABOUT STRING REFERENCES
Okay, let's say you're into tweak mods. Like mine. I have a mod that slightly changes the Swashbuckler: it takes traps away (I can't really see Swashbucklers patiently laying traps) but gives them a (reduced) backstab multiplier. Then I add a different kit, the Scout, which has traps but no backstabs like the vanilla Swashbuckler.
So, I want to change the Swashbuckler's description in-game so that the player will read and understand the kit's new abilities and restrictions. But where is the text I have to change? I could search for it and find the string reference with Near Infinity... but that's a pain. Plus there are many different games with different versions of dialog.tlk (TOB, BGT, BGEE, SoD, BG2EE, IWDEE, EET) and some of those have doubles or even triples of the kit description text. Dealing with all those variations is a minor nightmare. So what do we do?
Ignore the string reference. Here's how:
The strings for all kit names and descriptions are listed in KITLIST.2DA in columns 3, 4, and 5 (lower-case name, mixed-case name, and description, respectively). If your game has the files, the kit descriptions and the class descriptions are also in CLASTEXT.2DA and SODCLTXT.2DA, in columns 3, 4, and 5 (lower, description, and mixed, respectively - note the different order!). So to find and alter the strings, we can read those files. Here's example code that is extremely easy to adapt to your mod:
ACTION_IF (FILE_EXISTS_IN_GAME ~clastext.2da~) BEGIN COPY_EXISTING ~clastext.2da~ ~override~ COUNT_2DA_COLS cols READ_2DA_ENTRIES_NOW rows cols FOR (row = 1; row < rows; ++row) BEGIN READ_2DA_ENTRY_FORMER rows row 0 ~kit_name~ PATCH_IF ~%kit_name%~ STRING_EQUAL_CASE ~SWASHBUCKLER~ BEGIN SET patch_row = %row% END END SET_2DA_ENTRY %patch_row% 3 cols RESOLVE_STR_REF (@20001) SET_2DA_ENTRY %patch_row% 4 cols RESOLVE_STR_REF (@20003) SET_2DA_ENTRY %patch_row% 5 cols RESOLVE_STR_REF (@20002) BUT_ONLY END ACTION_IF (FILE_EXISTS_IN_GAME ~sodcltxt.2da~) BEGIN COPY_EXISTING ~sodcltxt.2da~ ~override~ COUNT_2DA_COLS cols READ_2DA_ENTRIES_NOW rows cols FOR (row = 1; row < rows; ++row) BEGIN READ_2DA_ENTRY_FORMER rows row 0 ~kit_name~ PATCH_IF ~%kit_name%~ STRING_EQUAL_CASE ~SWASHBUCKLER~ BEGIN SET patch_row = %row% END END SET_2DA_ENTRY %patch_row% 3 cols RESOLVE_STR_REF (@20001) SET_2DA_ENTRY %patch_row% 4 cols RESOLVE_STR_REF (@20003) SET_2DA_ENTRY %patch_row% 5 cols RESOLVE_STR_REF (@20002) BUT_ONLY END ACTION_IF (FILE_EXISTS_IN_GAME ~kitlist.2da~) BEGIN COPY_EXISTING ~kitlist.2da~ ~override~ COUNT_2DA_COLS cols READ_2DA_ENTRIES_NOW rows cols FOR (row = 1; row < rows; ++row) BEGIN READ_2DA_ENTRY_FORMER rows row 1 ~kit_name~ PATCH_IF ~%kit_name%~ STRING_EQUAL_CASE ~SWASHBUCKLER~ BEGIN SET patch_row = %row% END END SET_2DA_ENTRY %patch_row% 2 cols RESOLVE_STR_REF (@20001) SET_2DA_ENTRY %patch_row% 3 cols RESOLVE_STR_REF (@20002) SET_2DA_ENTRY %patch_row% 4 cols RESOLVE_STR_REF (@20003) BUT_ONLY END
You can copy and paste this into your mod, all you have to change is the kit name in the "PATCH_IF ~%kit_name%~ STRING_EQUAL_CASE" lines, and change the @_____ tra references to your own strings. Just, remember that the order is different between CLASTEXT and KITLIST!
If you want to get more advanced you can do more complicated things. Instead of using SET_2DA ENTRY to replace the whole string with a new one, you could READ_2DA_ENTRY to get the string reference into a variable, and then edit the original string with a more fine-grained method.
The key thing here, though, is that the above code works on every version of the games, at least going back to BG2/TOB. Players will be able to install your mod on SoD, BG2EE, or IWDEE and your code doesn't even have to know which game is being played.
@BCaesar and @agb1 may in particular be interested in this.
The part I don't understand is, "change the @_____ tra references to your own strings"
So I need to create a .tra file and put my new class description text in there? What is a .tra file and how do I create one? Sorry for the really basic questions.
No worries! First, I don't believe you need to use a .tra file... you can just put a string in quotes/tildes/delimiters there, so it could look like this (I think):
SET_2DA_ENTRY %patch_row% 5 cols RESOLVE_STR_REF (~Your text here!~)
I've been using .tra files for a while... it's just a text file, like the .tp2. It has a list of "@" references like this:
See, connected to each "@number" is a string that looks just like you have in your mod. You move all the strings out to en external document and put matching "@number" references into your .tp2 file. This way, if you want to add support for other languages to your mod, a translator only has to translate the .tra file, put the translated version into a different language directory, and your .tp2 file can use it automatically, by adding something like this in the front:
LANGUAGE ~English~ ~english~ ~scales_of_balance/language/en_US/setup.tra~ LANGUAGE ~Polski (Translation by etamin)~ ~polish~ ~scales_of_balance/language/en_US/setup.tra~ ~scales_of_balance/language/pl_PL/setup.tra~
Otherwise, to get a translation of the mod, you would have to make a translated duplicate of the whole .tp2 file. And then what if you want to change something later? You would have to meticulously keep all versions of the .tp2 file in sync... it would be a nightmare.
Also while I'm asking questions, if I have a mod that is only for Baldur's Gate 1:EE version 2.0 and later without SoD, so I am fine with editing a single string of dialog.tlk how to I do that?
Not sure exactly what you mean by this. If you know a particular string reference (but remember, they are different in each game!), you can replace that string using SET_STRING.
Also what's the easiest way to edit a single line of a file? For example with my monk mod I change HPCLASS.2DA and change only monks to HBBARB and leave the rest intact. I could create a whole new file with Near Infinity (that's what I'm doing now) but then that creates compatibility issues with other mods. I'd prefer just to edit one line and I'm assuming there's an easy code to do that in my TP2 file in WeiDU and then I wouldn't have to overwrite the whole HPCLASS.2DA file.
This depends. For any given file, there may or may not be a good way to do something like this. But specifically for .2da files, which are always sort of arranged like a spreadsheet, Weidu has nice tools to analyze and edit them. Some of which I've used in the posts above.
For your edit to HPCLASS.2DA, it is a simple matter: you wan to use SET_2DA_ENTRY:
That will change the entries for Monk, Sun Soul, and Dark Moon. (Which are in the 44th, 45th, and 46th rows of that file that have at least two colunms. That explains the 1st and 3rd numbers in those lines. The 2nd number, "1," is slightly trickier: it tells Weidu to edit the entry in the second column... because for whatever reason, in this instance, it starts counting at zero.)
@BCaesar There's no shorter way to deal with weapprof.2da. One of my mods affects every class and kit, it has like 800 lines of SET_2DA_ENTRY's. It's insane.
The #3 number is the "minimum column count." So when you say row # whatever, if that row has fewer columns than the number in #3, it will be ignored. WHat this really means is that there are only two things you should put here: 1, or the actual column count of the 2da table. For a file like weapprof.2da, just put 1. The only issue this causes is that some rows, like the very first one with some garbage text, get counted in your row count - because that garbage is considered a column. All this means is that your row number might be different. But that doesn't matter because you just put whatever row number works.
If you use 1 in #3 with weapprof, I believe row 11 is bastard sword, and row 34 is dual-wielding, and everything else you could possibly want to change is between those two rows. (I think those are the numbers you would use in #1... easy enough to double-check.)
I'm having trouble figuring out how to give effects to creatures in WeiDU. For example, I want to give the level 6 Minsc file "MINSC6" a minimum damage effect (opcode 250) and set the first parameter to 20. What would be the code for that?
But be aware, negative values do not work in all of these functions. I *think* it works in this one, but it does not work in ALTER_EFFECT or CLONE_EFFECT.
Hey there, I've been creating an NPC mod for personally use and I am on the last bit of it. But I keep running into the problem of the .TP2 file. All the mod making guides I've read have been vague on this aspect, and so I'm wondering since it allows the Weidu to script, what the heck do I put in it?
What do you have in it so far? It would be easier to answer with a bit of knowledge.
To give a really general example for an NPC Mod without addressing any tips for compatibility with EET or such (Especially since this is for personal use anyway), they all start like:
BACKUP: ~Where the backup folder is created. Generally something like ~ModFolderName/Backup~
AUTHOR: ~Your name here. Or Website. Or some people put the support file name. But this is personal use so...your name here.~
VERSION ~1.0, or whatever version number you are using to help keep track of what change/version your mod is at.~
AUTO_TRA ~%MOD_FOLDER%/tra/%s~ // Sets the directory of the language files.
LANGUAGE: Sets the language. Generally it will look like:
~English~
~English~
~%MOD_FOLDER%/tra/english/setup.tra~ , or ~French~ ~French~, ~Directory to set up file~ etc, etc.
BEGIN ~Mod name, can be anything. This is just a display string~
Let me know if I"m on the right track in regards to what you are looking for in an answer, then I'll spell out the rest of things.
After adding your suggestions in, this is what I got. I tried to mirror what the Branwen NPC tutorial showed me on it's .TP2 file prior to this, but due to the fact I don't have audio as well as other features, it fell flat in what I could mimic in that case.
My mod has a Backup folder, a Character folder, a Dialogue folder, an English folder, and a Scripts folder. I hope that can more or less shed some light on what I'm working with.
Okay, now that I know I'm on the right track, I'll make a full guide.
Your LANGUAGE: line isn't parsing because it should be LANGUAGE with no colon.
After Begin, it should start looking like this:
// Adds custom IsValidForPartyDialogue state used throughout
APPEND ~STATE.IDS~ // adds custom IsValidForPartyDialogue state
~0x80101FEF CD_STATE_NOTVALID~
UNLESS ~CD_STATE_NOTVALID~
IF and ONLY IF you use CD_STATE_NOTVALID, you can go ahead and include the above. Else it's wasted lines of copying. For BG:EE and BG2:EE, IsValidForPartyDialogue("Creature") works just fine and is much better than the group use of InParty("Creature") InMyArea("Creature") !StateCheck("Creature",CD_STATE_NOTVALID) that was standard use in older mods for interject. You only need it if you want to be compatible with older BG2 and BGT.
Next you can start copying the creature and lines over, this is a snippet, but since you've seen Branwen, you probably know what the full group is supposed to look like. Make sure to have an instance for ToB and/or SoD if you're using them in those expansions and want them to appear for people who are just starting those games out and not importing a save.
This will set up your Joined party, banter lines, and post party lines. The D after X3Emi (X3Emi is my character's creature code) is any lines that run after the rest party is clicked, a pre-rest talk with the protagonist.
If you have custom portraits, just include their path in a COPY command.
A small note while we are at creatures, non-joinable creatures don't need the lengthy set up, just these few lines will do.
Then you just set up the creature/dialogue/script files. Note for area files you use the EXTEND_TOP or EXTEND_BOTTOM. I tend to use EXTEND_TOP if it's noticeable that an NPC will just suddenly "appear" because its taking the game so long to get to the end of the area script, or there's a script preventing mine from firing. Otherwise EXTEND_BOTTOM works just fine.
Then make sure dialogue/script files start with COMPILE EVALUATE BUFFER
Let me know if that helps. If you get any other errors, just copy screenshots here again and I can help you.
This very much helps, thank you. But now I'm getting the cre error I was getting beforehand, despite the fact I labeled everything accordingly as shown. Unless there's something I'm missing or just not seeing.
EDIT: Actually, found and fixed that problem. But hold a moment.
Yeah, now something in the scripting is acting up. Don't exactly know what, but I have just about the full script showing in the screenshot in case it can be caught.
Hard to tell to be honest. It could be a pathing or directory issue, and I don't think it actually is an issue with the script. Attach the script file here and I can know for sure. Is this from GoG, Beamdog, or Steam?
Comments
At this stage: It's going to remove the effects in CLABBA01, because the current bard "kit" is "trueclass" - if you level up, that is the CLAB file that will be applied.
It works this way because of Multi/Dual classes - it needs to know which CLAB file to apply/remove for each class, and uses the trueclass CLAB file for any class without a matching kit.
for a moment I had an idea about implementing 3rd ed multiclass system in EE engine using AddKit() / AddSuperKit(). Storing each class level in a local variable or EEex stat (either of them assigned via custom level-up GUI screen coded in Lua) and implementing class specific features after each level using AddKit, with a help of ChangeClass (just to be able to use AddKit) and ChangeStat (to temporary assign LEVEL based on local variable value, so that correct CLAB columns are used). This would make the system highly customizable and easy to work with, but the idea fell flat due to problems with CLAB cleaning.
@kjeron, I assume there is no way to add multiple kits via AddKit and later remove those kits CLAB features one by one, right? I've experimented with changing classes before AddKit usage and created dummy kits with empty CLABS for each class, but even with this I can't make the AddKit CLAB cleaning work reliably for multiple kits. Too bad there is no RemoveKit action.
Where are you guys discussing this stuff and why I'm not part of these conversations? :P
is equivalent to
In other words, does ACTION_DEFINE_ASSOCIATIVE_ARRAY action accept variables?
I have a unique .DLG I created for an Innkeeper's rumors. I've set up all the logic for the states and responses, so I want to keep that intact. I have 10 states that use string 33999, because those are the states I want to put unique/new text into.
The WEIDU readme says that REPLACE will replace the whole state, but I want to keep the logic I set up. It also says that REPLACE_SAY will "destructively" replace the text. What does "destructively" mean, exactly?
What command should I use in my TP2 file?
I hope somebody can help me out with the following:
I've created a new 'display portrait icon' for a new ability (spell) that I'd like to add to STATDESC.2DA.
Now, I can just do this - that adds the BAM file (ULTRA4D.BAM) I created to an empty row.
COPY_EXISTING ~STATDESC.2DA~ ~override~
SET_2DA_ENTRY 188 2 2 ~ULTRA4D~
This is all fine but I just can't figure out how to add a description string to it.
I would basically need to add a new string to dialog.tlk, and then use its reference number (XX) to add it to STATDESC.2DA like this:
SET_2DA_ENTRY 188 1 2 ~XX~
I don't know how to do this properly so it would be compatible with both BG1 and BG2 since the reference number would need to be different. How do I do this? @subtledoctor
I think I'll try to do this in a simple list/recipe style. We'll see how it goes. The example was, how to apply an effect like extra spells based on INT score. But, understand this: opcode 326 does no handle dynamic effects very well. You simply can't do something like DEX-based thac0 bonuses that change whenever your DEX score changes, the way the vanilla DEX AC bonuses work. Sorry, too bad, so sad. I have tried and tried and tried to make a 'Finesse' effect, and it has never worked to my satisfaction. So I suggest you think in terms of permanent effects. Instead of INT-based spells, I'll deal with something simpler that I'm working on right now: extra proficiencies at level 1 for high INT.
1. First, make your effect spells. Opcode 326 triggers a spell, so you need to make a .spl file. Let's put aside the details of how this spell works - it uses my new feat system and it's based on the idea of reducing your initial proficiency cap from 2 pips to 1 pip, and then letting you get back up to 2 pips, in certain weapons, only if you are smart enough. So smart warriors will have an advantage over dumb ones. I'll cover the feat system thing in a future post, so for now let's just say this: we'll make a spell that uses opcode 233 to increment proficiency in long swords by one point. Put this .spl file somewhere in your mod folder and write a COPY line in your .tp2 file to copy it into the game. Let's call it "d5LSprof.spl." 2. Next, get your stat ready to be analyzed. Opcode 326 reads SPLPROT.2da. That file already has a line for "INT>=[value]" so if we only want to make a single effect for an INT score greater than or equal to 12, say, that would be easy. But what if we want a different effect for each score - zero bonuses at INT=12, and six bonuses for INT=18? The "greater-than-or-equal" relation doesn't work well here, we just want a simple "=" relation. So we'll have to add our own. I add a new line with four columns: a simple identifier (prefaced with my modding prefix, for safety), then "38" in the STAT cloumn (the line # in STATS.ids for INT), then "-1" in the VALUE column (-1 means, read the value from parameter1 of your 326 effect), then "1" in the RELATION column (1 means "equal" - read more about the RELATION values here). I separate them by the Weidu variable %TAB% so that they make nice neat columns in the .2da file.
Now I have a "INT=" line in SPLPROT.2da, so I am ready to give INT-based bonuses, right? Not so fast. I have my "INT=" line in the .2da file, but I have no way, in advance of the mod installation, to make a spell with a 326 effect that can reference that line. So I have to build it in Weidu.
3. Now, make your opcode 326 spell with dummy values. I make a spell with multiple effects. Each of these effects uses opcode 326. In the "parameter2" field (labeled "creature type" in Near Infinity) I'll choose a pre-existing one... let's use that "STAT INT >= value" that I mentioned earlier. We will change it later. But this lets me put a value into the parameter1 field (labeled "creature value"). (Remember, our new SPLPROT.2da entry has "-1" for VALUE so it will get the information from whet we put here.) I'll put a different INT score in parameter1 in each 326 effect. If I want a bonus for every point above 12, then I'll make six effects and populate parameter1 with 13, 14, 15, 16, 17, and 18. (or you could make 13 effects, and go all the way up to 25. The point is, since we're looking at a "=" relation, we need a separate 326 effect for each score.
Put this .spl file into your mod folder - let's call it "d5intspl.spl" - and add a COPY line to your .tp2 file. 4. Now you need to know how to find the line you APPENDed to SPLPROT.2da. I don't know what this file looks like; each player might have used many other mods that might have changed the file. So I have to find the line I added to it, at install-time. This is kind of the point of this thread: here is some code that you can adapt pretty easily to your own mod: This reads every entry in the first column ("column 0"), in every row in SPLPROT.2da, assigning what it reads there to the variable %stat%. If and when %stat% matches the string "D5_INT_EQ" (that's the first entry we APPENDed up in step 2) then it sets that row number into the variable %int_eq_row% for later use. Put this before the COPY commands so that the variable is ready to be used by them.
5. Now insert that variable into your 326 spell. Add this under the COPY line for the 326 spell: Remember I originally used a dummy value in parameter2 of the 326 spell, well now, at install-time, I am changing it to refer to the new row of SPLPROT.2da that I added.
6. Finally, let's add this to the Trueclass fighter CLAB table, to give them a bit of a boost over their superior kits: So the complete code in Weidu looks like this: You'll need to make the spell files yourself, but this can be adapted pretty easily to all sort of stuff. You can check any stat in STATS.ids, so ability scores, proficiency levels, saving throws, thief skills, spell effects, etc. You would only need to change that "38" to some other value in the APPEND line for SPLPROT.2da.
Okay that's it for now. Hopefully someday in the future I'll talk about how to make these effects dynamic (it involves what I call "auras," i.e. repeating self-cancelling effects).
Okay. Now we have applied a spell to a trueclass fighter that gives him or her some bonus based on INT score. What if we want this to apply to ALL fighters, of any kit? Not just the four vanilla kits, but all fighter kits from any source? The tricky thing is that we cannot control how each player mods their game. Some might stick with the four vanilla fighter kits but others might install tons of kits. How do we cover all bases?
We do that with the help of an associative array. My current method for this involving using arrays in a way that "they are not really meant to be used," according to one elder statesman of the modding scene. But it works! So to heck with it. Let's begin.
1. First, you make an array containing every kit and linking it to its base class. We will populate the array with information from kitlist.2da. This creates an array in memory that consists of a simple list of the clab table of every kit in kitlist.2da, linked to that kit's class number. (Mage = 1, fighter = 2, cleric = 3, thief = 4, paladin = 6, ranger = 12, etc.) The array is called d5_kit_clabs.
I never totally understood how arrays work, especially because it is not something that you can seen in front of your eyes. But all you need to know is what I just said: we have now linked each kit's clab table with its underlying class, in a way that Weidu can read and act upon, using ACTION_PHP_EACH.
2. Read and act upon the array using ACTION_PHP_EACH. Now the task is rather simple: tell Weidu to do something to every clab table that belongs to a fighter kit. First, we need to tell Weidu to read the array. (We are doing this as a fresh command, so it will be an ACTION operation; but you can also use PATCH_PHP_EACH if you want to do stuff in the middle of a PATCH operation.) "Yoo" and "hoo" are meaningless; they are just terms to help Weidu read the array. Remember above when we did DEFINE_ASSOCIATIVE_ARRAY we defined it as "clab => class." So now, "Yoo" equals clab, and "hoo" equals class. Now we will have Weidu do a command, but only for kits in the fighter class: The FILE_EXISTS check is just for safety's sake, to make sure the clab file listed in kitlist.2da hasn't disappeared somehow. If it has, this operation will gracefully skip over it and keep going. This is simply the patch we did above to the trueclass fighter clab table; only now instead of patching just the one table, I am patching every table that equates to the %yoo%.2da variable - i.e. every fighter kit.
Well, every fighter kit EXCEPT the trueclass - trueclass kits aren't listed in kitlist.2da. So we will keep the original APPEND line as well. Now we can put all that together, and we can combine it with the 326 code I posted above: Again, you can pull out this bit of code (everything from the COPY_EXISTING ~kitlist and below) and adapt it for all sorts of uses. I use this to give my "Quickstride" increased movement rate ability to all rangers. The sky is the limit.
BUT: install order matters. Like I said, this will patch every fighter kit's clab table, including those added by other mods... but ONLY if this is installed after those other mods. (Naturally, those mod kits can only be patched if they exists in your game at the time this patch is installed.) So this wants to be installed late in the install order. But so many mods want to be installed late in the order! How to deal with it?
My simple rule of thumb for install order, is this:
- Ascension
- quests
- NPCs
- items
- spells
- kits
- tweaks
- SCS
This sort of thing - patching abilities or effects into kits - is very much a tweak, so your mod should advertise it as such. Just tell players that it must be installed after all other mods that add kits to the game. Six or twelve different mods can do this sort of thing, and as long as they are all installed after all kit mods, they should work fine together.
Finally, PLEASE NOTE: there are better ways to do this. I mean, all of the info we put in the array (clab table and associated class number) is already right there in kitlist.2da, already associated with each other. We could forget the array and instead adapt the method from the post just above this one: read through each row of kitlist, noting the value in column 8 (class), check for a match against "2," and then note the row number, then read the value in column 5 in that row (clab table), then perform some action on that .2da file (using INNER_ACTION). Those skilled with Weidu will find a way to do this elegantly. But for enthusiastic amateurs like me, here's the important thing: the array method works, reliably. Variations of that code appear probably 30 or 40 times in my mods. So if you don't want to mess around with the code, but just want something to drop into your mod so you can focus on the creative decisions, this is for you.
Let's say you are making a kit mod, and you want to target your kit with opcode 177 "use .eff" effects, or opcode 326 "apply effects list" effects. Here's an example:
Say you make a sorcerer kit, and the theme is that it channels magical energy better than most sorcerers. Call it a "Channeler." Of course you will have read the excellent tutorial on creating KIT mods, so you know the first line of your ADD_KIT routine will look like this: That name is how the engine recognizes your kit... sometimes. Other times, it refers to the kit's value in the KIT.IDS file.
Say you want to add a special disadvantage, that every time a Channeler is struck by Magic Missile, they take an extra 2d4 magic damage. Because magic just flows through them too easily. The way to do this is to add an effect to the Magic Missile, using opcode 177 and targeting your kit, and making an .eff file that applies the damage. Opcode 177 uses the kit's value in KIT.IDS. For vanilla kits this is easy, they are listed in KIT.IDS and you can look up their value there and plug it into the 177 effect.
But mod-added kits are added to KIT.IDS sequentially, and you have no idea how many kit mods a player might install, or in what order. So how do you add that 177 effect to Magic Missile?
Well, each kit is listed in KITLIST.2DA, with its name and .2da clab table and description strings, etc. And in the EEs there is an extra column that contains the kit's IDS value. So with this file we can link the kit's name (which you will know, because you named it) to the kit's IDS value (which you will never know ahead of time. The following code will read the contents of that .2da file, find the line with your kit's name, and spit out the associated IDS value as a variable that you can use elsewhere. That is a bit hard to digest; I find the READ_2DA_FORMER/NOW stuff difficult to understand. But the nice thing is, there is no need to actually understand this. You can copy that and drop it directly into your own .tp2 code. The only thing you need to worry about is where it says: "...STRING_EQUAL_CASE ~D5_CHANNELER~..." J
just put your own kit's name between those two tildes: ~...~. The IDS value for your kit will be encoded into the %channeler_code% variable at the end there. (You can rename that variable if you like.)
Now, armed with that variable, we can edit Magic Missile: (Where "D5_MDMG.EFF" is the name of the .eff file you created that does the extra magic damage.)
You could also edit items to have unique characteristics with your kit, using ADD_ITEM_EFFECT or ADD_ITEM_EQEFFECT. Maybe the Staff of Power's thunderbolt ability does extra damage when used by a Channneler. Maybe a fleet-of-foot thief kit gets 2 extra APR when using Belm, instead of just one. The sky is the limit; you just need to know your kit's IDS value in order to make effects like that, and this code gives you that.
Okay, let's say you're into tweak mods. Like mine. I have a mod that slightly changes the Swashbuckler: it takes traps away (I can't really see Swashbucklers patiently laying traps) but gives them a (reduced) backstab multiplier. Then I add a different kit, the Scout, which has traps but no backstabs like the vanilla Swashbuckler.
So, I want to change the Swashbuckler's description in-game so that the player will read and understand the kit's new abilities and restrictions. But where is the text I have to change? I could search for it and find the string reference with Near Infinity... but that's a pain. Plus there are many different games with different versions of dialog.tlk (TOB, BGT, BGEE, SoD, BG2EE, IWDEE, EET) and some of those have doubles or even triples of the kit description text. Dealing with all those variations is a minor nightmare. So what do we do?
Ignore the string reference. Here's how:
The strings for all kit names and descriptions are listed in KITLIST.2DA in columns 3, 4, and 5 (lower-case name, mixed-case name, and description, respectively). If your game has the files, the kit descriptions and the class descriptions are also in CLASTEXT.2DA and SODCLTXT.2DA, in columns 3, 4, and 5 (lower, description, and mixed, respectively - note the different order!). So to find and alter the strings, we can read those files. Here's example code that is extremely easy to adapt to your mod: You can copy and paste this into your mod, all you have to change is the kit name in the "PATCH_IF ~%kit_name%~ STRING_EQUAL_CASE" lines, and change the @_____ tra references to your own strings. Just, remember that the order is different between CLASTEXT and KITLIST!
If you want to get more advanced you can do more complicated things. Instead of using SET_2DA ENTRY to replace the whole string with a new one, you could READ_2DA_ENTRY to get the string reference into a variable, and then edit the original string with a more fine-grained method.
The key thing here, though, is that the above code works on every version of the games, at least going back to BG2/TOB. Players will be able to install your mod on SoD, BG2EE, or IWDEE and your code doesn't even have to know which game is being played.
@BCaesar and @agb1 may in particular be interested in this.
For your edit to HPCLASS.2DA, it is a simple matter: you wan to use SET_2DA_ENTRY: That will change the entries for Monk, Sun Soul, and Dark Moon. (Which are in the 44th, 45th, and 46th rows of that file that have at least two colunms. That explains the 1st and 3rd numbers in those lines. The 2nd number, "1," is slightly trickier: it tells Weidu to edit the entry in the second column... because for whatever reason, in this instance, it starts counting at zero.)
The #3 number is the "minimum column count." So when you say row # whatever, if that row has fewer columns than the number in #3, it will be ignored. WHat this really means is that there are only two things you should put here: 1, or the actual column count of the 2da table. For a file like weapprof.2da, just put 1. The only issue this causes is that some rows, like the very first one with some garbage text, get counted in your row count - because that garbage is considered a column. All this means is that your row number might be different. But that doesn't matter because you just put whatever row number works.
If you use 1 in #3 with weapprof, I believe row 11 is bastard sword, and row 34 is dual-wielding, and everything else you could possibly want to change is between those two rows. (I think those are the numbers you would use in #1... easy enough to double-check.)
But be aware, negative values do not work in all of these functions. I *think* it works in this one, but it does not work in ALTER_EFFECT or CLONE_EFFECT.
To give a really general example for an NPC Mod without addressing any tips for compatibility with EET or such (Especially since this is for personal use anyway), they all start like:
BACKUP: ~Where the backup folder is created. Generally something like ~ModFolderName/Backup~
AUTHOR: ~Your name here. Or Website. Or some people put the support file name. But this is personal use so...your name here.~
VERSION ~1.0, or whatever version number you are using to help keep track of what change/version your mod is at.~
AUTO_TRA ~%MOD_FOLDER%/tra/%s~ // Sets the directory of the language files.
LANGUAGE: Sets the language. Generally it will look like:
~English~
~English~
~%MOD_FOLDER%/tra/english/setup.tra~ , or ~French~ ~French~, ~Directory to set up file~ etc, etc.
BEGIN ~Mod name, can be anything. This is just a display string~
Let me know if I"m on the right track in regards to what you are looking for in an answer, then I'll spell out the rest of things.
After adding your suggestions in, this is what I got. I tried to mirror what the Branwen NPC tutorial showed me on it's .TP2 file prior to this, but due to the fact I don't have audio as well as other features, it fell flat in what I could mimic in that case.
My mod has a Backup folder, a Character folder, a Dialogue folder, an English folder, and a Scripts folder. I hope that can more or less shed some light on what I'm working with.
Your LANGUAGE: line isn't parsing because it should be LANGUAGE with no colon.
After Begin, it should start looking like this:
// Adds custom IsValidForPartyDialogue state used throughout
APPEND ~STATE.IDS~ // adds custom IsValidForPartyDialogue state
~0x80101FEF CD_STATE_NOTVALID~
UNLESS ~CD_STATE_NOTVALID~
IF and ONLY IF you use CD_STATE_NOTVALID, you can go ahead and include the above. Else it's wasted lines of copying. For BG:EE and BG2:EE, IsValidForPartyDialogue("Creature") works just fine and is much better than the group use of InParty("Creature") InMyArea("Creature") !StateCheck("Creature",CD_STATE_NOTVALID) that was standard use in older mods for interject. You only need it if you want to be compatible with older BG2 and BGT.
Next you can start copying the creature and lines over, this is a snippet, but since you've seen Branwen, you probably know what the full group is supposed to look like. Make sure to have an instance for ToB and/or SoD if you're using them in those expansions and want them to appear for people who are just starting those games out and not importing a save.
This will set up your Joined party, banter lines, and post party lines. The D after X3Emi (X3Emi is my character's creature code) is any lines that run after the rest party is clicked, a pre-rest talk with the protagonist.
If you have custom portraits, just include their path in a COPY command.
A small note while we are at creatures, non-joinable creatures don't need the lengthy set up, just these few lines will do.
Then you just set up the creature/dialogue/script files. Note for area files you use the EXTEND_TOP or EXTEND_BOTTOM. I tend to use EXTEND_TOP if it's noticeable that an NPC will just suddenly "appear" because its taking the game so long to get to the end of the area script, or there's a script preventing mine from firing. Otherwise EXTEND_BOTTOM works just fine.
Then make sure dialogue/script files start with COMPILE EVALUATE BUFFER
Let me know if that helps. If you get any other errors, just copy screenshots here again and I can help you.
This very much helps, thank you. But now I'm getting the cre error I was getting beforehand, despite the fact I labeled everything accordingly as shown. Unless there's something I'm missing or just not seeing.
EDIT: Actually, found and fixed that problem. But hold a moment.
Yeah, now something in the scripting is acting up. Don't exactly know what, but I have just about the full script showing in the screenshot in case it can be caught.