1. GeneralBAMs: only decompressed. BGII BAMs are usually compressed. For BG1, they have to be decompressed, or the item will lead to a CTD. Decompressed BAMs work in BGII.
Near Infinity can be used to decompress BAMs.
Further, description BAMs have to be quartered in BG1 into four frames. BGII description BAMs are unsplit, they have to be split if meant to be used in BG1. I used
BAMWorkshop II (scroll down the page) to split the BGII BAMs. Note: In BG1:TotSC, unsplit BAMs work as intended. (Split BAMs also work fine in BGII.)
There is no overall script like "Baldur.BCS" in BG1. A replacement could be the dplayer3.bcs, which is the script of Player1. Some "monologues" are triggered via this script in the game.
cre and itm, if they are supposed to work in both BG1 and BGII engine (for Tutu or BGT): BG1 items and creature files without any special abilities, effects or weapon proficiencies will work for BGII, but not necessarily the other way around. Therefore, if you are planning a mod that should be compatible with all BG1 games, I suggest creating the files using BG1 resources.
For differences in special abilities, effects or weapon proficiencies, please have a look at Miloch's (aka Tervadh) tutorial
Converting BG1 items to BG2: Weapon proficiencies need to be set (at 0x31), otherwise all items will get a Large Sword proficiency. All the kit usability flags need to be unset as relevant. Since BG1 did not have kits, it didn't matter whether they were set or not. I could imagine differences could be covered during installation via tp2-patching depending on the detected game, as
described by Miloch, but I cannot provide examples for this yet.
No dialogues at restNPCs have no dream script. There is no "...D.bcs", meaning that the engine is not capable of directly triggering a dialogue before rest, i.e. if the player presses the "rest" button.
I know of no way of realising this for BG1.
For the Ajantis vanilla BG1 romance, I used the following workaround: upon entering a tavern, the NPC starts a dialogue in which the player can choose whether he wants to directly end and therefore postpone the dialogue, or whether he wants to play the dialogue that will be followed by a forced rest. The dialogue is triggered by a variable that got set in the script of the tavern area.
Example:
The dialogue would start like this:
IF WEIGHT #-2 ~Global("X#AjantisReadyForNT1","GLOBAL",3)~ THEN resttalk_1
SAY ~Is this where we will rest for today?~
++ ~No, not yet.~ DO ~SetGlobal("X#AjantisReadyForNT1","GLOBAL",4)~ + resttalk_1_0a //-> ends the dialogue and resets the timer for the next tavern
++ ~Yes, we will rest immediately.~ DO ~SetGlobal("X#AjantisReadyForNT1","GLOBAL",4)~ + resttalk_1_0b //-> plays the dialogue, followed by a forced rest
END
In the tavern script, the variable "X#AjantisReadyForNT1" was set to 1 beforehand, and in ajantis.bcs, the dialogue was triggered.
tp2-Patching of the tavern scripts looks like this (only the first levels of taverns were used, as the dialogue gets triggered after the entering of the tavern. Thanks to Zed Nocear for providing the list):
//Patching of the tavern scripts for "rest talk"
EXTEND_BOTTOM ~AR1001.BCS~ ~C#AjantisRomance_BG1/Scripts/C#Tavern_AD.BAF~
EXTEND_BOTTOM ~AR0116.BCS~ ~C#AjantisRomance_BG1/Scripts/C#Tavern_AD.BAF~
EXTEND_BOTTOM ~AR0119.BCS~ ~C#AjantisRomance_BG1/Scripts/C#Tavern_AD.BAF~
EXTEND_BOTTOM ~AR0103.BCS~ ~C#AjantisRomance_BG1/Scripts/C#Tavern_AD.BAF~
EXTEND_BOTTOM ~AR0114.BCS~ ~C#AjantisRomance_BG1/Scripts/C#Tavern_AD.BAF~
EXTEND_BOTTOM ~AR0705.BCS~ ~C#AjantisRomance_BG1/Scripts/C#Tavern_AD.BAF~
EXTEND_BOTTOM ~AR0105.BCS~ ~C#AjantisRomance_BG1/Scripts/C#Tavern_AD.BAF~
EXTEND_BOTTOM ~AR0133.BCS~ ~C#AjantisRomance_BG1/Scripts/C#Tavern_AD.BAF~
EXTEND_BOTTOM ~AR2301.BCS~ ~C#AjantisRomance_BG1/Scripts/C#Tavern_AD.BAF~
EXTEND_BOTTOM ~AR3304.BCS~ ~C#AjantisRomance_BG1/Scripts/C#Tavern_AD.BAF~
EXTEND_BOTTOM ~AR3307.BCS~ ~C#AjantisRomance_BG1/Scripts/C#Tavern_AD.BAF~
EXTEND_BOTTOM ~AR3351.BCS~ ~C#AjantisRomance_BG1/Scripts/C#Tavern_AD.BAF~
EXTEND_BOTTOM ~AR3357.BCS~ ~C#AjantisRomance_BG1/Scripts/C#Tavern_AD.BAF~
"Greeting sound": In BG1, it is not possible to "mute" a creature by assigning another blank sound reference (e.g. [C#BLANK]). Creatures say their assigned "greeting sound" nontheless. This results from a difference in processing of the sound reference between the engines, but I don't know how exactly. I haven't tried to assign a different sound file to a creature, so I don't know whether both sounds would be played if the creature is talked to, but considering Kulyok's experience for IWD I assume they'd do.
Coding interjections is a bit more complicated, since INTERJECT_COPY_TRANS (and presumably INTERJECT, as well) doesn't work as intended: The PC replies of the original dialogue state show during the interjection lines.
To simulate I_C_T, as it would appear using the BG2 engine, the following workaround has to be used (
(thanks to Kulyok). Please refer also to the tutorial
IWD engine: dialogue coding):
You need three files, that get compiled separately. The first one is a placeholder and is called PREFIX_DUMP.d. Make as much entries as you have interjections, for example:
BEGIN PREFIX_DUMP
IF ~~ 0 SAY ~Temp~ IF ~~ THEN EXIT END
IF ~~ 1 SAY ~Temp~ IF ~~ THEN EXIT END
IF ~~ 2 SAY ~Temp~ IF ~~ THEN EXIT END
IF ~~ 3 SAY ~Temp~ IF ~~ THEN EXIT END
IF ~~ 4 SAY ~Temp~ IF ~~ THEN EXIT END
IF ~~ 5 SAY ~Temp~ IF ~~ THEN EXIT END
IF ~~ 6 SAY ~Temp~ IF ~~ THEN EXIT END
IF ~~ 7 SAY ~Temp~ IF ~~ THEN EXIT END
IF ~~ 8 SAY ~Temp~ IF ~~ THEN EXIT END
IF ~~ 9 SAY ~Temp~ IF ~~ THEN EXIT END
IF ~~ 10 SAY ~Temp~ IF ~~ THEN EXIT END
The second file is called REPLACE.d. It is for saving the original, and adding an additional transition trigger to the original transitions of the state where the interjection should be added. The third file is the actual interjection, divided into an EXTEND_BOTTOM that adds the new interjection to the original state, and a CHAIN that allows the interjection flow like an I_C_T type of interjections. Important is to add a check variable, so the interjection only runs once. This variable can also be used as new transition trigger for the original transitions, so they show the next time once the interjection occurred since then check variable = 1.
The only drawback, and I guess there is no solution to that, is the missing OR() trigger in BG1. This makes it necessary to add a stupid extra-line to the original state, in case the NPC isn't able to speak his interjection line. A way to avoid this is to use less triggers for the interjection, e.g. only "InParty("npc")" instead of the triple "InParty("npc") See("npc") !StateCheck("npc",CD_STATENOTVALID)", but I wouldn't advise to.
Example. Ajantis meets Raleo Windspear in Beregost, and greets him as the brother of Lord Garren Windspear. So we have (please note that "C#" is my personal prefix):
PREFIX_DUMP.d:
BEGIN PREFIX_DUMP
IF ~~ 0
SAY ~TEMP~
IF ~~ THEN EXIT
END
REPLACE.d:
REPLACE PREFIX_DUMP
IF ~~ THEN 0
SAY ~TEMP~
COPY_TRANS Raleo 0
END
END
ADD_TRANS_TRIGGER Raleo 0 ~Global("C#AjantisBG1RALEO0","GLOBAL",1)~ //original transitions fire if the interjection run
INTERJECTION.d:
EXTEND_BOTTOM RALEO 0
IF ~Global("C#AjantisBG1RALEO0","GLOBAL",0)~ EXTERN RALEO Ajantis_Windspear_interjection
IF ~InParty("ajantis") Detect("ajantis") !StateCheck("ajantis",CD_STATE_NOTVALID) Global("C#AjantisBG1RALEO0","GLOBAL",0)~ EXTERN AJANTJ Ajantis_Windspear_interjection
END
CHAIN AJANTJ Ajantis_Windspear_interjection
~Sir Raleo Windspear! I am delighted to see (blabla).~ DO ~SetGlobal("C#AjantisBG1RALEO0","GLOBAL",1)~
== RALEO ~Ah, the youngest Ilvastarr's offspring. (bla).~
== RALEO ~Indeed. That will be another while, though. But now I am sure your friend has some questions about this town, hm?~
END
COPY_TRANS PREFIX_DUMP 0
CHAIN RALEO Ajantis_Windspear_interjection
~Don't be shy and ask away.~ //stupid passback line if Ajantis didn't come to say his bit
END
COPY_TRANS PREFIX_DUMP 0
All that's missing is the lines in the tp2, to compile the files separately:
COMPILE ~Mod/Dialogue/PREFIX_DUMP.d~
COMPILE ~Mod/Dialogue/REPLACE.d~
COMPILE ~Mod/Dialogue/INTERJECTION.d~
Items with charges: there exists no "recharges after rest" behaviour. This behaviour could be simulated by replacing the old item with a fresh one after rest, using
Zed Nocear's Trigger-Simulations for BG1, but for BG1, it's only limited charges or always usable.
Add mod NPC to BG1 Joinable NPCs have to be listed in the baldur.gam. In BGII every NPC that is not listed in Baldur.gam will be added automatically as soon as he joins the group, hence in BGII, the baldur.gam doesn't have to be altered by a mod. The BG1-Engine does not add NPCs automatically to the baldur.gam, but the mod has to alter the baldur.gam to make the mod NPC fully available in the game. If not, the game will crash the first time the NPC tries to travel fom one area to another.
WeiDU offers ADD_GAM_NPC (please refer to the WeiDU-readme), but with ADD_GAM_NPC all BALDUR.GAM files in SAVE and MPSAVE get altered, which is... cruel. EDIT: It seems that this leads to scew-ups if a mod that adds NPCs via ADD_GAM_NPC gets installed twice, as it can happen easily during a complex install, plus a proper deinstallation doesn't seem to be possible:
ADD_GAM_NPC and uninstallation, non-functionality of.
Zed Nocear suggests the following possibility to add a custom joinable NPC to the game via the tp2:
COPY_EXISTING ~baldur.gam~ ~override~
INSERT_BYTES 0xB4 0x160
WRITE_LONG 0x140 0xFFFFFFFF
WRITE_LONG 0x144 0xFFFFFFFF
WRITE_LONG 0x168 0xFFFFFFFF
WRITE_SHORT 0x16C 0xFFFF
READ_LONG 0x34 npc_count
WRITE_LONG 0x34 ("%npc_count%" + 1)
READ_LONG 0x20 party_offset
WRITE_LONG 0x20 ("%party_offset%" + 0x160)
WRITE_LONG 0x28 ("%party_offset%" + 0x160)
WRITE_LONG 0x50 ("%party_offset%" + 0x930)
WRITE_ASCII 0xC0 ~MYNPC~ // name of file MYNPC.CRE (without extension)
WRITE_ASCII 0xCC ~AR2600~ // area that you want NPC placed in
WRITE_SHORT 0xD4 123 // x co-ordinate on the map
WRITE_SHORT 0xD6 456 // y co-ordinate on the map
WRITE_LONG 0xC8 7 // face orientation
This solution has one disadvantage: A new game has to be started for the NPC to be present in the game.
The advantages are:
- only the main file Baldur.GAM gets changed, SAVEs don't get touched.
- the NPC-CRE-changes as defined by NPCLEVEL.2DA get applied, depending on the level of the PC (this would also be true for ADD_GAM_NPC).
- "face orientation" can be used.
- ADD_GAM_NPC carries the flag
"do not use this".
Post-dialogue of joinable NPCsIn BG1, the engine does not switch automatically from the joined dialogue "...J.dlg" to the post dialogue "...P.dlg" upon kickout. The kickout dialogue state is contained in the joined dialogue. Upon kickout, the post-dialogue has to be set via ~SetDialog("mynpcP")~. Upon joining, the dialogue is triggered via the post dialogue and the dialogue file is changed to the joined dialogue like in BGII.
An example: Ajantis' kickout dialogue is in his AJANTJ.dlg:
IF ~True()
~ THEN BEGIN 1 // from:
SAY #16623 /* ~Aber ... aber ... wir waren doch ein Team und durch Ehre verbunden! Seufz... Warum ist bloß alles immer so kompliziert?
~ */
IF ~~ THEN DO ~LeaveParty()
SetDialog("AjantP")
~ EXIT
END
This means in addition, that no "Kickout" variable is needed in BG1.
Further tutorials connected to NPC creation:
Extended list of BG1 character sound offset names, BG1 TotSC by Baronius, linking to the
BG1 CREATURE SOUND SLOTS by BaroniusPlayer initiated dialogue In BGII, player initiated dialogue (PID for short) can be realized by selection of the "dialogue" button, and klicking on the NPC. In BG1, chosing the "dialogue" icon will not work if applied to party members. To realize PIDs in BG1, I therefore chose the hotkey to enable the dialogue. The hotkey will only be executed, if the NPC in question is selected. Please note: If more than one NPC is selected, all could react to the hotkey. If all of them have PIDs that are coded like this, they would fire their dialogue one after the other.
I used the hotkey "k", and (instead of having the last dialogue state triggered with ~True()~ to be the PID like in BGII) I used a variable that will be set, and reset to "0" by the script. Further, the script block activated by the hotkey will lead to the NPC approaching to the PC, and only if he sees her. This was to make up for the missing "IsGabber()", to make sure it is the NPC and the PC talking.
So, to enable the hotkey, a script block has to be added to the NPC's script. The second block is for setting the variable back to zero, this will happen after the flirt menu was opened. (Theoretically, this variable could be set to zero in the dialog file after the flirt. But I didn't feel like adding closing variables for x++ flirts, and forgetting some in the end, so I went for the easier solution.):
// Player-initiated dialogue
IF
InParty(Myself)
HotKey(K)
Detect(Player1)
!StateCheck(Myself,CD_STATE_NOTVALID)
!StateCheck(Player1,CD_STATE_NOTVALID)
THEN
RESPONSE #100
SetGlobal("C#AjantisPCIniFlirtTime","GLOBAL",1)
Dialogue(Player1)
END
IF
Global("C#AjantisPCIniFlirtTime","GLOBAL",1)
THEN
RESPONSE #100
SetGlobal("C#AjantisPCIniFlirtTime","GLOBAL",0)
END
And in the dialogue file, the PID dialogue state trigger would look like this:
IF WEIGHT #-2
~Global("C#AjantisPCIniFlirtTime","GLOBAL",1) Global("X#AjantisRomanceMatch","GLOBAL",1) Global("X#AjantisRomanceActive","GLOBAL",1) !Detect([ENEMY])~ THEN BEGIN ajantisflirts_begin_01
SAY @0
...
Portraits The used format notation is "S" for the small and "L" for the medium sized portraits. Large, like they are used in BGII for the ToB epilogue, are not used in BG1.
No real time timersRealGlobalTimerExpired() and RealSetGlobalTimer() do not exist: there is no real time timer. Only the global game time timer (GlobalTimerExpired() - SetGlobalTimer()) exists. Possible time frames are ONE_DAY, TWO_DAYS etc.
If using numbers, like "150", experiences vary. Some say, it leads to unstable results. Zed Nocear reported the following:
From GTIMES.IDS, a game day takes 7200 seconds. The timer counts in real seconds, if the game is set to 30 frames per second. This would mean, that "150" is two minutes real time, or half an hour game time.
Variables: Original BG1 uses mostly global variables. BG1:TotSC uses two AREA-type variables. Still, all three variable types exist ("GLOBAL", "LOCALS", "AREA"), but with some difference in how they function compared to BGII
(by Zed Nocear):
GLOBAL-variables function the way they do in BGII.
LOCALS-variables work like in BGII, but they are not saved in the savegames. This means, that after every start or loading of the game the local variables are reset to zero. They are like temporary help variables. This might be of advantage, as they do not accumulate in the SAVEs.
Further, there seems to exist an upper boundary as to how much local variables can be used, although I don't know whether for the whole game or per creature. Trying to set 80 LOCAL variables via dplayer3.bcs has led to a CTD.
AREA-variables work, but they are globally available like GLOBAL variables. "MYAREA" is not available in BG1, so for AREA-variables, the area name has to be specified, which makes the variable available in other areas, too.
For testing, the NPCs of the party were placed in different inside areas. If a variable "Global("VariableName","AR0701",1)" was set of one creature in AR0701, it could be read by the script of an NPC in area AR0702.
Therefore, AREA-variables seem to be useless and can be replaced by global variables.
Setting of variables in dialogues and scripts - how fast are the new values available? (by Zed Nocear) If a variable gets set in a dialogue or a script, how long does it take until it is available? It will lead to errors, if a variable is called before the engine actually got to setting the new value.
1. Script (this behaviour is equal to BGII, as far as I know, but I will list it nonetheless): A script gets executed from top to bottom, until a block with valid trigger is found. This block gets executed, and the script execution starts from the top again. If the script block was about setting a new variable value, the new value will be available after the new script call.
An exception to the restart of the script appears if Continue() is used at the end of the script block. In this case, the following script block will be executed directly, but all variables will still have the old value. The new values, again, will only be available after the restart of the script.
2. dialogue: Experience has shown, that for variables set with SetGlobal(), the new value was available in the following dialogue state. For IncrementGlobal(), it takes two dialogue states, though.