Jump to content

[code] Code fixes, aka stuff we can't fix


CamDawg

Recommended Posts

11 hours ago, Bubb said:

The SPEED column in WSPECIAL.2DA is ignored by the engine — High Mastery and Grand Mastery do not provide bonuses to speed factor as they should

My goodness.  Is this in oBG, too?  "Unnerfed grandmastery" is one of the oldest tweaks I remember, so it's bizarre/wellofcourseitis no one ever caught it.

Link to comment

This is one where we can technically work around it without code changes, but really shouldn't.

Ammunition should not override the launcher's weapon speed (WS)

  1. Start a new ToB game with one of the pregens. Turn off party AI.
  2. Spawn a pair of identical Keldorns with C:CreateCreature("keldor14") and ctrl-q them into the party.
  3. Spawn in a regular heavy crossbow and Firetooth +5 with C:CreateItem("xbow01") and C:CreateItem("xbow16"). Note the listed weapon speed in the descriptions.
  4. Give and equip Firetooth to one Keldorn and the heavy crossbow to the other. Make sure that Firetooth is using the regular bolts in Keldorn's inventory and not its own magical ammo.
  5. Move the Keldorns a short distance away. Make a quick-save and pause the game.
  6. Spawn in an orc with C:CreateCreature("orc01"). Give the command for both Keldorns attack it, and then unpause.
  7. Reload the quicksave from step 5 and repeat as needed.

Observed

Both Keldorns attack more or less simultaneously, and instantly.

Expected

Keldorn has two attacks per round with a crossbow, and the difference in listed weapon speed between Firetooth (1) and a heavy crossbow (10) should result in about a ~2.7 second gap between the Firetooth attack (which should be pretty instant) and the heavy crossbow attack. The gap is so large that the first heavy crossbow attack should be happening fairly closely to Firetooth's second attack.

Notes

The weapon speed of ammo (arrows, bolts, and bullets) overrides the weapon speed of the launcher,. Since all ammo uses weapon speed 0, this makes the difference in weapon speed irrelevant. The crossbow Firetooth +5 (WS 1) using its own magical ammunition is slower than a heavy crossbow (WS 10) using bolts. If you equip Firetooth with normal bolts (or any bolts), both are equally fast at WS 0.

There are a couple of solutions, in order of preference:

  1. (requires code changes) Have launchers use the lower WS of the launcher or ammo. This requires a change to ammo (basically setting WS to 10 so that the launcher speed always 'wins') in both the base game and mod-added ammo, but allows for the possibility of magical ammo that speeds up a launcher.
  2. (requires code changes) Have launchers use the WS set in the item file and ignore WS in ammo. Simple, clean, requires no other changes.
  3. Set all ammo to WS 10, and have the launchers include WS bonuses. In this case something like Firetooth +5 would have a +9 WS bonus effect for an effective result of 1 WS. This would require changes to all launchers and ammo in the base game and mods; for mixed-use items (like the dagger Firetooth) it might interfere with other WS bonuses like those from the kensai kit.
Edited by CamDawg
borked the formatting
Link to comment
2 hours ago, Andrea C. said:

Wouldn’t 1) significantly slow down most ranged attacks compared to the status quo?

All of the options would slow down anything using arrows/bolts/bullets to their actual, listed weapon speed. Right now all of these attack instantly because they're effectively at 0 weapon speed.

Firetooth +5 and a heavy crossbow were chosen as examples because of the massive speed difference. If the WS were being correctly used, Firetooth should be close to its second shot by the time the heavy crossbow is shooting its first. As it currently stands, Firetooth using its own, magical ammunition is actually slower than the heavy crossbow--darts and throwing daggers are slower, too.

Link to comment

In preparation for the upcoming INI and CRE soundset fixes, here are some engine issues related to sounds. These have already been passed on to Beamdog (thanks, Galactygon!). They will have to be addressed for the other fixes to have the best effect (research and further discussion). Insomniator single-handedly patched the engine in a matter of days for the classics (TobExAL).

I've had some success getting audio-related engine bug reports across to Beamdog. These were fixed some time ago:

* Animation INI middle-column sounds should be played

* Animation INI slash, backslash and jab sounds should be played

So, there's hope.

1. [BGEE, BG2EE] CRE soundset ATTACK2, ATTACK3 and ATTACK4 don't play

Creatures have four attack sound slots in the CRE soundset:

* ATTACK1 (0xdc)
* ATTACK2 (0xe0)
* ATTACK3 (0xe4)
* ATTACK4 (0xe8)

