Jump to content

BG2 ToB effects


Taimon

Recommended Posts

I've had a look at the BG2 ToB effects and these are the notes I made.

They list most params an effect uses and includes some type information as well as possible values, if they were easy to spot.

I did omit some params (like target location), that are possibly handled by the engine itself. Not sure about the 0xCC param, so I left it in.

param5 and 0x78 should have probably named resource2 and resource3.

 

The legend is like this:

n/a means that the switch over the eff opcode points to an assertion failure (i.e. crash).

n/i marks those effects where the eff class did not override the eff processing method.

"copy of" signals that the same eff class is used, whereas "identical to" means that the two effs share the same switch block.

lb, lw and uw refer to the lower byte, lower word or upper word of the param. (!lb means that everything but the lower byte is used.)

I used * as wildcard for any other value than the ones specified.

The difference between [1,*] and [1] is, that in the first case, there are two different execution paths [if/else], while in the second there is only one [if].

Hope you can figure out the rest on your own.

 

The executable was from the german version and patched to v26498.

Keep in mind that this was a purely static analysis, so no guarantees that all those params are actually used.

And there are most likely some errors, due to missing concentration etc.

 

 

Most effects share one function that deals with the timings (converted to pseudo-code).

Might shed some light one those seldom used timings.

 

eff here refers to the engine intern effects structure.

So I think eff.0x110 is what you refer to as the run_once flag. (I've named it eff_done or something like that.)

Link to comment

Nice work, thanks. It's good to know the possible params - though when you say bounds[0-25], are params capped at those values or only applied within those values (e.g. would 100 be changed to 25, or ignored)?

 

There's also some info on previously unknown opcodes, which may make them easier to figure out.

 

:)

Link to comment

All 26498 executables come from an identical source, so you'd be looking at the universal 26498 (except for the beta 26499 executable, which doesn't have its Private Build Description value correctly modified to reflect the 26499 build).

 

According to the CGameEffect constructor, eff.0x118 is a copy of the duration. I haven't found whether that is altered before the effect is applied, though.

Link to comment
Might shed some light one those seldom used timings.
Hmm, Mode 5 is Delay/While equipped. Neat.

 

It means that the final result of the computations will be adjusted to (capped at) these bounds.
Some of these look off or discarded, though (most of the stat modifiers that you have 0-100 should allow penalties)?

 

0x007: The colorloc dual-wielding hack.

0x00C: Why the slayer change, I wonder?

