Jump to content

Interjections into Ascension


Recommended Posts

Later edit: Jastey has a way better way to do this, using STATE_WHICH_SAYS (below). I'll leave this up for interest, but do it Jastey's way, not mine.

Various mods (notably Longer Road and Iylos) add interjections into Ascension's dialogs. That makes sense: Ascension is basically a modified version of Chapter 10, which is important and dramatic; it's very widely-used; why not interject into it? This thread is about some technical issues in doing so and how to overcome them. It's possible that there are much better methods than this one, so it's also an invitation for suggestions. I'm going to use Iylos as an example.

So: Iylos includes these bits of banter with Balthazar and Irenicus at the Throne of Bhaal:

Quote

// Jon Irenicus - Ascension
I_C_T IRENIC2 12 LK#IylosJonIrenicusComment
  == LK#IYLJ IF ~InParty("Iylos") InMyArea("Iylos") !StateCheck("Iylos",CD_STATE_NOTVALID)~ THEN @0
  == IRENIC2 @1
  == IRENIC2 @2
END

// Balthazar - Ascension
I_C_T BALTH2 5 LK#IylosBalth2Comment
  == LK#IYLJ IF ~InParty("Iylos") InMyArea("Iylos") !StateCheck("Iylos",CD_STATE_NOTVALID)~ THEN @3
  == BALTH2  @4
  == LK#IYLJ @5
  == BALTH2  @6
  == BALTH2  @7
END


// Balthazar - Ascension
I_C_T BALTH2 9 LK#IylosBalth2Comment2
  == LK#IYLJ IF ~InParty("Iylos") InMyArea("Iylos") !StateCheck("Iylos",CD_STATE_NOTVALID)~ THEN @8
  == BALTH2  @9
  == LK#IYLJ @10
  == BALTH2  @11
END

The blocks into which he interjects (5 and 9 for Balthazar, 12 for Irenicus) are hardcoded, which worked fine with original Ascension, since its dialogs were built in one big step. It's not as reliable in 2.0, because it's modular: different bits might be added in different orders, depending on component installation order,  and I don't want to commit to permanently-unchanging block structure. 

The trick is to parse the ascension-added (or ascension-modified) dialog to determine the blocks to add into. I'll explain my method below.

 

Link to post

I use a function called FIND_DLG_BLOCK, which is a variant of the STATE_WHICH_SAYS function in WEIDU. It's included in the alter_dlg.tpa library in Ascension (in the 'lib' subfolder), so first you have to INCLUDE it. That is: put the alter_dlg.tpa library somewhere in your mod folder (I'll assume you put it in a lib subfolder) and then put a WEIDU INCLUDE command into your tp2, like this:

Quote

INCLUDE "%MOD_FOLDER%/lib/alter_dlg.tpa"

Then you use it like this:

Quote

      LAF FIND_DLG_BLOCK
                STR_VAR dialog=balth2
                        match=~SetGlobal("FinalFight","AR6200",5)~
                RET iylos_balth2_1=block_number
      END

It decompiles the 'dialog' block, and returns the block number of the block that contains the 'match' string (which has to be chosen to return a unique block). Now the variable 'iylos_balth_2' is set to the number of the block containing 'SetGlobal("FinalFight","AR6200",5)'.

In some cases, the existing dialog doesn't have a very convenient 'match' string. In that case, Ascension adds 'hooks': variable checks that never actually apply but allow the block to be identified uniquely. The block in irenic2.dlg, for instance, has a check for 'Global("w_irenicus_block_12_id","GLOBAL",1)'. That variable is never actually checked, and the block is never used as an initial block so its IF ~~ condition is irrelevant, but it lets the block be identified uniquely by FIND_DLG_BLOCK:

Quote

      LAF FIND_DLG_BLOCK
                STR_VAR dialog=irenic2
                        match=dw_irenicus_block_12_id
                RET iylos_irenicus=block_number
      END
 

I've added hooks like this to those blocks that are used by interjections I know about (mostly Iylos). I can add others if people need them. (The hooks are in the current repo version but not the latest release.)

Occasionally it's a bit more involved, because the hook is attached to the top of a CHAIN. Here's how it works for the second Balthazar trigger:

Quote


      LAF FIND_DLG_BLOCK
                STR_VAR dialog=finsol01
                        match=~dw_balth_happy_id~
                RET solar_block=block_number
      END
      LAF FIND_DLG_BLOCK
                STR_VAR dialog=balth2
                        match="EXTERN ~FINSOL01~ %solar_block%"
                RET iylos_balth2_2=block_number
      END
 

Now we have the dialog blocks we need; the next step is to modify the interjection dialog to reference them.

Link to post

To do that, we just replace the hardcoded state numbers in the .d file with the new variables, like this:

Quote

// Jon Irenicus - Ascension
I_C_T IRENIC2 %iylos_irenicus% LK#IylosJonIrenicusComment
  == LK#IYLJ IF ~InParty("Iylos") InMyArea("Iylos") !StateCheck("Iylos",CD_STATE_NOTVALID)~ THEN @0
  == IRENIC2 @1
  == IRENIC2 @2