Only ATTACK1 plays its sound, the other attack soundset slots don't work. For example, in BGEE wolf.cre:

* ATTACK1 -> StrRef: 5558 (wolff05.wav) always plays on every attack
* ATTACK2 -> StrRef: 5559 (wolff06.wav) never plays on any attack

All four ATTACK soundset slots should be enabled.

Most creatures have separate audio files for different attack animations (slash, backslash, jab). These sounds were designed to be played on specific attack animations. Back to the wolf example:

* wolff05.wav matches only the slash attack animation cycle (the wolf bites with its fangs)
* wolff06.wav matches only the backslash attack animation cycle (the wolf leaps with its paws and bites)

The attack sounds should be matched with the attack animations:

* ATTACK1 should play only on the SLASH attack animation cycle
* ATTACK2 should play only on the BACKSLASH attack animation cycle
* ATTACK3 should play only on the JAB attack animation cycle
* ATTACK4 should play only on the SHOOT attack animation cycle

If these ATTACK sounds simply played at random on any attack, there would be lots of mismatches between the attack sounds and the attack animations.

2. [BGEE, BG2EE] INI sounds overlap with CRE sounds

* INI.DAMAGE overlaps with CRE.DAMAGE (0xec)
* INI.DIE overlaps with CRE.DYING (0xf0)
* INI.ATTACK_SLASH overlaps with CRE.ATTACK (0xdc)
* INI.ATTACK_BACKSLASH overlaps with CRE.ATTACK (0xdc)
* INI.ATTACK_JAB overlaps with CRE.ATTACK (0xdc)

These sounds play at the same time. For example, BGEE wolf:

* dwolf08.wav in 7b00.INI.DAMAGE plays at the same time as wolff08.wav in WOLF.CRE.DAMAGE when the creature gets damaged
* dwolf09.wav in 7b00.INI.DIE plays at the same time as wolff09.wav in WOLF.CRE.DYING when the creature dies
* dwolf05.wav in 7b00.INI.ATTACK_SLASH plays at the same time as wolff05.wav in WOLF.CRE.ATTACK when the creature attacks with the slash animation
* dwolf06.wav in 7b00.INI.ATTACK_BACKSLASH plays at the same time as wolff05.wav in WOLF.CRE.ATTACK when the creature attacks with the backslash animation
* dwolf06.wav in 7b00.INI.ATTACK_JAB plays at the same time as wolff05.wav in WOLF.CRE.ATTACK when the creature attacks with the jab animation

INI sounds should be disabled if a given creature has sounds in the corresponding CRE soundet:

* INI.DAMAGE shouldn't play if a creature has CRE.DAMAGE sound
* INI.DIE shouldn't play if a creature has CRE.DYING sound
* INI.ATTACK_SLASH shouldn't play if a creature has CRE.ATTACK1 sound
* INI.ATTACK_BACKSLASH shouldn't play if a creature has CRE.ATTACK2 sound
* INI.ATTACK_JAB shouldn't play if a creature has CRE.ATTACK3 sound
* INI.SHOOT shouldn't play if a creature has CRE.ATTACK4 sound

The engine already properly disables:

* INI.SELECTION if a creature has CRE.SELECT_COMMON
* INI.BATTLE_CRY if a creature has CRE.BATTLE_CRY

All this will allow for creatures with the same animation to have completely different soundsets.

3. [BGEE, BG2EE] Hardcoded ITM weapon sounds overlap with INI and/or CRE attack sounds

The following weapon items are hardcoded to play sounds on attacks:

* ANGHEG1.itm -> ANKHG06.wav
* ANGHEG2.itm -> ANKHG05.wav
* BASIL1.itm -> BASIL06.wav
* BASIL2.itm -> BASIL05.wav
* BASILG1.itm -> GBASI05.wav
* BASILG2.itm -> GBASI06.wav
* BASILG3.itm -> GBASI07.wav
* ETTERC1.itm -> ETTER05.wav
* ETTERC2.itm -> ETTER06.wav
* WYVERN1.itm -> WYVER05.wav
* WYVERN2.itm -> WYVER06.wav
* WOLFWI1.itm -> WWOLF07.wav
* WOLFWI2.itm -> WWOLF06.wav

These hardcoded item sounds overlap with any attack sounds in the INI or CRE soundsets (they play at the same time). The hardcoded weapon item sounds should be disabled. They are leftovers from the legacy BG1 engine which didn't allow for setting different sounds for melee and ranged attacks. In the current engines we use INI and CRE soundsets to set sounds for attack animations.

4. [BGEE] Right-click INI.SELECTION sounds don't work

