Jump to content

BG1 coding tutorial


Recommended Posts

BG1 coding tutorial

Please note: this tutorial is for the classic BG1(TotSC) engine only. All listed restrictions are not present for classic BGII or EE engine.

This tutorial assumes a certain knowledge of the BGII syntax and IE-modding in general. It highlights some crucial differences between the engines of BG2 and BG1, especially with regards to the restrictions of the BG1 engine in comparison to that of BG2, but it also includes some available BG1 syntax that might not be readily apparent.
The focus of this tutorial is on the workarounds and simulations that emulate BG2 functionality.

Comments, corrections, and additions are welcome, especially if you can provide not yet listed workarounds for missing BGII functions, please feel free to post them here. I will update the tutorial accordingly.

Thank you to berelinde for language corrections!

notation:
BG1 - plain BG1, without the Tales of the Sword coast Add-on.

BG1:TotSC - BG1 with the add-on installed.

cre: creature file used in IE-modding

itm: item file used in IE-modding

The IESDP is always a good source, but for BG1 triggers and actions, it only reflects BG1:TotSC, and some of the used examples contain BGII only syntax.


1. General

BAMs: 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: Unsplit BAMs work as intended in BG1:TotSC. (Split BAMs work also 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 rest
NPCs 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~

 

CHAIN woes: Dialogues scripted using CHAIN don't work as in BGII. I experienced both the following:

1. CHAIN, followed by reply options, does only work properly if the last speaker (dlg) is the same as the first one. Otherwise, the reply options will show after the first line in combination with a "continue" button. If the CONTINUE is clicked on, the interjections show, but the reply options are shown in every line, too.

2. A CHAIN followed by reply options will skip all other CHAIN lines in between completely, unless the reply options are moved into a separate dialogue state (see here for an example how to prevent it).

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 PartyRested() trigger simulation, 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 NPCs
In 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 Baronius

Player 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 timers
RealGlobalTimerExpired() 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. But, GLOBAL are limited to 1024 for BG(TotSC) and 512 for plain BG. This leads to serious difficulties to finish a modded game. For this, the BG1 engine FIX - extending limit of maximum GLOBAL variables alters the exe to increase the amount of GLOBAL variables to 4096.

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.


2. Triggers

Alignment triggers: MASK_GOOD, MASK_EVIL and MASK_NEUTRAL do not seem to work for BG1 at all. Further, the single "alignmen.ids" identifiers ("LAWFUL_GOOD","CHAOTIC_NEUTRAL", etc.) if used like this don't seem to work, either. For checking the alignment of a creature, the (correct) numbers have to be used:

0 NONE
17 LAWFUL_GOOD
18 LAWFUL_NEUTRAL
19 LAWFUL_EVIL
33 NEUTRAL_GOOD
34 NEUTRAL
35 NEUTRAL_EVIL
49 CHAOTIC_GOOD
50 CHAOTIC_NEUTRAL
51 CHAOTIC_EVIL

Thus, to check for a CHAOTIC_GOOD PC, the trigger would be "Alignment(Player1,49)".

AreaCheck() does not exist. Have a look at Zed Nocear's Trigger-Simulations for BG1 for a neat workaround for this.

AreaType() does not exist. Have a look at Zed Nocear's Trigger-Simulations for BG1 for a neat workaround for this.

Class check: class.ids does not contain the _ALL identifiers ("MAGE_ALL" etc.) known from BGII. For BG1, only the plain class identifiers are available (for example "MAGE").

CombatCounter(0) does not exist. Have a look at Zed Nocear's Trigger-Simulations for BG1 for a possible workaround.

IsGabber(O:Object*) does not exist.

Level(), including LevelGT() and LevelLT() do not exist. "CheckStatLT(Myself,10,LEVEL)" might work, but I have no experience with this. As avenger_RR noted: "Note however, that LEVEL, LEVEL2 and LEVEL3 are different values and are used for multi and dual-classed characters".

OR() does not exist.
To minimize the amount of necessary script blocks for a large amount of "OR()" triggers, there are workarounds possible. See tutorial of Vlasak for coding helps concerning OR() and AND(): AND and OR in scripting, How to simulate them in various IE games
From my experience, I want to add: Always keep in mind, that scripts and dialogue files are executed from top to bottom. Take the following example, two dialogue blocks from Ajantis' player initiated dialogues. According to the alignments of the party members, different dialogues should be triggered:

 

//Original dialogue blocks as used in Tutu/BGT
IF //first block: no party member is of evil alignment
~Global("C#AjantisPCIniFlirtTime","GLOBAL",1) Global("X#AjantisRomanceMatch","GLOBAL",1) Global("X#AjantisRomanceActive","GLOBAL",1) !Detect([ENEMY])
!TimeOfDay(Night)
!Alignment(Player1,MASK_EVIL)
!Alignment(Player2,MASK_EVIL)
!Alignment(Player3,MASK_EVIL)
!Alignment(Player4,MASK_EVIL)
!Alignment(Player5,MASK_EVIL)
!Alignment(Player6,MASK_EVIL)
GlobalLT("X#AjantisRomanceBadDecision","GLOBAL",5)~ THEN BEGIN ajantisflirts_begin_01
SAY ~...~

IF //second block: at least one NPC is of evil alignment
~Global("C#AjantisPCIniFlirtTime","GLOBAL",1) Global("X#AjantisRomanceMatch","GLOBAL",1) Global("X#AjantisRomanceActive","GLOBAL",1) !Detect([ENEMY])
!TimeOfDay(Night)
!Alignment(Player1,MASK_EVIL)
OR(5)
Alignment(Player2,MASK_EVIL)
Alignment(Player3,MASK_EVIL)
Alignment(Player4,MASK_EVIL)
Alignment(Player5,MASK_EVIL)
Alignment(Player6,MASK_EVIL)
GlobalLT("X#AjantisRomanceBadDecision","GLOBAL",5)~ THEN BEGIN ajantisflirts_begin_02
SAY ~...~


First the MASK_EVIL has to be replaced as described above. Then, Instead of replacing the OR() in the second dialogue block in the example with a lot of new blocks, putting it last as unconditioned is equal to the wanted trigger condition, since the dialogue file will be executed from top to bottom, therefore the second block only will be triggered if the conditions of the first aren't met.

 

//Adapted dialogue blocks for BG1:
IF //first block: no party member is of evil alignment, BG1 syntax
~Global("C#AjantisPCIniFlirtTime","GLOBAL",1) Global("X#AjantisRomanceMatch","GLOBAL",1) Global("X#AjantisRomanceActive","GLOBAL",1) !Detect([ENEMY])
!TimeOfDay(Night)
!Alignment(Player1,19)
!Alignment(Player1,35)
!Alignment(Player1,51)
!Alignment(Player2,19)
!Alignment(Player2,35)
!Alignment(Player2,51)
!Alignment(Player3,19)
!Alignment(Player3,35)
!Alignment(Player3,51)
!Alignment(Player4,19)
!Alignment(Player4,35)
!Alignment(Player4,51)
!Alignment(Player5,19)
!Alignment(Player5,35)
!Alignment(Player5,51)
!Alignment(Player6,19)
!Alignment(Player6,35)
!Alignment(Player6,51)
GlobalLT("X#AjantisRomanceBadDecision","GLOBAL",5)~ THEN BEGIN ajantisflirts_begin_01
SAY ~...~

//second block, only triggers if one group member except player1 is of evil alignment
IF WEIGHT #-2
~Global("C#AjantisPCIniFlirtTime","GLOBAL",1) Global("X#AjantisRomanceMatch","GLOBAL",1) Global("X#AjantisRomanceActive","GLOBAL",1) !Detect([ENEMY])
!TimeOfDay(Night)
!Alignment(Player1,19)
!Alignment(Player1,35)
!Alignment(Player1,51)
GlobalLT("X#AjantisRomanceBadDecision","GLOBAL",5)~ THEN BEGIN ajantisflirts_begin_02
SAY ~...~


PartyRested() does not exist. To trigger a dialogue directly after rest, have a look at Zed Nocear's Trigger-Simulations for BG1 for a neat workaround for this.

XP(), including XPGT() and XPLT() do not exist. For checking the amount of XP an NPC has, a check of the form "CheckStatLT(Myself,161000,XP)" can be used instead.



3. Actions

Here, some difference between BG1 and BG1:TotSC have to be considered. They are pointed out accordingly.

ApplySpellRES() works for both BG1 and BG1:TotSC (by Zed Nocear).
The Action.ids has to be patched accordingly:

APPEND ~action.ids~ ~160 ApplySpellRES(S:ResRef*,O:Target*)~
UNLESS ~ApplySpellRES~

CreateCreature() For BG1:TotSC, the syntax for CrateCreature() is the same as in BGII: "CreateCreature("name",[x.y],0)", with "0" being the face orientation of the creature.
For BG1 without TotSC, there is no face orientation: "CreateCreature("name",[x.y])" is the correct syntax here.
To code for both in one file, the method described by cmorgan's "Crossing the Great Divide" tutorial using OUTER_SPRINT has to be used (see here for more details: crossing the great divide: Coding for BG1, Tutu and BGT).

CreateCreatureObjectOffScreen() does not exist. This action is used in BGII to create creatures outside the visual range of the group, but in BG1, this action is not available.

CreateItem() doesn't work for containers: The item lands on the ground in front of the container.
Normally, creating an item in a container would be done via area script, for example:
IF
Global("C#ItemInChest","GLOBAL",0)
THEN
RESPONSE #100
SetGlobal("C#ItemInChest","GLOBAL",1)
ActionOverride("Container1",CreateItem("C#ITEM",0,0,0))
END
Unfortunately, this does not work for BG1: the item is not created inside the container, but in front on the ground.

CreateVisualEffectObject() does not exist.

EraseJournalEntry() is not available/ does not exist. To cross-code for Tutu/BGT and BG1, and make the EraseJournalEntry() available to the Tutu/BGT versions, see here for more details: crossing the great divide: Coding for BG1, Tutu and BGTl.

EscapeAreaMove(S:Area*,I:X*,I:Y*,I:Face*) does not exist. A very simple work around for unjoinable NPCs would be, to destroy the creature at one place and recreate it at another, using for example
~MoveToPoint(), Destroyself()~ and then ~CreateCreature()~ at the new location (remember that possible local variables of the first encounter are lost then, like "NumTimesTalkedTo").
Zed Nocear suggests the following for BG1:TotSC: ~RunAwayFrom(LastTalkedToBy,60) LeaveAreaLUA("AR4801","",[284.454],0)~
I don't know whether LeaveAreaLUA() would work in BG1 (without TotSC), though.

FaceObject() does not exist.
If you know where the two characters that should face each other are standing (either they were created or moved to another area with known coordinates) "Face()" could be used to let them turn.
If you don't know where the chracters are going to be, devSin proposed a possible workaround which I haven't checked yet, though: "You could always do something (...) like have your NPC ReallyForceSpell(Player1,FAKE_SPELL_DOES_NOTHING) Wait(1) StartDialog("DLG",Player1). Spellcasting should have the character zip around to face the target (you have to create the fake spell, though)."

FindTraps() does not work. Instead, it seems to disables the "find trap" modus, if it was chosen by the player by clicking on the game icon. If used in scripts, the thief would be unable to use the "find traps" modus as it gets repeatedly disabled. (by Zed Nocear)

GivePartyGoldGlobal(S:Name*,S:Area*) does not work as intended. IESDP: "Will give the party a sum of gold corresponding to the given global variable. The gold amount is deducted from the actor running the script." In BG1, the amount specified by the value of the global variable will be ignored, and all gold of the creature running the script will be transferred. (by Zed Nocear)

Journal entries:
only JOURNAL. There exists no "SOLVED_JOURNAL" or "UNSOLVED_JOURNAL" as known from the BGII engine (To cross-code for Tutu/BGT and BG1, see here for more details: crossing the great divide: Coding for BG1, Tutu and BGT)

Journal entries as actions: AddJournalEntry(~text~). For BGII engine, the syntax is AddJournalEntry(~text~,QUEST_DONE) for solved journal, and AddJournalEntry(~text~,QUEST) for unsolved quests. (To cross-code for Tutu/BGT and BG1, see here for more details: crossing the great divide: Coding for BG1, Tutu and BGT)

107 MoveToOffset(P:Offset*) does not work in BG1 and BG1:TotSC. If used, nothing will happen.

RestParty() does not exist.
I use the following Workaround:
1a. In a tavern, for BG1:TotSC:
A cutscene is used, that blackens the screen for a moment (there is no "rest at inn" movie in BG1, so in this case blackening the screen would be sufficient):

 

ClearAllActions()
StartCutSceneMode()
CutSceneId(Player1)
FadeToColor([20.0],0)
Wait(5)
Rest()
ActionOverride(Player2,Rest())
ActionOverride(Player3,Rest())
ActionOverride(Player4,Rest())
ActionOverride(Player5,Rest())
ActionOverride(Player6,Rest())
Wait(10)
FadeFromColor([20.0],0)
Wait(1)
EndCutSceneMode()

1b. In a tavern, BG1 without TotSC: For BG1 without TotSC, FadeToColor() is not available. I helped myself by triggering the "resting outdoor" movie also for an inside rest. See 2. for the cutscene.

2. For resting outdoor, the "resting outdoor" movie can be triggered (BG1 with or without TotSC😞

 

ClearAllActions()
StartCutSceneMode()
CutSceneId(Player1)
Rest()
ActionOverride(Player2,Rest())
ActionOverride(Player3,Rest())
ActionOverride(Player4,Rest())
ActionOverride(Player5,Rest())
ActionOverride(Player6,Rest())
StartMovie("REST")
Wait(1)
EndCutSceneMode()


SetPlayerSound() does not exist. I don't know any workaround, so this means that there is no possibility to provide a "fix-it" script to restore the creature's sounds in BG1.

StartDialogNoSet() does not exist. Dialogue() has to be used instead.

TakePartyItem(S:Item*) and
TakePartyItemNum(S:ResRef*,I:Num*) should both be used with care: Stackable items in the inventory are treated as one and all are items of the stack are removed. (by Zed Nocear)

4. Spell Effects

#122 (0x7a) Item: Create Inventory Item [122] does not work as in BGII: It will always only create one item, and only, if timing mode is set to "Delay/Permanent (4)" and number of items is set to "0". (by Zed Nocear)


5. Tokens

Not all BGII Tokens are available.

I repost the list from the IESDP for BG1:TotSC here:

<CHARNAME> Returns the name of the PC.
<DAY> Returns the current numerical day.
<DAYANDMONTH> Returns the current numerical day as well as the month. (Example: It is <DAYANDMONTH>, would produce: It is 24 Mirtul...or whatever the current day and month happens to be.)
<DURATION> Returns the elapsed time from the start of the game in days and hours. (Example: We've been around for <DURATION>, would produce: We've been around for 23 days and 13 hours...or whatever the elapsed time happens to be.)
<DURATIONNOAND> Returns the same thing as <DURATION> except it omits the and. So it would be 23 days 13 hours rather than 23 days and 13 hours.
<GABBER> Returns the name of the current speaker. (Example: If I use Jaheira and click-talk her on a creature rather than using the PC, this would return Jaheira if used in a dialogue.)
<GAMEDAY> Returns the current game day. (Starts at 1 for a new game.)
<GAMEDAYS> Returns the number of game days that have elapsed since the start of the game. (Starts at 0 for a new game.)
<HOUR> Returns the current hour of the day in numerical 24 hour format.
<MINUTE> Returns the current number of real-time minutes (0-59) that have passed in the last hour.
<MONTHNAME> Returns the current month's name. (Example: It is <MONTHNAME>, would produce: It is Mirtul...or whatever the current month is in your game.)
<YEAR> Returns the current year in numerical format. (Example: It is <YEAR> currently, would produce: It is 1369 currently...or whatever year it is in your game.

I am not sure whether all tokens will work for BG1 without TotSC, but the tokens <LADYLORD> and <PRO_LADYLORD> definitely do not work for BG1 but return empty spaces.

Link to comment

Wow. This is huge...

 

You might want to put a sort of "Contents" at the start of this, like

Contents

1. General

2. Triggers

etc.

I don't think you can use hyperlinks unless you split the topics into separate posts, but at least it'll give people an idea of what this contains.

 

I think you mentioned elsewhere that BG1 description BAMs have to be quartered - you can't use the unsplit BG2 description BAMs.

 

BG1 items do not have proficiencies - they show up as "Large Sword" (no proficiency) in BG2 editors. Instead, the proficiency is determined by the Item Type which in turn is related to the proficiency flags in the .cre file.

 

Also, BG1 cannot enforce minimum stat requirements on items, as Echon has noted. And kit usability flags on items are pretty much ignored I think. I mentioned some things to look out for if you are going the other way (BG1 to BG2) here.

Link to comment

Yes, I was wondering about a content list myself, and a useful way to link to the separate chapters. You say there is no way to link inside a post?

 

I think you mentioned elsewhere that BG1 description BAMs have to be quartered - you can't use the unsplit BG2 description BAMs.
I forgot about that one! Good point, I will add it as soon as possible.

 

Thank you for the information about items, and the link. Will be integrated, too.

 

EDIT: spelling!

Edited by jastey
Link to comment

Splitting is a good idea, anywhy, since other boards don't accept such long posts. I don't have the rights here, though, but if any moderator wants to split the comments into a "comments on.." thread, I would appreciate (for later redesign as suggested).

Link to comment
Guest Guest
I think you mentioned elsewhere that BG1 description BAMs have to be quartered - you can't use the unsplit BG2 description BAMs.

 

In my experience the unsplit description BAMs from BG2 work fine in BG1+TotSC, only for pure BG1 they have to be quartered, otherwise they make CTD

Link to comment
Guest Zed Nocear
I think you mentioned elsewhere that BG1 description BAMs have to be quartered - you can't use the unsplit BG2 description BAMs.

 

In my experience the unsplit description BAMs from BG2 work fine in BG1+TotSC, only for pure BG1 they have to be quartered, otherwise they make CTD

Link to comment

I could have sworn I had a CTD or at least a wrong shown description BAM in BG1:TotSC. If you say it's your experience, I will change that line, though.

 

EDIT: Tutorial is updated concerning quatering description BAMs in BG1:TotSC (where it is not necessary, only in plain BG1).

Edited by jastey
Link to comment
WeiDU offers ADD_GAME_NPC (please refer to the WeiDU-readme), but with ADD_GAME_NPC all BALDUR.GAM files in SAVE and MPSAVE get altered, which is... cruel.

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.

- "face orientation" can be used.

I'm trying to decide which method (ADD_GAME_NPC or the code above) is less of a disadvantage. I'm leaning toward ADD_GAME_NPC as long as either the readme or the install makes it clear the user should back up their saves first.

 

But are you saying npclevel.2da doesn't work in BG1(TotSC)? If not, why's it there?

 

The facing I can live without, or I guess it could be scripted if it's important.

CreateItem() doesn't work for containers: The item lands on the ground in front of the container.

Normally, creating an item in a container would be done via area script, for example:

IF

Global("C#ItemInChest","GLOBAL",0)

THEN

RESPONSE #100

SetGlobal("C#ItemInChest","GLOBAL",1)

ActionOverride("Container1",CreateItem("C#ITEM",0,0,0))

END

Unfortunately, this does not work for BG1: the item is not created inside the container, but in front on the ground.

Does this work in TotSC? I have a macro from Ascension64 that simulates CreateItem() in a container, but it's kind of long... Still, if your BG1 mod is enforcing that a new game must be started anyway, it might be useful.
Link to comment
if your BG1 mod is enforcing that a new game
Not BGQE, where I would have needed this, but I found another workaround.

 

But are you saying npclevel.2da doesn't work in BG1(TotSC)?
Where do you read that? I think it works fine, and with the NPC creation possibility introduced here it gets applied for the mod NPC, as well.
Link to comment
if your BG1 mod is enforcing that a new game
Not BGQE, where I would have needed this, but I found another workaround.
You did? I thought you decided not to put items in a container, which isn't really much of a workaround if a BG1 modder does want to do that.

 

Edit: So do you know if it works for TotSC or is it just invalid for BG1 w/o TotSC?

But are you saying npclevel.2da doesn't work in BG1(TotSC)?
Where do you read that? I think it works fine, and with the NPC creation possibility introduced here it gets applied for the mod NPC, as well.
It's here:
The advantages are:

...

- the NPC-CRE-changes as defined by NPCLEVEL.2DA get applied, depending on the level of the PC.

Saying this is an advantage for Zed's method implies it isn't the case for the existing (ADD_GAME_NPC) method. Edited by Miloch
Link to comment

No, CreateItem bumps the item in front of the container for TotSC, also.

 

Ah, you mean for ADD_GAME_NPC the NPCLEVEL.2DA doesn't get applied. Zed Nocear would have to say something to that, I have no idea (I'll give him a note).

Or maybe someone else knowing about ADD_GAME_NPC is still around. I assume the WeiDU-readme doesn't say anything to that?

Link to comment
No, CreateItem bumps the item in front of the container for TotSC, also.
Damn. Well I did some code stealing and came up with a routine that patches the .are container if BG1 and scripts it otherwise. The macro is from Ascension64. The actual platform dependent code without the macro (which you can shove off into an external library) is pretty minimal.
DEFINE_PATCH_MACRO are_update_offset BEGIN
 READ_LONG 0x54 actor_off
 READ_LONG 0x5c info_off
 READ_LONG 0x60 spawn_off
 READ_LONG 0x68 entr_off
 READ_LONG 0x70 ctnr_off
 READ_SHORT 0x74 ctnr_num
 READ_LONG 0x78 itm_off
 READ_LONG 0x7c vertex_off
 READ_LONG 0x84 ambient_off
 READ_LONG 0x88 var_off
 READ_LONG 0xa8 door_off
 READ_LONG 0xa0 explored_off
 READ_LONG 0xb0 anim_off
 READ_LONG 0xb8 tiled_off
 READ_LONG 0xbc song_off
 READ_LONG 0xc0 rest_off
 READ_LONG 0xc4 automap_off
 FOR (i = 0; i < ctnr_num; i += 1) BEGIN
READ_ASCII (ctnr_off + i * 0xc0) ctnr_name (12)
PATCH_IF (~%ctnr_name%~ STRING_MATCHES_REGEXP ~%ctnr%~ = 0) BEGIN
  READ_LONG (ctnr_off + i * 0xc0 + 0x40) ctnr_itm_idx
  INSERT_BYTES (itm_off + ctnr_itm_idx * 0x14) 0x14 //Add item
  WRITE_ASCIIE (itm_off + ctnr_itm_idx * 0x14) ~%additm%~ //item ResRef
  READ_SHORT 0x76 itm_num //Update global and ctnr item count
  WRITE_SHORT 0x76 (itm_num + 1)
  READ_LONG (ctnr_off + i * 0xc0 + 0x44) ctnr_itm_num
  WRITE_LONG (ctnr_off + i * 0xc0 + 0x44) (ctnr_itm_num + 1)
  FOR (j = i + 1; j < ctnr_num; j += 1) BEGIN //Update item indices in all ctnrs
	READ_LONG (ctnr_off + j * 0xc0 + 0x40) ctnr_itm_idx
	WRITE_LONG (ctnr_off + j * 0xc0 + 0x40) (ctnr_itm_idx + 1)
  END
  SPRINT arx_dt_off ~%arx_dt%_off~
  SPRINT arx_dt_num ~%arx_dt%_num~
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~actor~)=0))
			AND ((actor_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((actor_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0x54 (actor_off + arx_incr)
  END
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~info~)=0))   
			AND ((info_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((info_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0x5c (info_off + arx_incr)
  END
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~spawn~)=0))
			AND ((spawn_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((spawn_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0x60 (spawn_off + arx_incr)
  END
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~entrance~)=0))
			AND ((entr_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((entr_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0x68 (entr_off + arx_incr)
  END
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~ctnr~)=0))
			AND ((ctnr_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((ctnr_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0x70 (ctnr_off + arx_incr)
  END
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~itm~)=0))
			AND ((itm_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((itm_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0x78 (itm_off + arx_incr)
  END
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~vertex~)=0))
			AND ((vertex_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((vertex_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0x7c (vertex_off + arx_incr)
  END
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~ambient~)=0))
			AND ((ambient_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((ambient_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0x84 (ambient_off + arx_incr)
  END
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~variable~)=0))
			AND ((var_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((var_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0x88 (var_off + arx_incr)
  END
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~door~)=0))
			AND ((door_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((door_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0xa8 (door_off + arx_incr)
  END
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~explored~)=0))
			AND ((explored_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((explored_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0xa0 (explored_off + arx_incr)
  END
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~anim~)=0))
			AND ((anim_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((anim_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0xb0 (anim_off + arx_incr)
  END
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~tiled~)=0))
			AND ((tiled_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((tiled_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0xb8 (tiled_off + arx_incr)
  END
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~song~)=0))
			AND ((song_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((song_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0xbc (song_off + arx_incr)
  END
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~rest~)=0))
			AND ((rest_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((rest_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0xc0 (rest_off + arx_incr)
  END
  PATCH_IF (NOT ((~%arx_dt%~ STRING_COMPARE_CASE ~automap~)=0))
			AND ((automap_off > EVALUATE_BUFFER ~%%arx_dt_off%%~)
			OR ((automap_off = EVALUATE_BUFFER ~%%arx_dt_off%%~) AND (EVALUATE_BUFFER ~%%arx_dt_num%%~ = 0))) BEGIN
	WRITE_LONG 0xc4 (automap_off + arx_incr)
  END
END
 END
END

////////////////////////////////////////////////////////////////
BEGIN ~AddItem~

<<<<<<<< .../additem-inlined/t-ar2616.baf
IF
 Global("T-Potn03","FW2616",0)
THEN
 RESPONSE #100
ActionOverride("Container 1",CreateItem("_POTN03",0,0,0))
SetGlobal("T-Potn03","FW2616",1)
END
>>>>>>>>

ACTION_IF FILE_EXISTS_IN_GAME ~ar2616.are~ THEN BEGIN //BG1
 COPY_EXISTING ~ar2616.are~ ~override~ //Candlekeep Inn
PATCH_IF (SOURCE_SIZE > 0x11b) BEGIN //Invalid file protection
  SPRINT arx_dt ~itm~ //ARE update offset datatype
  SET arx_incr = 20 //ARE update offset increment
  SPRINT ctnr ~Container 1~ //Container name
  SPRINT additm ~POTN03~ //Item to add
  LAUNCH_PATCH_MACRO are_update_offset
END
 BUT_ONLY_IF_IT_CHANGES
END ELSE BEGIN //Tutu
 ACTION_IF FILE_EXISTS_IN_GAME ~fw2616.are~ THEN BEGIN //Tutu
EXTEND_BOTTOM ~_ar2616.bcs~ ~.../additem-inlined/t-ar2616.baf~
COPY_EXISTING ~fw2616.are~ ~override~ //Candlekeep Inn
  PATCH_IF (SOURCE_SIZE > 0x11b) BEGIN //Invalid file protection
	READ_ASCII 0x94 ~arsc~ //Area script
	PATCH_IF (~%arsc%~ STRING_EQUAL_CASE ~none~ = 0) OR (~%arsc%~ STRING_EQUAL ~~ = 0) BEGIN
	  WRITE_ASCII 0x94 ~_AR2616~
	END
  END
BUT_ONLY_IF_IT_CHANGES
 END
END

The inlined script can be external too, obviously. This adds a potion to Candlekeep Inn by way of example, using the best method possible based on your platform.

Ah, you mean for ADD_GAME_NPC the NPCLEVEL.2DA doesn't get applied. Zed Nocear would have to say something to that, I have no idea (I'll give him a note). Or maybe someone else knowing about ADD_GAME_NPC is still around. I assume the WeiDU-readme doesn't say anything to that?
The WeiDU tutorial doesn't say anything about it. The ideal "user-friendly" thing to do would be to ask the users if they want to install the mod for all saved games or for new games only. I guess I'll make that my next project... Edited by Miloch
Link to comment

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...