END

// Balthazar - Ascension
I_C_T BALTH2 %iylos_balth2_1% LK#IylosBalth2Comment
  == LK#IYLJ IF ~InParty("Iylos") InMyArea("Iylos") !StateCheck("Iylos",CD_STATE_NOTVALID)~ THEN @3
  == BALTH2  @4
  == LK#IYLJ @5
  == BALTH2  @6
  == BALTH2  @7
END


// Balthazar - Ascension
I_C_T BALTH2 %iylos_balth2_2% LK#IylosBalth2Comment2
  == LK#IYLJ IF ~InParty("Iylos") InMyArea("Iylos") !StateCheck("Iylos",CD_STATE_NOTVALID)~ THEN @8
  == BALTH2  @9
  == LK#IYLJ @10
  == BALTH2  @11
END

And we need to make sure to use EVALUATE_BUFFER when COMPILEing the dialog, so that the variables are swapped with their values.

Link to post

There's one other wrinkle: because the previous version of Ascension was basically an all-or-nothing install, all your mod had to do was check for Ascension (or some Ascension-specific file). That's unreliable for 2.0, because it's broken down into smaller parts. In most cases, what matters for interjections is that the chapter 10 rewrite is separated out from Balthazar's redemption, so that you can install one but not the other. That means that your mod has to be a bit more fine-grained as to which bit of Ascension it's interjecting into.

In the case of Iylos, the Irenicus block is an interjection into Irenicus's dialog when Mel summons him, so it's part of 'Chapter 10 rewrite' and doesn't have anything to do with Balthazar. So that block needs to be compiled, and the relevant FIND_DLG_BLOCK run, if the main component is installed. (You can check for it with MOD_IS_INSTALLED, using ID_OF_LABEL if you don't trust the Ascension component numbers to be invariant - though I think I can promise that they will be.)

The second Balthazar block, conversely, interjects into Balthazar's final conversation with the Solar, which is part of the 'redeem Balthazar' component and is independent of the Chapter 10 rewrite. So this component needs to check for that component.

The first Balthazar block interjects into one of his conversations in the middle of deactivating the energy pools. That's specific to the Ascension chapter-10 rewrite, but also involves Balthazar, so this one should only be included if both components are installed.

I recommend doing an install test of your mod in all three configurations: chapter 10 rewrite without redeemed Balthazar; redeemed Balthazar without chapter 10 rewrite; both together.

Link to post

Thanks! I did it using STATE_WHICH_SAYS:

Spoiler

 

    // Ascension crossmod dialogue

/* Ascension component "Ascension main component: rewritten Chapter 10" - DESIGNATED 0 */
    ACTION_IF MOD_IS_INSTALLED ~Ascension.tp2~ 0 BEGIN
      ACTION_IF GAME_IS ~bgt tob~ THEN BEGIN
      // Get state for IRENIC2 %iylos_irenic2%
/* @456 = ~So my ungrateful sister, you finally stand by my side. I shall deal with your treachery afterwards. As for you, <CHARNAME>, Melissan has granted me the power to rip the divinity from your body. The first thing I shall feel in an eon shall be pleasure as I watch your corpse turn to dust.~ */
        OUTER_SET iylos_irenic2 = STATE_WHICH_SAYS 456 IN ~ascension/lang/%s/iren_bodhi.tra~ FROM ~IRENIC2~
      // Get state for BALTH2 %iylos_balth2_1%
/* @1007 = ~She has, no doubt, guarded the pools in some fashion... but it is the only way to continue that I can think of. Short of standing here and watching her become a goddess, that is.~ */
        OUTER_SET iylos_balth2_1 = STATE_WHICH_SAYS 1007 IN ~ascension/lang/%s/pool_banter.tra~ FROM ~BALTH2~
      END ELSE BEGIN
      // Get state for IRENIC2 %iylos_irenic2%
/* @456 = ~So my ungrateful sister, you finally stand by my side. I shall deal with your treachery afterwards. As for you, <CHARNAME>, Melissan has granted me the power to rip the divinity from your body. The first thing I shall feel in an eon shall be pleasure as I watch your corpse turn to dust.~ */
        OUTER_SET iylos_irenic2 = STATE_WHICH_SAYS 456 IN ~weidu_external/lang/ascension/%s/iren_bodhi.tra~ FROM ~IRENIC2~
      // Get state for BALTH2 %iylos_balth2_1%
/* @1007 = ~She has, no doubt, guarded the pools in some fashion... but it is the only way to continue that I can think of. Short of standing here and watching her become a goddess, that is.~ */
        OUTER_SET iylos_balth2_1 = STATE_WHICH_SAYS 1007 IN ~weidu_external/lang/ascension/%s/pool_banter.tra~ FROM ~BALTH2~

      END

      COMPILE EVALUATE_BUFFER ~iylos/dialogue/crossmod/ascension.d~
    END

