Skip to content

Fixing Turnabout's loading issues?

CirosanCirosan Member Posts: 25
Turnabout is plagued by a problem described in its readme:
Q: The pool didn't offer me anybody! Why?

A: Computers are much, much faster than they were when BG2 was first released
years ago, and the dialogue and scripting engines aren't very well synchronized
for modern PCs. If you hit Enter/Continue too quickly when "talking" to the
pool, you actually race ahead of our ability to set key variables, and the pool
thinks there are no valid targets to recall.

Q: The pool offered me choices to raise, but I could only answer "No" to them
and couldn't opt to raise no one! Why?

A: For a similar reason to the previous problem, unfortunately. The pool-
activation trigger is sometimes less than reliable, and if you click it several
times with no response, or skip too quickly through the introductory dialogue,
the game doesn't set an important variable for us correctly.

However, in my experience, no other mod really suffers from this. I suspect it's because Turnabout was never updated since its original release in 2005, and other, more recent mods take advantage of new scripting techniques or some such. I'm a total newbie to BG modding, but I have a long history of modding other games, and I want to polish this old gem to a more presentable state. What exactly would I have to do to resolve Turnabout's loading problems, such that modern computers don't break the code?

Comments

  • megamike15megamike15 Member Posts: 2,666
    yeah turnabout was never updated at all since it was made. there were plans for more characters to be added but that fell through.

    it was made ee compataible by someone else but it's not the best solution. i also find turnabout not that great mod as i find some characters besides gorion out of character as this was before we had stuff like bg 1 npc project that gave a consistent characterization to people like dynahair.
  • CirosanCirosan Member Posts: 25
    megamike15 wrote: »
    it was made ee compataible by someone else but it's not the best solution.

    Yes, I know. I was the one that did that. Now I want to go back and do a more thorough job.
  • jasteyjastey Member Posts: 2,671
    My experience is that inside a dialogue, you need one additional dialogue line to be executed before a variable is recognized as being set.
    Do you know what combination of dialogue and script actions the mod uses to determine the summoning?

    In the EE, there is also a flag that can be set so transactions are executed immediately. I think it was FLAGS 128 but I don't have much experience with it.
  • CirosanCirosan Member Posts: 25
    I do, sort of. All summoning is handled through summon.d, which I've dumped into pastebin for your convenience.

    Here's an example of how the summoning is handled. This plays upon the player interacting with their first pool:
    EXTEND_BOTTOM PPGUY02 1 // This is the first pool.
    IF ~~ THEN DO ~SetGlobal("T#SourceState","LOCALS",1)
    IncrementGlobal("T#Round","GLOBAL",1)~ GOTO Raise1
    END

    EXTEND_BOTTOM PPGUY02 2 // This is the second pool.
    IF ~~ THEN DO ~SetGlobal("T#SourceState","LOCALS",2)
    IncrementGlobal("T#Round","GLOBAL",1)~ GOTO Raise1
    END

    EXTEND_BOTTOM PPGUY02 3 // This is the third pool.
    IF ~~ THEN DO ~SetGlobal("T#SourceState","LOCALS",3)
    IncrementGlobal("T#Round","GLOBAL",1)~ GOTO Raise1
    END

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    APPEND PPGUY02

    IF ~~ THEN BEGIN Raise1
    SAY ~As you gaze deeper into the pool, you see strange patterns begin to form. Strands of fate, such as those the Pocket Plane gave you the power to access, float in ethereal space, daring you to touch one of them.~
    = ~Tethered to each of the strands, you see a phantom image of someone who was once close to you. You may use the power granted to you by the pool to call one of these phantoms forth, if you wish.~
    IF ~GlobalGT("T#Round","GLOBAL",2) Global("T#RaiseAlianna","GLOBAL",0)~ THEN REPLY ~Bring me Alianna, my mother.~ GOTO Alianna1
    IF ~GlobalGT("T#Round","GLOBAL",0) Global("T#RaiseDynaheir","GLOBAL",0)~ THEN REPLY ~Bring me Dynaheir.~ GOTO Dynaheir1
    IF ~GlobalGT("T#Round","GLOBAL",2) Global("T#RaiseGorion","GLOBAL",0)~ THEN REPLY ~Bring me Gorion, my foster father.~ GOTO Gorion1
    IF ~GlobalGT("T#Round","GLOBAL",1) Global("T#RaiseKhalid","GLOBAL",0)~ THEN REPLY ~Bring me Khalid.~ GOTO Khalid1
    IF ~GlobalGT("T#Round","GLOBAL",0) Global("T#RaiseTiax","GLOBAL",0)~ THEN REPLY ~Bring me Tiax.~ GOTO Tiax1
    IF ~GlobalGT("T#Round","GLOBAL",1) Global("T#RaiseXzar","GLOBAL",0) !Global("D0Harpers","GLOBAL",10)~ THEN REPLY ~Bring me Xzar.~ GOTO Xzar1
    IF ~GlobalGT("T#Round","GLOBAL",2) Global("T#RaiseYoshimo","GLOBAL",0)~ THEN REPLY ~Bring me Yoshimo.~ GOTO Yoshimo1
    IF ~Global("T#SourceState","LOCALS",1)~ THEN REPLY ~Never mind. Do not raise a spirit.~ GOTO Nobody1
    IF ~Global("T#SourceState","LOCALS",2)~ THEN REPLY ~Never mind. Do not raise a spirit.~ GOTO Nobody2
    IF ~Global("T#SourceState","LOCALS",3)~ THEN REPLY ~Never mind. Do not raise a spirit.~ GOTO Nobody3
    END

    The PPGuy02 it keeps mentioning appears to be a dummy NPC which is pulled directly from the game's vanilla files, with an odd comment, to be used as the speaker of these narration lines. From the .tp2:
    // CREs

    // Fix the stupid freaking bird animation problem

    COPY_EXISTING ~ppguy02.cre~ ~override/ppguy02.cre~
    WRITE_SHORT 0x28 24851

    I have no idea what he means by "the stupid freaking bird animation problem" nor do I know why he uses a vanilla NPC to append all these narration lines to instead of using an original file.
  • jasteyjastey Member Posts: 2,671
    The used cre is an invisible cre that can be used to do the "talking" of e.g. the pool because you do not see the actual helper-cre. The "stupid freaking bird animation problem" (I like that term) is probably the problem that not all invisible cres are able to actually starting a dialogue, I had similar problems in my mod with hours of debugging why the helper-cre'd dialogue would not trigger until I figured it's because it can't per engine restrictions.

    My original question was whether you traced down how exactly the spawning is scripted, because I do not feel like going through all the mod files. I did look at the summon.d before I asked. The question is, where are the trigger variables set (where in the dialogue - as soon as possible or after the summoning is requested, for example) and which script does use the variables. So basically, what failed if it failed.
    Because honestly, I don'd understand why too fast of an engine would lead to a failure - if variables are set fast this must be a good thing, no? Usually the problem is that variables get set too slow inside a dialogue to make use of them right away. But inside a script, a fast execution should be no problem as long as the relevant steps are scripted one after the other.
  • CirosanCirosan Member Posts: 25
    edited November 2019
    I believe I understand what you're asking, and I think I have the answer.

    I've attached a copy of PPGUY02.dlg for you to look at as I explain, but I think I have an idea of how to fix this already. However, please correct me if I'm wrong.

    Turnabout attempts to track which of the three pools the player is at by using the variable "T#SourceState","LOCALS" - this is the first thing it adds in the summon.d file. I believe this is the variable that can't be set quickly enough because the developers already had a means of tracking which pool the player is at that's fool proof.

    Every single confirmation requires this value to be set to 1, 2, or 3 as a trigger. The final confirmation for whether to revive someone begins at state 36 in the dialogue; follow along with me from there (Alianna is at 44, Yoshimo at 46, Gorion at 48, Tiax at 50, and Dynaheir at 52, and Khalid at 54). Note how every "Yes" option requires T#SourceState to be at 1, 2, or 3 (corresponding to which pool they're at, so the revived person doesn't appear at a pool across the room). But tellingly, the only response Turnabout adds that does not require T#SourceState to be set to anything is the option to not summon an ally, which is the very problem the readme describes.

    However, the vanilla files simply use a combination of Global("fin_pool_talker","AR6200",1), Global("Pool1Active","AR6200",3), Global("Pool2Active","AR6200",3), and Global("Pool3Active","AR6200",3). By arranging them such that the only true outcome gives the player's position - e.g., if Global("fin_pool_talker","AR6200",1), !Global("Pool1Active","AR6200",3), and !Global("Pool3Active","AR6200",3), the only thing that can mean is that the player is at pool #2; and the vanilla files even have that in their actions as (SetGlobal("Pool2Active","AR6200",3). Every pool in vanilla ToB simply arranges these four variables such that their outcome tells the game where the player is.

    I believe that by using ToB's own methods and replacing all instances of, for example, IF ~Global("T#SourceState","LOCALS",1)~ with IF Global("fin_pool_talker","AR6200",1) !Global("Pool2Active","AR6200",3) !Global("Pool3Active","AR6200",3)~ (and so on with the correct arrangement for each pool) will fix the problem by bypassing the need for the redundant, fickle T#SourceState variable.

    The only thing I need is a BG2EE save game with Ascension already installed that's already at the final battle, so I can test these changes myself to see if they fix the problem. Anyone have one handy?
  • ALIENALIEN Member Posts: 1,271
    Cirosan wrote: »
    The only thing I need is a BG2EE save game with Ascension already installed that's already at the final battle, so I can test these changes myself to see if they fix the problem. Anyone have one handy?

    Why you simply can't use EE's MoveToArea? AFAIK, It will launch all area scripts (unlike classic engine).
  • CirosanCirosan Member Posts: 25
    edited November 2019
    ALIEN wrote: »
    Cirosan wrote: »
    The only thing I need is a BG2EE save game with Ascension already installed that's already at the final battle, so I can test these changes myself to see if they fix the problem. Anyone have one handy?

    Why you simply can't use EE's MoveToArea? AFAIK, It will launch all area scripts (unlike classic engine).

    I did not know this.

    However, I've noticed something else: look at the top of summon.d:
    EXTEND_BOTTOM PPGUY02 1 // This is the first pool.
    IF ~~ THEN DO ~SetGlobal("T#SourceState","LOCALS",1)
    IncrementGlobal("T#Round","GLOBAL",1)~ GOTO Raise1
    END

    EXTEND_BOTTOM PPGUY02 2 // This is the second pool.
    IF ~~ THEN DO ~SetGlobal("T#SourceState","LOCALS",2)
    IncrementGlobal("T#Round","GLOBAL",1)~ GOTO Raise1
    END

    EXTEND_BOTTOM PPGUY02 3 // This is the third pool.
    IF ~~ THEN DO ~SetGlobal("T#SourceState","LOCALS",3)
    IncrementGlobal("T#Round","GLOBAL",1)~ GOTO Raise1
    END

    And remembering what Jastey said:
    jastey wrote: »
    My experience is that inside a dialogue, you need one additional dialogue line to be executed before a variable is recognized as being set.

    Bingo. We transition immediately from setting T#SourceState to Raise1, which is where all of the responses require that variable.

    Thus, I believe its method of appending the T#SourceState variable to states 1, 2, and 3 of PPGUY02 are faulty, and are what's behind the problem.
    Post edited by Cirosan on
  • jasteyjastey Member Posts: 2,671
    Hm, Raise1 is already split into two separated lines before the reply options use the variables, so that makes the needed additional line and they should be set by then.

    The readme explicitely states that a too fast execution would be the problem, that's what I don't get.
Sign In or Register to comment.