Jump to content

Short-circuiting Or trigger.


tomprince

Recommended Posts

devSin uses a Fisher Price Mac, it might make the disassembling process hard.

hmm... I thought IDA Pro was ported to OS X some time back?

 

besides, I'm sure dev has at least one PC.

 

I logged in from one of my PCs and it worked flawlessly (the whole process, from login to receipt, took about 10 seconds), using Mozilla.

 

but then again, now that I think about it, this mofo is so good he doesn't really need a disassembler. He can simply look at a bunch of 1s and 0s and tell you exactly what it does. Not even Taimon can do that!

Link to comment
devSin uses a Fisher Price Mac, it might make the disassembling process hard.
Not least because there is no HoW for Mac.

 

Even if I had the code in front of me, though, I still wouldn't look. Because I don't care.

 

And even if I did look, and I knew for sure, it wouldn't matter, because Avenger apparently won't believe it if it comes from me. :p

 

Really, though, all this needs is for someone to check in-game. Would take all of five minutes to get the exact behavior.

Link to comment

Nah, i would believe you if you said you looked with a debugger.

 

So, lets recap the assumptions and experiences so far:

1. short circuiting for normal condition blocks (AND) in all engines

2. short circuiting in IWD2 Or subblocks

3. no short circuiting in BG2/HoW Or subblocks

4. DLG triggers work exactly the same way as script triggers.

 

I hope i got all right :suspect:

Link to comment

So bigg just has to link to something Sim claimed a decade ago to have amazing insight (even though I posted pretty much the same statement a day earlier), but I have to disassemble the exe? When did my information become so unreliable?

 