/* Ascension component "Redeemable Balthazar" - DESIGNATED 10 */
    ACTION_IF MOD_IS_INSTALLED ~Ascension.tp2~ 10 BEGIN
      ACTION_IF GAME_IS ~bgt tob~ THEN BEGIN
      // Get state for BALTH2 %iylos_balth2_2%
/* @1011 = ~And there is nothing that I ever wanted more. I choose to become a mortal gladly, and with all my heart. To walk the earth without that dark burden twisting my soul... I... I cannot even imagine...~ */
        OUTER_SET iylos_balth2_2 = STATE_WHICH_SAYS 1011 IN ~ascension/lang/%s/balth_finsol.tra~ FROM ~BALTH2~
      END ELSE BEGIN
      // Get state for BALTH2 %iylos_balth2_2%
/* @1011 = ~And there is nothing that I ever wanted more. I choose to become a mortal gladly, and with all my heart. To walk the earth without that dark burden twisting my soul... I... I cannot even imagine...~ */
        OUTER_SET iylos_balth2_2 = STATE_WHICH_SAYS 1011 IN ~weidu_external/lang/ascension/%s/balth_finsol.tra~ FROM ~BALTH2~
      END
      COMPILE EVALUATE_BUFFER ~iylos/dialogue/crossmod/ascension_10.d~
    END

 

This would fail in case there is more than one dialogue state using the lines which are used for detection.

Now I am pondering whether this could happen and I should switch to your version or just leave it.

Link to post

Well, yours is much simpler, so if it's robust, it's probably best to keep with it.

I'm a bit unclear how this works with multiple languages, or if Ascension and Iylos get installed with different languages.

There might be some EE/non-EE issues. You can insulate against that by checking against the tra file in ascension/lang on a vanilla install, and the tra file in weidu_external/lang/ascension on an EE install.

Link to post
10 minutes ago, DavidW said:

I'm a bit unclear how this works with multiple languages, or if Ascension and Iylos get installed with different languages.

That's the total fantastic part: STATE_WHICH_SAYS takes the language the mod it checks is installed in. So, Ascension could be installed in English and Iylos in Klingon and it would still pull the correct states. I checked this for one of my mods which has more language choices than the one it was interjecting into.

12 minutes ago, DavidW said:

There might be some EE/non-EE issues.

Is "weidu_external/lang/ascension" where the utf-8 transformed texts are copied to (and used by the installation)? Then I should probably take those for EE, yes.

Link to post
16 minutes ago, DavidW said:

so if it's robust

As I said, it fails if the line occurs multiple times inside the dlg. But I wouldn't know how that would happen for these cases.

Link to post

Terrific. I did not know that about STATE_WHICH_SAYS. Thanks.

I think it makes sense for me to enforce Ascension before Iylos, rather than replicate the Iylos code on my side - do you agree?

Link to post

Yes, that makes sense. It's a useful install order anyway, since Ascension adds more contents NPC mods might want to interject into. And you do not want to update Ascension every time an NPC mod gets updated.

Link to post

Hi, in order to make mod compatible with Balthazar interjection in final solar talk,  regardless of install order there is this problem in balth_finsol.d

INTERJECT_COPY_TRANS FINSOL01 29 balth_sol_2
== balth2 IF ~Global("BalthazarFights","GLOBAL",1)~ THEN @1017 END
INTERJECT_COPY_TRANS FINSOL01 30 balth_sol_2
== balth2 IF ~Global("BalthazarFights","GLOBAL",1)~ THEN @1017 END
INTERJECT_COPY_TRANS FINSOL01 31 balth_sol_2
== balth2 IF ~Global("BalthazarFights","GLOBAL",1)~ THEN @1017 END

 

This creates 3 identical state blocks in balth2 which seems redundant but more importantly makes it so i cannot use STATE_WHICH_SAYS 1017 as i would get returned multiple results.

I believe the tighter script would be to have finsol01 29,30 & 31 refer to the same (one) dialogue state in balth2 and not have duplicates.

Without this change its impossible to guarantee compatibility because balth2.dlg states cannot be reasonably targeted as depending on the order you install component 0 (rewrite chap 10) or 10 (balth redeem) the dialogue states in balth2 would change numbers.

 

 

 

 

 

Link to post

@Vanatos: They're not the same state block. The INTERJECT_COPY_TRANS blocks are being applied to different FINSOL01 blocks, and so (potentially) give different sets of responses, even though the actual line Balthazar speaks is the same.

Link to post

You can do it using the technology I discuss in the early part of this thread. It's a bit cumbersome, though, and it requires me to add some hooks for you (which isn't that easy for ICT). Have a look at my suggestion in the other thread. If that works for you (and no-one spots a problem), we're good. If not, we can talk about alternatives. (To be honest, it might at that point just be easier for Imoen to enforce an install order using FORBID_COMPONENT).

Link to post
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...