Jump to content

DavidW

Gibberlings
  • Posts

    8,009
  • Joined

  • Last visited

Posts posted by DavidW

  1. I've just released version 2.0.11, which fixes a few bugs and compatibility issues (notably, the nasty bug where you got granted the slayer power, lots of times, upon going to Watcher's Keep in Shadows of Amn). 

    Bugfixes:
    - Slayer power is no longer granted (and re-granted, and regranted...) if you go to Watcher's Keep in SoA
    - slightly tightened the script that grants Bhaalspawn powers
    - Blocked a (rare) scenario where Balthazar or another NPC ally could get resurrected, mid-battle, as a neutral
    - 'Slightly improved cutscenes' was erroneously requiring Enhanced Edition
    - Imoen and Sarevok's banters should now be less interruptable (thanks to Jastey for advice on this one)

    Compatibility fixes:
    - Imoen's NPC dialogs no longer lead to a stutter-bug clash with Imoen Romance
    - Removed compatibility code with Iylos and Edwin, since those mods have updated to work without it
    - Enforced the requirement that Iylos and Spell Revisions are installed after Ascension
    - Melissan's spear ('spermel') is now patched correctly (and no longer throws a WARNING) if the beta fixes from BG2 fixpack are installed

    Links:

    Download the mod

    Download at GitHub

    Project page

    Readme

    EDIT: updated to 2.0.12 - the only changes are to translations. (Notably, the Russian translation has been fully updated.)

    EDIT: updated to 2.0.13. Changelog:

    • Further Russian translation updates
    • Updated Detectable Spells
    • Guarded against Timestop detection variable not properly being deactivated

    EDIT: updated to 2.0.14. Changelog:

    • Fixed a bug that was preventing some non-English translation lines being used properly
    • Replaced outdated use of LONG_BOW as a synonym for MAGE_ALL in scripts (in rare cases this was causing compatibility issues with other mods)
  2. There are two separate bugs here.

    - The missing splstate.ids is probably because you are using an older version of BG2EE. Ascension 2.0 uses features of the more recent versions; update to 2.5 and try again.

    - cdheld.eff is added by the Fixpack, which you have installed, and it is quite hard to see how another mod could have removed it. Is it possible you're using an out-of-date version of Fixpack? (If some mod is removing that file, it's likely to break quite a lot of things, not just Ascension installation.)

  3. That's very bizarre. My best guess (if you're still around) is that the ascension.tra file has just got corrupted. Delete the mod folder; delete the contents of 'weidu_external/lang/ascension', re-download it, and try again.

  4. On 2/15/2020 at 1:54 AM, Bartimaeus said:

    I wish weidu didn't spit out "INSTALLED WITH WARNINGS" for this, since it can be a bit misleading. Basically, the mod expected to modify a file (specifically an item), but didn't end up doing so. That's all the warning is there for. It is almost 99% sure to be inconsequential.

    I'm glad it does; otherwise the modder doesn't notice bugs. In this case, it was a clash between the beta fix component of BG2 Fixpack and Ascension - not game-breaking, but Melissan's spear ends up more powerful than it's supposed to be. Now fixed, but I'd probably never have known about it without that warning.

  5. 2 hours ago, jastey said:

    I don't think it's also dependent whether we talk about a joinable or non-joinable NPC. As far as I know, local variables are not stored in the savegame for non-joinable characters, which makes their use for quests unreliable.

    Local variables are stored for non-joinables. (The engine represents them as an opcode-187 effect attached to the CRE file.)

  6. I think I have it fixed on my side; no need to do anything on yours.

    The problem with removing the checks entirely is that (since banters are called randomly from time to time) you might by chance get the banter implausibly early. I assume that's why Imoen Romance's original writer added them, and it's why I put them into Ascension. They just need to be coordinated - but I've done that (locally) in Ascension now, so I think you can leave Imoen alone for this one.

  7. I sort of feel this is the kind of thing that works okay on first implementation and then breaks a year later when some apparently-innocuous change gets made somewhere else. I think my suggestions of rewiring the Imoen dialog structure or, failing that, just enforcing that Imoen is installed first, are safer.

  8. Quote

    DEFINE_ACTION_FUNCTION RES_NUM_OF_SPELL_NAME_SAFE STR_VAR spell_name="" RET spell_num spell_res success

    BEGIN

            ACTION_IF IDS_OF_SYMBOL (spell "%spell_name%")>=0 BEGIN

                     LAF RES_NUM_OF_SPELL_NAME STR_VAR spell_name RET spell_num spell_res END

                     OUTER_SET success=1

            END ELSE BEGIN

                     OUTER_SET spell_num="-1"

                     OUTER_SPRINT spell_res "%spell_name%"

                     OUTER_SET success=0

            END

    END

     

     

    (Not tested.)

  9. 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).

  10. On Imoen romance compatibility:

    The problem is that as currently coded, Imoen romance needs to redirect Imoen redirections to new blocks: block 15 gets redirected to 'FD', blocks 18 and 19 get redirected to 'finalbreak'.

    I would be inclined to repurpose those hardcoded numbers for the new blocks instead: that is, in FinalConversation.d, I'd do

    Quote

    REPLACE imoen25j

    IF ~~ THEN 15
        SAY ~<CHARNAME>...~ [IRDusk]
        IF ~!Global("ImoenRomanceActive","GLOBAL",2) OR(2) Global("ImoenRomanceActive","GLOBAL",3) !Global("IRTBhaalImoen","GLOBAL",1)~ THEN GOTO imoen_15_old
        IF ~Global("ImoenRomanceActive","GLOBAL",2) Global("IRTImoenWarnedAboutLeaving","GLOBAL",1) !GlobalLT("IRTLoveMeter","GLOBAL",7)~ THEN GOTO FDWarned // Imoen was warned about the player probably leaving
        IF ~Global("ImoenRomanceActive","GLOBAL",2) !Global("IRTImoenWarnedAboutLeaving","GLOBAL",1) !GlobalLT("IRTLoveMeter","GLOBAL",7)~ THEN GOTO FDUnwarned // Imoen wasn't warned
        IF ~Global("ImoenRomanceActive","GLOBAL",2) !Global("IRTBhaalImoen","GLOBAL",1) GlobalLT("IRTLoveMeter","GLOBAL",7)~ THEN DO ~SetGlobal("IRTEpBreakup","GLOBAL",1) SetGlobal("ImoenRomanceActive","GLOBAL",5)~ GOTO FDBreakup // Love meter is too low, Imoen breaks up with the player
        IF ~!Global("ImoenRomanceActive","GLOBAL",2) !Global("ImoenRomanceActive","GLOBAL",3) Global("IRTBhaalImoen","GLOBAL",1)~ THEN DO ~SetGlobal("IRTCorruptProposal","GLOBAL",1)~ GOTO FDCorrupt // Imoen isn't romanced but the player corrupted her
    END

        IF ~~ THEN 18
           SAY ~Wow.~

        // Imoen not romanced, not corrupt (Vanilla responses - since priority is given from bottom to top, these will only be followed if none of the stuff below is met)
            IF ~Global("PlayerChoseEssence","GLOBAL",1) Global("ImoenRomanceActive","GLOBAL",0)~ GOTO imoen_18_old
            IF ~Global("PlayerChoseEssence","GLOBAL",0) Global("ImoenRomanceActive","GLOBAL",0)~ GOTO imoen_19_old
    <everything else in the current 'finalbreak' script block>

    END

    IF ~~ THEN 19

    <exact copy of the '18' block>

    END

    Then, in the APPEND bit, you also include

    Quote

    IF ~~ THEN imoen_15_old

      SAY #67917

      IF ~~ THEN GOTO 16
    END

    IF ~~ THEN imoen_18_old

      SAY #68088 
      IF ~True()~ THEN EXTERN ~FINSOL01~ 33
      IF ~IsValidForPartyDialog("Valygar")
    Global("ValygarRomanceActive","GLOBAL",2)~ THEN EXTERN ~VALYG25J~ 11
      IF ~IsValidForPartyDialog("Anomen")
    Global("AnomenRomanceActive","GLOBAL",2)~ THEN EXTERN ~ANOME25J~ 20
      IF ~IsValidForPartyDialog("Viconia")
    Global("ViconiaRomanceActive","GLOBAL",2)~ THEN EXTERN ~VICON25J~ 18
      IF ~IsValidForPartyDialog("Aerie")
    Global("AerieRomanceActive","GLOBAL",2)~ THEN EXTERN ~AERIE25J~ 20
      IF ~IsValidForPartyDialog("Jaheira")
    Global("JaheiraRomanceActive","GLOBAL",2)~ THEN EXTERN ~JAHEI25J~ 18
    END

    IF ~~ THEN imoen_19_old

      SAY #68091 
      IF ~~ THEN GOTO 20
    END

     

    (These are just renamed copies of imoen25j's blocks 15, 18, 19.)

    That lets you avoid any modifications to the solar's script and any problems with Ascension or extra NPCs, without needing to allow explicitly for Ascension and without having to restructure Imoen's dialog.

    The downside is that if some other mod wants to interject into Imoen's comment to the solar, it'll interject at slightly the wrong place. But that strikes me as an edge case, and less serious than the alternative (which risks Imoen's dramatically critical final lines being skipped entirely).

    Comments welcome from people who spend more time with NPCs than me. (@Jastey ?)

     

  11. @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.

  12. 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.

  13. 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.

  14. 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.

  15. 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.

  16. 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.

     

  17. 1 hour ago, jastey said:

    Ah, gotcha.

    I would just use STATE_WHICH_SAYS to identify the state numbers in case that suffices.

    Does STATE_WHICH_SAYS work here? The dialog lines are Ascension-added, after all: Iylos won't know which they are.

    I guess you could parse Ascension's tra files, but that's getting messy, particularly when non-English installs are allowed for.

    I may be missing something here; STATE_WHICH_SAYS isn't something I use much.

    In a moment I'll do a separate thread on how I'm currently managing interjections into Ascension; if it turns out there's a better method, I'd be happy to hear about it.

    As for Crossmod - it's of course up to you as Iylos's maintainer, but my inclination is not to - I don't think people think of Ascension as an NPC mod (and it isn't, really: it's just that Balthazar functions a bit like an NPC for the purposes of the final Solar conversation). As and when Iylos handles Ascension correctly, I'd be inclined to enforce the Ascension->Iylos install order, by telling Ascension not to install if Iylos is there already.

  18. 6 hours ago, jastey said:

    Although question is: why does Ascension move around existing dialogue states at all? It sounds as if dialogues are doubled, which usually leads to problems with compatibility.

    I’m not moving existing dialog states. Iylos interjects into dialog states that are added by Ascension.

    I’ll describe how to do that on new Ascension shortly.

  19. Longer Road

    LR interacts quite significantly with Ascension, and there will definitely be problems - but it's too complex for me to realistically do much about it, and its current maintainer (Roxanne) doesn't participate on these boards and has a history that makes me reluctant to reach out. It seems to have been updated to allow for 2.0 but I'm not in a position to comment on whether those updates are successful.

    Best guess at current status: I don't know. (But install Ascension first.) 

  20. Wheels of Prophecy

    This is a bit of a mess. WoP (which I also wrote) is a horrible mess of spaghetti code written when I had much less idea how to organize complex mods. There are clearly significant bugs in its logic but I can't tell which are internal to WoP and which are clashes with new Ascension. I need to do some serious surgery on it; it's at the top of my to-do list but it's difficult to find time.

    Best guess at current status: Use at own risk. (Install Ascension first: Ascension itself will enforce this.)

  21. Iylos

    Iylos is now updated to be fully compatible with Ascension 2.0

    Iylos interjects into a couple of Balthazar's dialogs if Ascension is installed. That's done by hardcoded dialog numbers, which worked fine when Ascension was a big hardcoded block, but doesn't work for Ascension 2.0: some the dialog numbers have changed, and they aren't guaranteed to be the same on every install (install order may affect them, now that the main component is broken down into smaller pieces).

    If Iylos is installed first, Ascension actually adds those interjections back in itself; this seems to work fine (it does assume that the player has not removed the Iylos folder from the BG2 directory after installing Iylos, but I think that's a reasonable assumption). If Ascension is installed first, I suspect Iylos's existing hardcoded interjections will come out wrong. (They're all Interjects, so I don't think they can break anything else, but they might happen in inappropriate places.)

    It shouldn't be too difficult for Iylos's maintainer (Jastey?) to borrow Ascension's code to allow Iylos to interject into Ascension if installed after. (It does require one of Ascension's function libraries, though.) Let me know if this is of interest and I'll talk you through it.

    Best guess at current status: Compatible if Ascension is installed first (and Ascension will enforce this order).

  22. Imoen Romance

    The most recent release of Imoen Romance should be fully compatible with Ascension 2.0.

    This is a bit complex because it redirects various of Imoen's dialog lines in finsol01.dlg to new lines. If Ascension is installed after Imoen Romance, that's not a problem: Ascension 2.0 uses interject-copy-trans and works around it fine. But it's a problem the other way around: if you install Ascension first then in some circumstances Imoen's new dialog lines are skipped. Specifically, if Balthazar (or, as of 2.0, non-in-party, good-aligned Sarevok) interjects, and the next interject should be Imoen, you get the old version.

    This is being addressed by the Imoen Romance maintainer.

     

    Best guess at current status: Fully compatible; install in either order.

×
×
  • Create New...