This has been fixed only for BG2EE engine. From 2.6.6 patch notes:

"Creature Sounds | Right-clicking on creatures will once again play their default sound"

The same fix should be applied to the BGEE engine.

Edited by skellytz
Link to comment
On 5/24/2022 at 7:45 PM, CamDawg said:

Ammunition should not override the launcher's weapon speed (WS)

Keldorn has two attacks per round with a crossbow, and the difference in listed weapon speed between Firetooth (1) and a heavy crossbow (10) should result in about a ~2.7 second gap between the Firetooth attack (which should be pretty instant) and the heavy crossbow attack. The gap is so large that the first heavy crossbow attack should be happening fairly closely to Firetooth's second attack.

gap is a little random:

Speed =  weapon speed + 1d6 - 3

1d6 - INITIATIVE roll, Speed limited to [0-10]

Link to comment
On 5/24/2022 at 9:35 AM, Awachi said:
Quote

The SPEED column in WSPECIAL.2DA is ignored by the engine — High Mastery and Grand Mastery do not provide bonuses to speed factor as they should

My goodness.  Is this in oBG, too?

oBG2 too.

Edited by Insomniator
Link to comment

@Bubb

So, here is another possible engine bug...

Could you please look into op272 mechanics and report here how it really works?

As you surely know, this opcode might trigger under intended circumstances, and that's certainly bad...

This odd behavior is easily reproducible with spells such as Summon Insects ("sppr319.spl"): this spell (op272 - once per 2 seconds for 42 seconds => "ipdam1.eff") might deal more damage than intended, especially if the targeted creature is hasted (since the opcode is subjected to Haste/Slow)...

Is there a way to prevent it from triggering under unintended circumstances...? I mean, if we're dealing with a removable (limited) effect, then the following setup should be enough to bypass this issue

Spoiler
  • op272, <frequency>, res="EFF"
  • "EFF"
    • op146, p2=1, timing=1, prob1=100, res="SPL"
  • "SPL"
    • op321, timing=1, res="SPL" // prevent self stacking
    • <opcode>, timing=0, duration=X (where X should be the lowest duration value compatible with Haste/Slow, which halves/doubles the timing rate of op272)

But what about this particular case (Summon Insects)...?

I mean, op12 (Damage) does not leave behind a removable effect, so op321 would not work in this case... Is there a workaround...?

Edited by Luke
Link to comment

It's complicated.

The general idea is that the persistent effects list is checked every 15 AI ticks. Spell duration works on "world time", and when you pause the game "world time" is reversed by 1 tick. So, you get a situation where a single "world time" tick is processed multiple times:

  1. worldTime increases to 1, everything in the game is processed.
  2. I pause the game, worldTime is reverted to 0, everything in the game is processed.
  3. I unpause the game, worldTime increases to 1, everything in the game is processed.

Oops, worldtime=1 was processed twice, which ticked the AI twice in a single "world time" tick... this is why pausing speeds up op272's next evaluation.

As for op272 happening multiple times in a single tick, I don't know. It's theoretically possible if the effects list is ticked 15+ times in a single update while the game is unpaused, but I have no idea what would cause that. Do you have an example?

And no, there's no way to fix the interval being inconsistent, it's a design flaw of the engine.

Link to comment

@Bubb

Yeah, it's a real mess...

16 hours ago, Bubb said:

As for op272 happening multiple times in a single tick, I don't know. It's theoretically possible if the effects list is ticked 15+ times in a single update while the game is unpaused, but I have no idea what would cause that. Do you have an example?