0x00D: It looks like this logic is the auto-explode for frozen death (although, it wouldn't have done this in BG/TotSC or IWD)?

0x011: The insidious heal flags. SoA should only have 2^0 (raise dead).

0x019: param3 is the damage when param2 == 4.

0x03B: Why is this check only in thief skill statmods?

0x03C: 3-5 might be ToB only (dead magic).

0x03E: Have to break this one out from 0x2A in NI.

0x062: Same as 0x019.

0x077: Yeah, this gets stored as a separate effect with the # images.

0x07A: Param(1,3,4) here are charges(1,2,3); 0xFF has the funkiness where the expiry is stored in the item structure on the CRE.

0x0F3: Stupid nishruu.

 

 

I'm pretty happy with NI here; it looks like most (other than the priest L<8) everything is already known and correct, although the following are ones that could use further checks (mostly because of the 0x108 attached or 0xCC values):

0x005: param1 [0,*], param2 [0-5,1000-1005], dices, timing [1], 0xCC [!0]
0x012: param1, param2 [0-5], dices, timing [1,*], 0xCC [!0]
0x027: timing [1,*], 0xCC [!0], duration	// 0xCC checked if timing != 1
0x035: param1, param2 [2], timing [1,*], 0xCC [!0], param3 (if 0xCC == 0, else overriden)
0x043: param2 [0,1,3,4,5,*], resource, duration, param5, timing [1]
0x047: param1 [lb], param2 [0,1], timing [1], 0xCC [0]
0x078: param1, param2 [0-11]
0x07F: param1, param2 [0-9], duration, resource, param5, timing [!1]
0x081: param1 [lb], param2 [lw], 0xCC [!0]
0x087: param2 [0,*], resource, 0xCC [0,*], param3, param1 [lw] (p1 + p3 overridden in some paths)
0x08D: param1 [0,*], param2 [0-40] (multiples of 3 in range 0-31 -> failed assert)
0x091: param2 [0-2], 0xCC [!0]
0x0B1: param1 [0,*], param2 [2-8], resource, duration (copied), timing (copied), 0xCC (copied), param3 [0,*] (bool)
0x0B4: param1, param2 [0,*], resource
0x0B5: param1, param2 [0,*]
0x0B6: resource, param5
0x0B7: param1, param2, resource
0x0BA: param1 [lw], param2 [lw], resource, caster location
0x0BB: resource, param5, 0x78, variable (8), resistance [lw], param1, param2, param3 [lw], param4, 0x48 [0,*]
0x0BF: param1 [lb], param2 [0,1]
0x0C3: param1 (copied), timing [lb] (copied)
0x0C4: param1 [2,*]
0x0D2: 0xCC [0,*]
0x0D3: 0xCC [!0]
0x0D4: param2 [!0]  (can create an 0x0D5 and 0x138 eff -> disabled?)
0x0D5: param2 (copied), duration, 0xCC [!0] (creates an 0x0D4 eff)
0x0D8: param1, param3 [lb], param4, 0xCC [!0]
0x0E8: resource, param5, 0x78, param2 [0-11], param1 [0-3], param3 [!0], parent resource (if res_type == 1)
0x0EA: param1 [lb:0-9], param2 [lb:0-3|uw], parent resource (if res_type == 1)
0x0EC: param2 [1-3], duration, 0xCC [!0], timing (copied)
0x0F5: param2   // sets dword/stat (0xCC4) in actor to p2 (eff 0x0F6 sets 0xCC8, 0x0F7 sets 0xCCC)
0x0F6: param2, 0xCC [!0]
0x100: resource, param5, 0x78, param3 [!0], parent resouce (if res_type == 1)
0x101: param1 [lb:0-9], param2 [lb:0-3], parent resource (if res_type == 1)
0x102: resource
0x109: resource, param5, 0x78, param1, param2 [lb]
0x10D: param1 [lb], param2 [1,*] (1 uses p1 + negated p1), duration
0x10F: param2 [1]
0x110: param1 [lw], param2 [lw:0-4], resource, timing [0,3,6,0x1000], duration (if one of the mentioned timings is used), param3 [lw]
0x117: param2 [0-13], resource
0x11B: param1 [0,*], param2 [2-8], resource, timing (copied), duration (copied), 0xCC (copied)
0x126: param1
0x12B: param1 [>0], param2 [0,*]
0x135: resource, param5, 0x78, param1, param2 [lb]

Link to comment
It means that the final result of the computations will be adjusted to (capped at) these bounds.
Some of these look off or discarded, though (most of the stat modifiers that you have 0-100 should allow penalties)?

Most of these checks only occur if timing is set to 1. In general, the engine is very inconsistent there. For example, in the charisma modifier effect, the bounds are checked in every case except if p2 = 0 and timing != 1.

And all those stats are stored in at least three different places, so who knows which of these changes are only temporarily.

 

 

I think the 0xCC flag in the effects can be ignored. It's not copied by what I think is the copy constructor of an effect.

 

I assume it is already known, but you can actually use p1 = 0 as wildcard for the IDS targeting effects. (Doesn't work with align.ids, though.)

 

Got confused while looking at the dispel/bypass resistance field.

To actually resist an effect, bit2 has to be zero, the targets magic resistance has to be greater than the roll (0-99) and bit1 must be set? (checked in that order)

And bit3 of this field is checked in a different routine (before the resistance/save check, together with the probabilities). I think it bypasses power-based checks.

Link to comment

Charisma makes sense because 0-25 is the real stat cap (don't want people setting 99 here). But resistance can decrement and such (is signed stat; negative resist is extra damage), although I guess maybe not permanently if the check is != 1 for those.

 

I think the 0xCC flag in the effects can be ignored. It's not copied by what I think is the copy constructor of an effect.
Yeah. It's interesting that the effects that seem to chat with it the most are animation altering (animation change; sex change; polymorph; etc.), but the engine knew to set it back to 0 every time I tried toggling it in a saved GAM (param3 was always 3; no idea what it's doing ATM).

 

I assume it is already known, but you can actually use p1 = 0 as wildcard for the IDS targeting effects. (Doesn't work with align.ids, though.)
Yeah, 0-anything is always (most IDS are smart enough to not have 0). This is just the way engine matches objects; works in script too.

 

Got confused while looking at the dispel/bypass resistance field.

To actually resist an effect, bit2 has to be zero, the targets magic resistance has to be greater than the roll (0-99) and bit1 must be set? (checked in that order)

And bit3 of this field is checked in a different routine (before the resistance/save check, together with the probabilities). I think it bypasses power-based checks.

You're labeling from Bit1 (i.e., Bit1-2-3 = 01 02 04)? My final though was that Bit0 (1) was dispel and Bit1 (2) was bypass; however, when the value gets stored, it's common to get Bit2 (4) turned on for a value of 6 or 7 with no known effect, although could just be "I've already been attached always apply me" flag (not sure it'd ignore power, though)? Also, having all bits off seems to be interpreted as Bit0=0,Bit1=1 (seems to be what you're saying since dispel has to be set to do resistance checks).
Link to comment
Charisma makes sense because 0-25 is the real stat cap (don't want people setting 99 here).

But why isn't it checked, when p2 = 0 and timing != 1? Can we go over 25 temporarily?

Doesn't make sense that this should only happen with a cumulative modifier.

Maybe it's capped elsewhere.

 

You're labeling from Bit1 (i.e., Bit1-2-3 = 01 02 04)?

Sorry, yes. :)

 

My final though was that Bit0 (1) was dispel and Bit1 (2) was bypass; however, when the value gets stored, it's common to get Bit2 (4) turned on for a value of 6 or 7 with no known effect, although could just be "I've already been attached always apply me" flag (not sure it'd ignore power, though)?

Bit2 should be somehow related to the power param.

Also, the maximum power level seems to be 9.

Link to comment
But why isn't it checked, when p2 = 0 and timing != 1? Can we go over 25 temporarily?
No, but p1 can be below zero when used with p2 = 0 (increment). Checking it in the majority of cases beyond p2 = 1 would seem to serve no purpose, however (and it's definitely irrelevant for p2 = 2); I'm pretty sure sanity checks for the stat values live somewhere else (somewhere where they can actually be enforced).

 

Bit2 should be somehow related to the power param.
Could be, but I'm not quite sure what effect it would have; power only has one use (although, it'd be a neat test indeed to see if this bit really bypasses level immunity), so I'm not sure why the engine would (only occasionally) set this bit on stored effects. I could see this being a way to get effects that don't bypass these types of checks (the engine has arcane rules for when an applicable effect is checked for immunities and resistances) to just shut up when they're attached, but I'm not sure why they'd even want those checks on attached effects then (since the others would automatically bypass, no check would ever fail and you just wasted all that effort).

 

Edit: setting just Bit2 seems to be useless (immunity to power is still checked); unless it's a "run_once" effect (which the engine chucks no matter what mode and flags are set to), the engine clears the bit and sets mode to 1024 + mode (so, an Instant/Permanent(1) effect would be attached with 0x5C set to 0 and 0x24 set to 1025), causing limited effects to actually attach and stick around foreverish (without updating the duration to the expiry time -- a limited run VVC of 2s retains the 2s duration value and plays once for 2s every reapplication period). (Essentially, the game suddenly treats mode and flags as a WORD and just copies the two bytes to the attached EFF mode and ignores flags?)

 

Also, the maximum power level seems to be 9.
Even if the immunity effects try to cap the value at 9 (which it looks like from your notes), any value should work (it just won't be blocked by any level immunities)? I believe there are some effects with powers larger than 9 in the default game (10s, 15s, and maybe a 100 or some other crazy number for the rift device IIRC). Unless you use 1-9, you're basically just saying "this is an unblockable level effect."

 

Random
From the timing notes, eff.0x118 defaults to 6 (either initialized to or if it finds 0 there).
Link to comment

They are not totally unknown, but it's good to have more info on them:

0x0B6: resource, param5

0x0BF: param1 [lb], param2 [0,1]

0x0F5: param2 // sets dword/stat (0xCC4) in actor to p2 (eff 0x0F6 sets 0xCC8, 0x0F7 sets 0xCCC)

0x126: param1

0x134: param2

0x137: // random SPWISH01-25

Link to comment

The "disallow item" junk is neat, but I still doubt they can be made to do anything useful.

The casting level mod params are known, but param1 still doesn't do anything (param2 just toggles wizard(0) and priest(1)).

The three berserk effects set the "STAGE" block of stats.

Protection from tracking uses p2 for the stat value (must be !0).

 

#120 - Type 5 just seems to be Non-silver in BG2/ToB (no different from Type 4)?

#129 - param2 stores the HP bonus after the effect is applied; it looks like when the value at 0xCC is 0 (defaults 1), the bonus is read from param2 instead of being recalculated.

#180 - if param2 is non-zero, the effect doesn't seem to work (specified item can still be equipped)?

Link to comment

Small update for the berserk effects:

 

Avenger already mentioned this, 0x003 only works on party members.

0x0F5 enables a 1% chance to go berserk while attacking (checked in attack roll). This sends an 0x0F6 (stage 1) to the creature, which will later turn to 0x0F7 (stage 2). (might overlap)

Stage 1 has all the stats changes (hp/ac/thac0), while stage 2 sets the berserk state flag. Both durations are 45 ticks, but I think they messed up the timing (0x1000), so the effects won't be applied at all.

 

On a side note, the berserk action (#124) does not use any berserk effects.

It only works if the first integer parameter is > 1 and there is someone to attack nearby. (Will result either in an attack or wait action.)

Link to comment

Taimon: you mean they didn't add gametime to the tick count (45) ?

Heh, yes, i don't see it anywhere.

 

You forgot to mention: the 1% could be affected by luck.

Since i'm on bad terms with signs in assembly code, i cannot determine how luck affects this. It looks like bad luck (any negative luck) would disable this feature.

I'm unsure on how is it a bad luck to not go randomly berserk :)

 

Also, berserk adds 2 to the attack roll, this is also in the attack roll code.

Link to comment
Taimon: you mean they didn't add gametime to the tick count (45) ?

Yes, either that or they should use timing 0.

 

You forgot to mention: the 1% could be affected by luck.

Since i'm on bad terms with signs in assembly code, i cannot determine how luck affects this. It looks like bad luck (any negative luck) would disable this feature.

I'm unsure on how is it a bad luck to not go randomly berserk :)

Who knows, maybe they consider it lucky to go berserk from time to time. In my opinion it shouldn't be a LuckyRoll at all, but since it's bugged I guess nobody cares. :laugh:

Link to comment

Archived

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

×
×
  • Create New...