General Notes
- Plan on patching, not overwriting. More on this later.
- APPEND, rather than REPLACE
- Beware of hijacking original material. More on this later, too.
- It's really cool when added quests use new areas and new creatures, because that lets existing material play out as intended, and avoids overlap with other mods. This does take work and planning, though.
Most mods make some adjustment to existing game dialogue. This is as it should be, but there are a few ways to ensure that your mod will still "play nice" with other mods.
- Use INTERJECT sparingly. INTERJECT_COPY_TRANS is fine, because the COPY_TRANS ensures that other mod content can play. INTERJECT derails the content entirely, sending it off in its own direction. Original content may not be seen, let alone contributions by other mods that might affect this dialogue. Sometimes, you may be tempted to do this, for a plot-essential reason, but you should still give the matter a lot of thought before you go there.
Suppose I wanted to make it impossible for the PC to work for Bodhi, because Gavin, a cleric of Lathander, would have grave reservations about working for a vampire. I could accomplish this by using an INTERJECT or two, but I'd be railroading the PC into a major decision, and I'd be making huge chunks of other mods unavailable in the process. I would be better off using INTERJECT_COPY_TRANS to let him register his objections, or even forcing him to leave the party if the PC still decided to work for Bodhi. (I'm not planning on doing this, by the way.) - Use the same caution with REPLACE_STATE_TRIGGER and other REPLACE actions. There is usually some kind of workaround.
- When using EXTEND_TOP and EXTEND_BOTTOM in dialogue files, it is considerate to COPY_TRANS back into the original state to allow other content to show.
- Please use EXTEND_BOTTOM wherever possible in dialogue files, as opposed to EXTEND_TOP. Modders rely on original BioWare transition order for ADD_TRANS_ACTION and ADD_TRANS_TRIGGER functions, so using E_T is likely to break other mods.
- Always use modder prefixes!
Suppose I wanted to add a brand new character called Vai to BG1. I'd be in deep trouble if I didn't use a modder prefix, because the vai.cre I copied into the game would overwrite the existing Officer Vai. Now suppose I wanted to create a creature called Tashia. Again, I'd have some trouble, because there is already an older mod who uses tashia.cre as a creature code. There is nothing we can do about older mods, because that's how things were done then. But going forward, there is no reason not to use a modder prefix. Creatures, dialogue files, scripts, etc.: all need them. - Use modder prefixes in interjections! Every time an interjection is made, a global variable is added to the game. This variable is the same as the state label for the interjection. If I use AranJob as a state label for an interjection, I'll mess up the game. Better to use a prefix.
- Be careful about state weighting with banter files. Because many mods use the banter file for scripted dialogue, a mis-assigned state weight might cause some oddness. More on that in a minute, during the great B/J debate, but for now, when you add unscripted banter to a BioWare NPC, you are better off not assigning a state weight. Yes, your banters will play after all the original material, but at least it won't cause any of the NPCs to develop a sudden case of chatterbox.
- Any mod that allows the player to skip chunks of the game is probably going to be incompatible with some other mod. Chloe spawns in Chateau Irenicus, so using DungeonBeGone will prevent you from picking her up. This does not mean that you should not play with Chloe or DungeonBeGone, nor that you should not write mods that allow the player to skip some of the more tedious aspects of the game. It just means that you should be considerate about how you do it. For example, in DungeonBeGone, you don't have to talk to Jasper, nor do you have to accept his help. In the Ust Natha Accelerator in Zyraen's Miscellany, you don't have to go do the pit fights before going to Solaufein. Both of these mods provide shortcuts in a compatibility-friendly fashion.
Please do not copy already-compiled scripts into the override. You're going to have to write it anyway before you compile it, so you might as well write it as a BAF and let WeiDU compile it during installation.
And some more things to think about:
- Do not use a True() trigger outside a cutscene. Including them in area scripts, BioWare NPC scripts, or other game scripts will prevent material added after them from showing up.
- EXTEND_TOP vs. EXTEND_BOTTOM... check it out first. Look at the script in question to see if it already includes a True() trigger or an OnCreation() trigger. Many do. If the script includes a True() trigger, you should EXTEND_TOP. Otherwise, EXTEND_BOTTOM. If you do need to EXTEND_TOP, please do it in a way that will not create problems in the original script. Use efficient scripting, and make sure you close your check variable, so that the script will only fire once. If the script contains an OnCreation() trigger, you need to read up on the use of Continue(). It is outside the scope of this tutorial to talk about it here, but in general, if you are extending the top of an area script, use Continue().
- Be very, very careful about what you add to the game scripts, baldur.bcs and baldur25.bcs. There will be times when this is unavoidable, but if possible, consider using NPC scripts or area scripts instead. If your script contains InParty(), it can go in an NPC script. If you simply must add to a game script, you must do so considerately. Keep it short, keep it clean, and keep it efficient.
This would be all right to add:
IF
Global("BAidanDiagott","GLOBAL",1)
PartyHasItem("B!AMACE")
Global("B!AidanHasMace","GLOBAL",0)
THEN
RESPONSE #100
SetGlobal("BAidanDiagott","GLOBAL",2)
SetGlobal("B!AidanDelayTalk","GLOBAL",1)
SetGlobal("B!AidanHasMace","GLOBAL",1)
END
This would NOT (crossing it out, just to reinforce the idea of not using it):
IF
OR(18)
AreaCheck("AR0021") //City Gates - Crooked Crane 1st floor
AreaCheck("AR0022") //City Gates - Crooked Crane 2nd floor
AreaCheck("AR0313") //Docks - Sea's Bounty 1st floor
AreaCheck("AR0314") //Docks - Sea's Bounty 2nd floor
AreaCheck("AR0406") //Slums - Copper Coronet
AreaCheck("AR0509") //Bridge - Five Flagons 1st floor
AreaCheck("AR0510") //Bridge - Five Flagons theater
AreaCheck("AR0511") //Bridge - Five Flagons 2nd floor
AreaCheck("AR0513") //Bridge - Calbor's Inn 1st floor
AreaCheck("AR0514") //Bridge - Calbor's Inn 2nd floor
AreaCheck("AR0514") //Bridge - Calbor's Inn 3rd floor
AreaCheck("AR0522") //Bridge - Five Flagons 1st floor (stronghold)
AreaCheck("AR0523") //Bridge - Five Flagons theater (stronghold)
AreaCheck("AR0704") //Waukeen's Promenade - Mithrest Inn
AreaCheck("AR0709") //Waukeen's Promenade - Den of the Seven Vales
AreaCheck("AR1105") //Umar Hills - Imnesvale Inn
AreaCheck("AR1602") //Brynnlaw - Brynnlaw Inn
AreaCheck("AR2010") //Trademeet - Vytori's Pub
Global("BAidanDiagott","GLOBAL",1)
PartyHasItem("B!AMACE")
Global("B!AidanHasMace","GLOBAL",0)
THEN
RESPONSE #100
SetGlobal("BAidanDiagott","GLOBAL",2)
SetGlobal("B!AidanDelayTalk","GLOBAL",1)
SetGlobal("B!AidanHasMace","GLOBAL",1)
END
Most of this is related to considerations for Mac users and for folks who want Mega installs.
-
Don't use \ in your tp2. It makes life tough for Mac users.As per the bigg, this issue has been addressed. - Linux is case sensitive, so you should label all your files in lowe-case. If you are saying COMPILE ~mymod/dialogue/lovetalks.d~, then the mod folder should be named mymod, the dialogue subfolder should be titled dialogue, and the D itself should be titled lovetalks.d. WeiDU has been adapted to convert to lower-case on the fly, though, so you could COPY_EXISTING ~sw1h01.itm~, COPY_EXISTING ~Sw1H01.itm~, COPY_EXISTING ~SW1H01.ITM~ or whatever.
- The songlist in BGT installations fills up pretty fast. Your mod might have trouble if you've decided to add your music to the songlist. You might be better off using a different method of getting music into your mod, like copying mymusic to the override, and then using
PlaySong(0)
PlaySound("mymusic")
If you really feel strongly about allowing non-Mega folks to listen to your music, you could detect for BGT, one of the key mods of a Mega, and only install the music, and the version of the script that calls it, on installations that do not contain it. This would involve creating two versions of the script, though, one that includes the PlaySong("mysong") and one that includes PlaySong(0). Of course you could be very clever and use cross-platform variables for this with EVALUATE_BUFFER, but that is the subject of another tutorial. - Kits. NPC-only kits are no problem. You can add as many of them as you like. But when you make a kit available to the PC as well, that might cause problems on mega installs, some of which are already maxed out for kits. There are workarounds for this, as well. You can make two versions of the kit, one of which is NPC-only, and then offer the player the choice of whether or not to install the custom kit. If the player does install the custom kit, install the NPC CRE with the PC-available version. If the player decides not to install the custom kit, install the NPC CRE with the NPC-only version.
- When using COPY_EXISTING(_REGEXP) to copy game files with the tp2, always include BUT_ONLY_IF_IT CHANGES. When APPENDing 2da, always use UNLESS file.
- Patch rather than overwrite. If I was writing a mod for a neutral evil Jaheira that used a half-orc avatar, I'd patch her alignment change and avatar, rather than just replacing her CRE. Any field you can view in NI can be altered by patching. All you need is the hex offset. For example, suppose I did want to change Jaheira's alignment. I'd fire up NI, look up the hex offset for alignment and animation, and then stick the following in my tp2:
COPY_EXISTING_REGEXP GLOB ~^jahei.*\.cre$~ ~override~
WRITE_BYTE 0x27b 35 //alignment neutral evil
WRITE_SHORT 0x28 24853 //FIGHTER_FEMALE_HALFORC
BUT_ONLY_IF_IT_CHANGES
This can be done for spells, and other things, too, but it takes more homework, and more testing.
It is entirely a matter of personal preference where you put your scripted banter. Some folks put it in the B file, and use Interact() to call it. Others use the J, and use StartDialogueNoSet() to call it. Historically, modders have used the B.
I like to put all scripted dialogue in the J file as opposed to the B file, and use StartDialogueNoSet() to call it. The reason for this is simple: state weighting is less complicated, and crossmod is greatly simplified. If ever you need to add anything to the J after the PID is compiled, no need to worry about state weights: just stick a WEIGHT #-1 after the IF for each conditional state, and you have nothing to worry about.
But not everyone does it this way. Suppose, for a moment, that I decide to use the B file for Gavin's scripted dialogue.
I install Kelsey, the Flirt Pack, the Banter Pack, Xan, Iylos, Gavin, and Crossmod Banter Pack. The first five mods all have scripted banter in the B. Now suppose I have decided to include both scripted and unscripted crossmod banter in Gavin's B file.
Now, suppose I wanted to have have Kelsey/Gavin crossmod, and have unscripted ToB banters between them, but that I didn't want to have them appear in game dead last. I would need to add a state weight that will put them ahead of some of Kelsey's already compiled unscripted banter, but after his scripted banter. I would decompile bj#kel25.dlg, and find out that the ending weight for the last state in the scripted portion of the mod is 25. So, I would go ahead and weight the banter with a WEIGHT #27, which will put it ahead of many original Kelsey banters. OK... except for one small problem: Xan and Iylos went and added scripted banter to Kelsey's B, which means that now, state weight #27 happens to fall before some of the Xan/Kelsey material and all of the Iylos/Kelsey material. So, when the banter engine accesses the banter file for an Interact() in Iylos, it's going to have all the unscripted banters file in rapid succession before it gets to the scripted banter added by Iylos. Iylos will get the bug report, but it's really Gavin's fault. So, I can't weight unscripted Gavin/Kelsey crossmod, so it will just have to show up dead last, after all other banters. Bummer. There isn't much I can do about this, since I'm not responsible for Iylos', Kelsey's, or Xan's coding.
The whole thing is a heck of a lot simpler if modders put all scripted banter in the J file instead. That way, all the modder needs to do is add a WEIGHT #-1 to get scripted banter ahead of any possible PID, and everything is cool. When somebody wants to add weighted banters to Gavin for the CBP, it's easier, too. A sensible state weight for the banter going into ~BB!Gav25~, and it's all good.
But I admit that this doesn't matter much unless you want crossmod.
Edited by berelinde, 23 December 2007 - 03:16 AM.