Ideally (and unless I'm missing something), since Summon Insects triggers once per 2 seconds for 42 seconds, it should deal:

  • 21 piercing damage if the targeted creature is not Hasted/Slowed
  • 42 piercing damage if the targeted creature is Hasted
  • 10 piercing damage if the targeted creature is Slowed

However, I got the following (I never manually paused the game and disabled/turned off all auto-pause game options):

  • 21 piercing damage if the targeted creature is not Hasted/Slowed
  • 62 piercing damage if the targeted creature is Hasted
    • Wut???
  • 21 piercing damage if the targeted creature is Slowed
    • Wut???

As you can see, there is something wrong with Hasted/Slowed targets 😕... Do you have an explanation for that...?

Edited by Luke
Link to comment
2 hours ago, Luke said:

As you can see, there is something wrong with Hasted/Slowed targets 😕... Do you have an explanation for that...?

 

Haste/Slow do not impose a direct 2x / 0.5x on frequency effects (25/78/98/272).  The last two posts in this thread explain it nicely for haste.

  • x/2-3 seconds triggers 3x times
  • x/4-7 seconds triggers 4x times
  • x/8-15 seconds triggers 5x times
  • etc...

Final multiplier for Haste = 2+RoundDown(Log2 (Original Frequency))

And each trigger happens at half the frequency from the previous, then repeats from the original frequency:

So an 8 second frequency would trigger at: 4, 6, 7, 7.5, 8, 12, 14, 15, 15.5, 16, 20, 22, 21, 21.5, 22, (+4, +2, +1, +0.5, +0.5)

Slow likely has a similar mechanic, to the point that sometimes it makes no difference.

Edited by kjeron
Link to comment

@kjeron

Interesting, that would certainly explain my in-game tests...

Did you perhaps round up .75 to 1...? I mean, technically speaking (and unless I'm missing something), 8 should be 7.75 (7.5/2 + 4 = 7.75)...

Having said that, if we consider again the BG(2) version of Summon Insects ("sppr319.spl"), we have (in case the targeted creature is hasted):

Spoiler

Default => once per 2 seconds

Hasted => once per 1 second (multiplier = 2 + floor(log2(2)) = 3)

  1. 1, 1.5, 2
  2. 3, 3.5, 4
  3. 5, 5.5, 6
  4. 7, 7.5, 8
  5. 9, 9.5, 10
  6. 11, 11.5, 12
  7. 13, 13.5, 14
  8. 15, 15.5, 16
  9. 17, 17.5, 18
  10. 19, 19.5, 20
  11. 21, 21.5, 22
  12. 23, 23.5, 24
  13. 25, 25.5, 26
  14. 27, 27.5, 28
  15. 29, 29.5, 30
  16. 31, 31.5, 32
  17. 33, 33.5, 34
  18. 35, 35.5, 36
  19. 37, 37.5, 38
  20. 39, 39.5, 40
  21. 41, 41.5, 42

As you can see, the total number of triggers is 63. To be precise, I got 62... But I guess that's due to some rounding issues, right...?

Now, in order to get 42 damage, we would need to block one effect at each interval... So for instance, when the one at 1 triggers, the next one at 1.5 should not fire (i.e., we would need a 0.5 second immunity...)

I tried the following setup and everything seems to work fine

Spoiler

  • op272, p1=2, p2=3, res="EFF" // once per 2 seconds
  • "EFF"
    • op146, p2=1, timing=1, prob1=100, res="SPL"
  • "SPL"
    • op12, p1=1, p2a=0, p2b=16, timing=1 // 1 piercing damage -- this will most likely be moved into another subspell so as to support "7eyes.2da"...
    • op206, timing=10, duration=1, res="SPL" (1 tick duration)

Moreover, sometimes everything seems to be fine even in case of Slow (i.e., a slowed creature takes 11 piercing damage... Though I'm not sure how to consistently replicate it...) But since we do not know how the multiplier scales with Slow, I'm not sure why it is so...

As far as Creeping Doom is concerned, it should be fine as is since its default frequency is already once per second (i.e., the multiplier is a direct 2x in this case)... To be precise, the subspell is still needed to support "7eyes.2da" (which cannot block EFF files)... However, the trailing op206 effect can hopefully be omitted...

Anyway, just out of curiosity: would you or @Bubb be able to determine the multiplier in case of Slow...? How would you proceed...?

Link to comment

Ok, I'll attempt to explain how op272's Type=3 works and why it's buggy. First, some things you need to know before I get to the opcode:

  • The main engine loop ticks along at the set FPS value, normally 30tps. I will call this an "engine tick".
  • Creature AI ticks at random mutiples of the engine ticks based on the object's internal ID. Normal creatures tick every other engine tick, Hastened creatures tick every engine tick, Slowed creatures tick every 4 engine ticks.
  • The persistent effect list is updated every AI tick as long as the game is unpaused. 15 of these updates constitute a period. Note how AI ticks can occur at 15tps, 30tps, or 7.5tps given the state of the creature.
  • "world time" ticks every other engine tick while the game is unpaused; spells use world time for duration.

Now, how op272 Type=3 works:

1) The world time the effect is decoded at is stored at .EFF +0x6C. This decode happens when the effect is added to the projectile, (I.E. at the end of casting), NOT when the effect is applied to the target.

2) When the effect is applied to the target the engine decides how long it will wait until applying op272's resref field. It does this via:

m_periodCounter = (<current world time> - <decode time>) / 15 % m_period // where m_period is Param1 when using type Param2=3

3) Remember how a period is 15 AI ticks? The persisent effect list keeps track of a creature-wide count of how many times it has been ticked. Every 15 AI ticks of this creature-wide counter => m_periodCounter increments and op272 checks if it should apply its resref. For type Param2=3, the resref is applied once the following is satisfied:

++m_periodCounter >= m_period

Once op272's resref is applied the process starts over from step 2.

 

Let's step through a real example to see how the above works, Param2=3, Param1=2:

Spoiler
op272 decoded on worldTime: 5453054
op272 applying on worldTime: 5453062, m_periodCounter: 0
op272 applying on worldTime: 5453063, m_periodCounter: 0
    15th AI Update on worldTime 5453065, m_periodCounter: 1
    15th AI Update on worldTime 5453080, m_periodCounter: 2
    Repeating effect applied [+26 from decode]
op272 applying on worldTime: 5453081, m_periodCounter: 1
    15th AI Update on worldTime 5453095, m_periodCounter: 2
    Repeating effect applied [+15 from last apply]
op272 applying on worldTime: 5453096, m_periodCounter: 0
    15th AI Update on worldTime 5453110, m_periodCounter: 1
    15th AI Update on worldTime 5453125, m_periodCounter: 2
    Repeating effect applied [+30 from last apply]
op272 applying on worldTime: 5453126, m_periodCounter: 0
    15th AI Update on worldTime 5453140, m_periodCounter: 1
    15th AI Update on worldTime 5453155, m_periodCounter: 2
    Repeating effect applied [+30 from last apply]
op272 applying on worldTime: 5453156, m_periodCounter: 0
    15th AI Update on worldTime 5453170, m_periodCounter: 1
    15th AI Update on worldTime 5453185, m_periodCounter: 2
    Repeating effect applied [+30 from last apply]
...

The system works OK at normal AI speed. However, if a creature is hastened it sneaks in extra applications. Consider:

Spoiler
op272 decoded on worldTime: 5453044
op272 applying on worldTime: 5453054, m_periodCounter: 0
op272 applying on worldTime: 5453054, m_periodCounter: 0
    15th AI Update on worldTime 5453054, m_periodCounter: 1
    15th AI Update on worldTime 5453062, m_periodCounter: 2
    Repeating effect applied [+18 from decode]
op272 applying on worldTime: 5453062, m_periodCounter: 1
    15th AI Update on worldTime 5453069, m_periodCounter: 2
    Repeating effect applied [+7 from last apply]
op272 applying on worldTime: 5453070, m_periodCounter: 1
    15th AI Update on worldTime 5453077, m_periodCounter: 2
    Repeating effect applied [+8 from last apply]
op272 applying on worldTime: 5453077, m_periodCounter: 0
    15th AI Update on worldTime 5453084, m_periodCounter: 1
    15th AI Update on worldTime 5453092, m_periodCounter: 2
    Repeating effect applied [+15 from last apply]
op272 applying on worldTime: 5453092, m_periodCounter: 1
    15th AI Update on worldTime 5453099, m_periodCounter: 2
    Repeating effect applied [+7 from last apply]
op272 applying on worldTime: 5453100, m_periodCounter: 1
    15th AI Update on worldTime 5453107, m_periodCounter: 2
    Repeating effect applied [+8 from last apply]
op272 applying on worldTime: 5453107, m_periodCounter: 0
    15th AI Update on worldTime 5453114, m_periodCounter: 1
...

Slow appears to work OK most of the time, though maybe I've missed something:

Spoiler
op272 applying on worldTime: 5453113, m_periodCounter: 0
    15th AI Update on worldTime 5453141, m_periodCounter: 1
    15th AI Update on worldTime 5453171, m_periodCounter: 2
    Repeating effect applied [+67 from decode]
op272 applying on worldTime: 5453173, m_periodCounter: 0
    15th AI Update on worldTime 5453201, m_periodCounter: 1
    15th AI Update on worldTime 5453231, m_periodCounter: 2
    Repeating effect applied [+60 from last apply]
op272 applying on worldTime: 5453233, m_periodCounter: 0
    15th AI Update on worldTime 5453261, m_periodCounter: 1
    15th AI Update on worldTime 5453291, m_periodCounter: 2
    Repeating effect applied [+60 from last apply]
op272 applying on worldTime: 5453293, m_periodCounter: 0
    15th AI Update on worldTime 5453321, m_periodCounter: 1
    15th AI Update on worldTime 5453351, m_periodCounter: 2
    Repeating effect applied [+60 from last apply]
...

 

And remember that pausing while op272 is active still double-counts a worldTime tick, causing additional problems.

Link to comment

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

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