So, lets recap the assumptions and experiences so far:
That all seems correct, or close enough (none of the vanilla dialogues in any engine would break even if the assumption for #4 was wrong, so meh).
Link to comment
Even if I had the code in front of me, though, I still wouldn't look. Because I don't care.
Actually, I got curious, so I looked. It behaves the same as BG2.

 

So IWD2 really is the only one with the stupid Or() logic. They also have the instants hack so that Continue() is actually useful (HoW uses BG2 Continue(), and all the scripts where they tried to use it are junk; some of them were ass-hard to fix because of the way they're written).

 

Come to think of it, how are you guys handling Continue() (all three or four versions of it)?

Link to comment

I personally wasn't aware of the different Continue() implementations.

 

Of the scripting, I looked only at Bg2's code, and even that wasn't too serious about the main control 'loop'.

Mostly looked at individual actions.

 

I do remember to tinker with Continue() based on modder documents after my initial 'naive' implementation failed.

Link to comment
So bigg just has to link to something Sim claimed a decade ago to have amazing insight (even though I posted pretty much the same statement a day earlier), but I have to disassemble the exe? When did my information become so unreliable?

 

Because you said so :)

 

I think it depends on the engine, but I haven't thought about it in a long time.

 

I believe they are all evaluated in BG2, but in Icewind2 (and maybe HoW), Or() bails as soon as a true condition is found (evaluated top-down). The only thing that gives me pause is that maybe BG2 was also first true but evaluated bottom-up, but in lieu of writing a script to actually test, I'm reasonably sure it just checks them all (definitely, short-circuiting top-down in BG2 is just plain wrong and will break a ton of stuff).

 

I have no idea why the behavior would be different in dialogues. It wouldn't make one bit of difference either way, no?

 

On the other hand, thanks for looking into it! :suspect:

Link to comment

I'm not sure how it works in BG/PST (and I think vanilla IWD); I know it's completely different from BG2.

 

In BG2, of course, the queue is held while all the triggers are checked (so none of the actions actually execute until the first action list without a Continue() or there's nothing left to evaluate; with the caveat that values retrieved from evaluated triggers, like LastSeenBy() from See()s and such, are updated as the triggers are checked) This is the same for HoW, but the designers clearly thought the actions were executing before any additional blocks were checked in a Continue() action.

 

In IWD2, they add some sort of instant actions for scripts (like dialogues; this may have to do with breaking up to DLGInst.ids ScrInst.ids or whatever, but I didn't want to look into where those come into play too much) that *do* execute before the Continue() takes over (so SetGlobal(), MarkSpellAndObject(), SetMyTarget(), and a few others, actually do run before additional blocks are checked, although most actions are still deferred). If GemRB doesn't do this currently, I'm not sure how anything is working right with IWD2 there (MSAO() in particular absolutely relies on this in IE).

 

Because you said so
For BG2, yeah, I left open the possibility to be wrong just because I've been away for so long (but even then, I only allowed for two possibilities, neither of which allowed for short-circuiting top-down). But the IWD2 was meant to be with certainty.
Link to comment

You remind me this...

 

There is something unrelated to condition blocks (but related to continue):

 

If a condition block is true, execution of the previous queued actions breaks, queue is cleared, new actions are queued. Except, if this is the same condition block as the one triggering the current queue.

But continue makes this complicated, because it makes more than one condition blocks active (as in if they are coming true again, their block shouldn't be executed).

 

How does this work with continue? I guess, the first condition block containing continue() is saved and if it is true in the second run, the script stops evaluating conditions in that run (keeps the queue from the old run).

Is this correct?

 

The above stuff is obviously unaffected by instants of any kind, because it would not execute any response block if the previous true condition block is the same as the current one.

 

So, now you say, some actions are simply executed, instead of being put on the queue based on their 'instant' flag.

And that this is different in scripts vs. dialog.

Hope these instants are still queued, if they are after a non-instant action. (This is how we implemented it).

Link to comment

That is correct (the initial block is "active"; subsequent blocks may be true, but the engine doesn't track that they're also being executed).

 

I can't tell with the different instants (my supposition was based on them splitting the usual Instant.ids into two files; one with a dialogue prefix and the other with script).

 

Mixed instant and queue requires testing; I think they actually might run even after queued actions (i.e., they're fired in place when they're encountered). EDIT: Yeah, this appears to be the case (the instants are executed before Continue()ing, always).

Link to comment

While I'm here, the last byte of the spawn flags in the ARE actor structure (+0x2C) is an area difficulty mask. If the area difficulty (whatever the fudge area difficulty is) is an unflagged level (either Level 1 or Level 2 or Level 3, bits 0-2 respective at +0x2F), then the creature is discarded when the ARE is loaded.

 

That said, I don't think any of the area difficulty stuff actually works (there's a big f-ing surprise)? I can't tell where or how this would be determined (it's possible the ARE header +0x54 is some sort of level requirement (byte*2 for Level 2, Level 3), like the average party level or something), and both times I looked, I was unable to get the game to think we were on anything but Level 1 (CheckAreaDiffLevel(1) is the only one to return true; all actors without the Level 1 bit set get dumped; I have no idea about the INI crap, though; etc.). I don't believe the levels were intended to be exclusive (i.e., any or all levels could be true at the same time), whatever the hell they were intended to actually do.

 

EDIT: Ok, it looks like it works on load but not afterward (CheckAreaDiffLevel() is just useless because it always says L1, but maybe it works in the area script as in AR6103.bcs when the area is first entered; and maybe the INI area_diff_[123] attributes too?). The two bytes at +0x54 do indeed look like the average level requirement for L2, L3 (I think the preceding word is still the unimplemented weather, and L1 is just always true), and creatures will be purged or loaded according to their area difficulty bits (only on the first load of course).

 

EDIT2: It just doesn't work at all from script. On load, it's assigned a difficulty (exclusive, not cumulative as I speculated) of either 1 or 2 or 3 (level-based, apparently not modified for game difficulty setting nor HoF). Creatures without the corresponding bit get dumped. Afterward, picking up the difficulty level doesn't work (although, it should just be made to work in GemRB, since the designers clearly thought it did, but it needs to return always the difficulty assigned when the area was first loaded); it always just hits Level 1. Don't know don't care what happens with the INI attrs.

 

Something also gets written to the ARE header +0x56 after loading, but I didn't look at what was happening with it. Additionally, some of the main actor flags (+0x28) get copied into the CRE struct after load.

 

I think GemRB still just skips over all this?

Link to comment

Yeah, and in case I forget, WeiDU 218 and various (probably none public) builds of NI now have the correct object matching for IWDII scripts: EA.GENERAL.RACE.CLASS.SPECIFIC.GENDER.ALIGNMNT.SUBRACE.AVCLASS.CLASSMSK (I don't know where in GemRB you do the test for these?).

 

The object matching might be the same (0 is any actor and -1 (255) is any AI object; except some of the specific triggers seem to match only literal values, e.g., Class(0) likes an actual '0' for class and not "any class", unlike normal IE, I think), but most sources have had Class and Subrace swapped. Subrace is a straight comparison, so it's either 0 or 1 or 2 and not the race/subrace dword combo from the IDS. AVClass seems like a duplicate of class but doesn't ever seem to work for anything. Class-matching is 3E "aware" (it checks ClassLevelGT(0), or the mask, not the actual CRE stat).

 

AVClass and the class mask are actually stored (and maintained, for the mask) in the CRE file at +0x3B4 (AVClass word; duplicate of Class byte) and +0x3B6 (ClassMsk dword bit field); I have no idea why they bothered. Although the object spec has this "AvClass" (as above), it doesn't actually seem to be detected correctly. Heretofore unreleased builds of NI also get these right (Class is just used in place of AVClass, always, since it doesn't matter and they were clearly the same when BIS was doing whatever it was they were trying to do).

 

I forgot to note, but the difficulty bits are cleared after loading. Yawn.

 

Dead() is purely an object check, and it defaults to true (Dead(null) still returns true, for objects not found). You know, they could have added a new trigger instead of changing entirely the way it works. Oh, wait. I forgot this was a BIS game.

Link to comment

GemRB has a configuration file (script.2da) which describes the object ID map.

GameScript.cpp handles it.

There is an array of ID matcher functions (IDSLink idsnames[])

 

I'm aware of the change in the Dead trigger, gemrb supports both all ways in a composite code.

The third is pst where the death variables are in the KAPUTZ namespace.

Link to comment

Yeah. I think I was hoping it would fall back to checking the string against variables if it couldn't suss out an object when I first looked at an IWD2 script, but I don't know why that would even occur to me. (I know why they made it work the way they did, but it's still nasty to default to true.)

 

Let me know if any of the prior post needs clarification; I didn't realize how much of a mess it turned into. Quick summary:

 

ARE header +0x54: these 2 bytes set the level requirement for area difficulty Level 2 (+0x54) & Level 3 (+0x55)

ARE header +0x56: a value gets stored here that may be related to area difficulty, but I can't tell how it's used and don't think it works anyway

ARE actor struct +0x28: some of the flags here get moved to the CRE flags on load (mostly the "CreAreFl" bits, but also plot critical)

ARE actor struct +0x2F: flags for area difficulty Level 1 (Bit 0), Level 2 (1), Level 3 (2). On first load, a difficulty is assigned; if the matching bit is not on, the creature is purged; otherwise, load and clear all bits

CheckAreaDiffLevel() doesn't work (always considers area difficulty to be 1); it should return the difficulty assigned on first load in GemRB

Link to comment

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...