Skip to content

[(BGEE, BG2) bug] Animate dead (divine)

CamDawgCamDawg Member, Developer Posts: 3,438
edited August 2012 in Fixed
From the Fixpack:

The divine version of Animate Dead had a number of minor issues. It used the mage spell icon at most levels; it lacked headers for being cast at levels 6-9, meaning when it was cast at those levels it behaved as if the caster was level 5; when cast at level 5 it had a 25% chance of three summons instead of two; when cast at level 11 there was a 10% chance of no summons at all.

The code's old and ugly, but it works:
// animate dead has several minor errors in effect headers and is missing headrs for levels 6-9
COPY_EXISTING ~sppr301.spl~ ~override~
READ_LONG 0x64 "abil_off"
READ_SHORT 0x68 "abil_num"
READ_LONG 0x6a "fx_off"
SET "lev6_exist" = 0
SET "lev7_exist" = 0
SET "lev8_exist" = 0
SET "lev9_exist" = 0
SET "new_abil" = 0
FOR (index = 0 ; index < abil_num ; index = index + 1) BEGIN
WRITE_ASCII ("%abil_off%" + 0x04 + (0x28 * "%index%")) ~sppr301b~ // consistent icon
READ_SHORT ("%abil_off%" + 0x10 + (0x28 * "%index%")) "min_lev"
PATCH_IF ("%min_lev%" = 1) BEGIN // if first header
READ_SHORT ("%abil_off%" + 0x1e + (0x28 * "%index%")) "abil_fx_num"
READ_SHORT ("%abil_off%" + 0x20 + (0x28 * "%index%")) "abil_fx_idx"
FOR (index2 = 0 ; index2 < abil_fx_num ; index2 = index2 + 1) BEGIN
READ_SHORT ("%fx_off%" + (0x30 * ("%abil_fx_idx%" + "%index2%"))) "opcode"
PATCH_IF ("%opcode%" = 177) BEGIN // last eff opcode
READ_BYTE ("%fx_off%" + 0x13 + (0x30 * ("%abil_fx_idx%" + "%index2%"))) "prob_lo"
PATCH_IF ("%prob_lo%" = 0) BEGIN
WRITE_BYTE ("%fx_off%" + 0x03 + (0x30 * ("%abil_fx_idx%" + "%index2%"))) 3 // power
WRITE_BYTE ("%fx_off%" + 0x12 + (0x30 * ("%abil_fx_idx%" + "%index2%"))) 75 // probability
END ELSE BEGIN
WRITE_BYTE ("%fx_off%" + 0x13 + (0x30 * ("%abil_fx_idx%" + "%index2%"))) 76 // probability
END
END
END
READ_ASCII ("%abil_off%" + (0x28 * "%index%")) "abil_clone" (0x28)
READ_ASCII ("%fx_off%" + (0x30 * ("%abil_fx_idx%"))) "fx_clone" (0x30 * "%abil_fx_num%")
SET "new_fx" = "%abil_fx_num%"
SET "start_fx" = ("%abil_fx_idx%" + "%abil_fx_num%")
SET "insert" = ("%abil_off%" + (0x28 * ("%index%" + 1)))
END ELSE
PATCH_IF ("%min_lev%" = 6) BEGIN
SET "lev6_exist" = 1
END ELSE
PATCH_IF ("%min_lev%" = 7) BEGIN
SET "lev7_exist" = 1
END ELSE
PATCH_IF ("%min_lev%" = 8) BEGIN
SET "lev8_exist" = 1
END ELSE
PATCH_IF ("%min_lev%" = 9) BEGIN
SET "lev9_exist" = 1
END ELSE
PATCH_IF ("%min_lev%" = 11) BEGIN // level 11 header
READ_SHORT ("%abil_off%" + 0x1e + (0x28 * "%index%")) "abil_fx_num"
READ_SHORT ("%abil_off%" + 0x20 + (0x28 * "%index%")) "abil_fx_idx"
FOR (index2 = 0 ; index2 < abil_fx_num ; index2 = index2 + 1) BEGIN
READ_SHORT ("%fx_off%" + (0x30 * ("%abil_fx_idx%" + "%index2%"))) "opcode"
PATCH_IF ("%opcode%" = 177) BEGIN // last eff opcode
READ_BYTE ("%fx_off%" + 0x13 + (0x30 * ("%abil_fx_idx%" + "%index2%"))) "prob_lo"
PATCH_IF ("%prob_lo%" = 0) BEGIN
WRITE_BYTE ("%fx_off%" + 0x12 + (0x30 * ("%abil_fx_idx%" + "%index2%"))) 45 // probability
END
END
END
END
END
PATCH_IF ("%lev6_exist%" = 0) BEGIN
SET "level" = 6
INSERT_BYTES (("%fx_off%" + (0x28 * "%new_abil%")) + (0x30 * ("%start_fx%" * ("%new_abil%" + 1)))) (0x30 * "%new_fx%")
WRITE_EVALUATED_ASCII (("%fx_off%" + (0x28 * "%new_abil%")) + (0x30 * ("%start_fx%" * ("%new_abil%" + 1)))) "%fx_clone%"
FOR (index2 = 0 ; index2 < new_fx ; index2 = index2 + 1) BEGIN
READ_SHORT (("%fx_off%" + (0x28 * "%new_abil%")) + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) "opcode"
PATCH_IF ("%opcode%" = 177) BEGIN // last eff opcode
READ_BYTE (("%fx_off%" + (0x28 * "%new_abil%")) + 0x12 + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) "prob_hi"
PATCH_IF ("%prob_hi%" != 100) BEGIN
WRITE_BYTE (("%fx_off%" + (0x28 * "%new_abil%")) + 0x12 + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) (100 - ("%level%" * 5)) // probability
END ELSE BEGIN
WRITE_BYTE (("%fx_off%" + (0x28 * "%new_abil%")) + 0x13 + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) (101 - ("%level%" * 5)) // probability
END
END
END
INSERT_BYTES ("%insert%" + (0x28 * "%new_abil%")) 0x28
WRITE_EVALUATED_ASCII ("%insert%" + (0x28 * "%new_abil%")) "%abil_clone%"
WRITE_SHORT (("%insert%" + (0x28 * "%new_abil%")) + 0x10) "%level%"
WRITE_SHORT (("%insert%" + (0x28 * "%new_abil%")) + 0x1e) "%new_fx%"
WRITE_SHORT (("%insert%" + (0x28 * "%new_abil%")) + 0x20) ("%start_fx%" * ("%new_abil%" + 1))
SET "new_abil" = "%new_abil%" + 1
END
PATCH_IF ("%lev7_exist%" = 0) BEGIN
SET "level" = 7
INSERT_BYTES (("%fx_off%" + (0x28 * "%new_abil%")) + (0x30 * ("%start_fx%" * ("%new_abil%" + 1)))) (0x30 * "%new_fx%")
WRITE_EVALUATED_ASCII (("%fx_off%" + (0x28 * "%new_abil%")) + (0x30 * ("%start_fx%" * ("%new_abil%" + 1)))) "%fx_clone%"
FOR (index2 = 0 ; index2 < new_fx ; index2 = index2 + 1) BEGIN
READ_SHORT (("%fx_off%" + (0x28 * "%new_abil%")) + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) "opcode"
PATCH_IF ("%opcode%" = 177) BEGIN // last eff opcode
READ_BYTE (("%fx_off%" + (0x28 * "%new_abil%")) + 0x12 + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) "prob_hi"
PATCH_IF ("%prob_hi%" != 100) BEGIN
WRITE_BYTE (("%fx_off%" + (0x28 * "%new_abil%")) + 0x12 + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) (100 - ("%level%" * 5)) // probability
END ELSE BEGIN
WRITE_BYTE (("%fx_off%" + (0x28 * "%new_abil%")) + 0x13 + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) (101 - ("%level%" * 5)) // probability
END
END
END
INSERT_BYTES ("%insert%" + (0x28 * "%new_abil%")) 0x28
WRITE_EVALUATED_ASCII ("%insert%" + (0x28 * "%new_abil%")) "%abil_clone%"
WRITE_SHORT (("%insert%" + (0x28 * "%new_abil%")) + 0x10) "%level%"
WRITE_SHORT (("%insert%" + (0x28 * "%new_abil%")) + 0x1e) "%new_fx%"
WRITE_SHORT (("%insert%" + (0x28 * "%new_abil%")) + 0x20) ("%start_fx%" * ("%new_abil%" + 1))
SET "new_abil" = "%new_abil%" + 1
END
PATCH_IF ("%lev8_exist%" = 0) BEGIN
SET "level" = 8
INSERT_BYTES (("%fx_off%" + (0x28 * "%new_abil%")) + (0x30 * ("%start_fx%" * ("%new_abil%" + 1)))) (0x30 * "%new_fx%")
WRITE_EVALUATED_ASCII (("%fx_off%" + (0x28 * "%new_abil%")) + (0x30 * ("%start_fx%" * ("%new_abil%" + 1)))) "%fx_clone%"
FOR (index2 = 0 ; index2 < new_fx ; index2 = index2 + 1) BEGIN
READ_SHORT (("%fx_off%" + (0x28 * "%new_abil%")) + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) "opcode"
PATCH_IF ("%opcode%" = 177) BEGIN // last eff opcode
READ_BYTE (("%fx_off%" + (0x28 * "%new_abil%")) + 0x12 + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) "prob_hi"
PATCH_IF ("%prob_hi%" != 100) BEGIN
WRITE_BYTE (("%fx_off%" + (0x28 * "%new_abil%")) + 0x12 + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) (100 - ("%level%" * 5)) // probability
END ELSE BEGIN
WRITE_BYTE (("%fx_off%" + (0x28 * "%new_abil%")) + 0x13 + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) (101 - ("%level%" * 5)) // probability
END
END
END
INSERT_BYTES ("%insert%" + (0x28 * "%new_abil%")) 0x28
WRITE_EVALUATED_ASCII ("%insert%" + (0x28 * "%new_abil%")) "%abil_clone%"
WRITE_SHORT (("%insert%" + (0x28 * "%new_abil%")) + 0x10) "%level%"
WRITE_SHORT (("%insert%" + (0x28 * "%new_abil%")) + 0x1e) "%new_fx%"
WRITE_SHORT (("%insert%" + (0x28 * "%new_abil%")) + 0x20) ("%start_fx%" * ("%new_abil%" + 1))
SET "new_abil" = "%new_abil%" + 1
END
PATCH_IF ("%lev9_exist%" = 0) BEGIN
SET "level" = 9
INSERT_BYTES (("%fx_off%" + (0x28 * "%new_abil%")) + (0x30 * ("%start_fx%" * ("%new_abil%" + 1)))) (0x30 * "%new_fx%")
WRITE_EVALUATED_ASCII (("%fx_off%" + (0x28 * "%new_abil%")) + (0x30 * ("%start_fx%" * ("%new_abil%" + 1)))) "%fx_clone%"
FOR (index2 = 0 ; index2 < new_fx ; index2 = index2 + 1) BEGIN
READ_SHORT (("%fx_off%" + (0x28 * "%new_abil%")) + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) "opcode"
PATCH_IF ("%opcode%" = 177) BEGIN // last eff opcode
READ_BYTE (("%fx_off%" + (0x28 * "%new_abil%")) + 0x12 + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) "prob_hi"
PATCH_IF ("%prob_hi%" != 100) BEGIN
WRITE_BYTE (("%fx_off%" + (0x28 * "%new_abil%")) + 0x12 + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) (100 - ("%level%" * 5)) // probability
END ELSE BEGIN
WRITE_BYTE (("%fx_off%" + (0x28 * "%new_abil%")) + 0x13 + (0x30 * ("%index2%" + "%start_fx%" + ("%new_abil%" * "%new_fx%")))) (101 - ("%level%" * 5)) // probability
END
END
END
INSERT_BYTES ("%insert%" + (0x28 * "%new_abil%")) 0x28
WRITE_EVALUATED_ASCII ("%insert%" + (0x28 * "%new_abil%")) "%abil_clone%"
WRITE_SHORT (("%insert%" + (0x28 * "%new_abil%")) + 0x10) "%level%"
WRITE_SHORT (("%insert%" + (0x28 * "%new_abil%")) + 0x1e) "%new_fx%"
WRITE_SHORT (("%insert%" + (0x28 * "%new_abil%")) + 0x20) ("%start_fx%" * ("%new_abil%" + 1))
SET "new_abil" = "%new_abil%" + 1
END
PATCH_IF ("%new_abil%" > 0) BEGIN
SET "abil_num" = ("%abil_num%" + "%new_abil%")
WRITE_SHORT 0x68 "abil_num"
WRITE_LONG 0x6a ("%fx_off%" + (0x28 * "%new_abil%"))
FOR (index = 0 ; index < abil_num ; index = index + 1) BEGIN
READ_SHORT ("%abil_off%" + 0x10 + (0x28 * "%index%")) "min_lev"
PATCH_IF ("%min_lev%" > 9) BEGIN // if after new inserted effects
READ_SHORT ("%abil_off%" + 0x20 + (0x28 * "%index%")) "abil_fx_idx"
WRITE_SHORT ("%abil_off%" + 0x20 + (0x28 * "%index%")) ("%abil_fx_idx%" + ("%new_abil%" * "%new_fx%"))
END
END
END
BUT_ONLY_IF_IT_CHANGES
Post edited by Bhryaen on
Tanthalas

Comments

  • TanthalasTanthalas Member Posts: 6,738
    I find this code most aesthetically pleasing.

    When I scroll through it fast enough there's this pleasant wavy effect.
    CuvBhryaenMatteoTurini
  • CuvCuv Member, Developer Posts: 2,535
    Its those staggered 'END's... wow, that made me dizzy
  • CamDawgCamDawg Member, Developer Posts: 3,438
    Ha! If you compare my stuff to someone like, say, @Wisp's you'll see what new, compact code looks like. :)

    I learned to code on Perl which is self-obfuscating. It taught me to use blatantly long variable names and comment, comment, comment.
  • KeithSKeithS Member Posts: 623
    Adding...
  • NathanNathan Member Posts: 1,007
    I'm slightly cross-eyed after reading that code block, but, verified that it has been implemented internally. ;) Should be present in the next build.
  • Avenger_teambgAvenger_teambg Member, Developer Posts: 5,862
    Confirmed fixed.
    Icons - check
    Probabilities - check
    Nitpick - The 3 effects could be replaced by 2, just like i suggested in the other summon spell.
  • TanthalasTanthalas Member Posts: 6,738
    edited August 2012
    Confirmed Fixed for:

    - From level 5 to 9 my Cleric displayed the correct icon for the spell.

    - In 50 casts of the spell, my level 5 Cleric never summoned 3 skeletons, max was always two.

    - Probability of raising more than one skeleton was, for the most part, increased from levels 6 to 9:

    Level 5 -> 10/52 -> 19%
    Level 6 -> 15/50 -> 30%
    Level 7 -> 25/50 -> 50%
    Level 8 -> 23/54 -> 43%
    Level 9 -> 23/54 -> 43%

    I think the difference can be attributed to luck, 50 casts aren't enough for perfect results.

    I can't check the level 11 problem in the game.
  • BhryaenBhryaen Member Posts: 2,874
    edited August 2012
    Oh, wow- I already did some work toward this one, didn't recognize...

    1. It used the mage spell icon at most levels
    2. it lacked headers for being cast at levels 6-9, meaning when it was cast at those levels it behaved as if the caster was level 5
    3. when cast at level 5 it had a 25% chance of three summons instead of two
    4. when cast at level 11 there was a 10% chance of no summons at all.
    Well, @Tanthalas raised the bar on how many test instances are required (that jerk!), so here goes with 50 each...

    RESULTS:
    1.
    This one I don't know about. It seems like the icon that both the divine and arcane Animate Dead icon use are identical, no? I'm probably just not understanding the issue properly.

    2. & 3.
    5th Level - 25% chance, RESULT: 16%
    ATTEMPT 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 ToTAL1, TOTAL2
    DAMAGE 2 1 1 1 1 2 1 1 1 2 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 2 1 1 1 1 1 1 2 1 1 1 1 1 41 9

    6th Level - 30% chance, RESULT: 24%
    ATTEMPT 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 ToTAL1, TOTAL2
    DAMAGE 1 1 1 1 2 1 1 2 1 1 2 1 2 2 1 1 1 1 1 1 1 1 2 1 1 1 2 1 1 2 2 1 1 2 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 38 12

    7th Level - 35% chance, RESULT: 30%
    ATTEMPT 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 ToTAL1, TOTAL2
    DAMAGE 1 1 2 1 2 1 1 1 1 2 1 1 1 2 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 2 1 2 1 2 1 2 2 2 1 1 1 1 2 1 1 2 1 2 1 1 35 15

    8th Level - 40% chance, RESULT: 36%
    ATTEMPT 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 ToTAL1, TOTAL2
    DAMAGE 2 1 2 1 2 2 2 1 1 1 1 2 2 1 1 2 1 1 1 1 1 1 1 2 2 2 1 2 1 2 1 1 1 2 1 1 1 1 1 1 2 1 2 1 1 1 1 2 1 2 32 18

    9th Level - 45% chance, RESULT: 46%
    ATTEMPT 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 ToTAL1, TOTAL2
    DAMAGE 1 2 2 2 2 2 2 1 1 2 1 1 1 1 2 2 1 2 1 1 1 1 2 1 2 2 1 1 1 1 2 1 2 2 1 2 1 1 1 1 2 2 1 1 2 1 2 1 2 2 27 23
    So my usual tendency to roll below the expected is proven unequivocally now: the game cheats me! And now I've caught it in the act! Ha!

    Anyway clearly no chance of getting 3 boneheads at 5th lvl now, and a clear progression seen from 6-9.

    4.
    Since we can't level up past 9, this would otherwise be a moot point, but due to research on the ToBEex 13 thread I used @Avenger_teambg's +5 Caster Level Ring on a Level 6 Cleric to get these results:
    6th Level + 5 Caster Levels - 55% chance, RESULT: 58%
    ATTEMPT 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 ToTAL1, TOTAL2
    DAMAGE 1 2 2 1 1 1 2 2 2 1 1 2 2 1 2 1 2 2 1 2 2 2 1 2 1 2 2 1 2 2 2 2 1 2 2 2 1 2 1 2 1 1 1 2 2 2 1 1 2 1 21 29
    With 50 tries at a 10% chance of summoning nothing, it would have been a dud around 5 times, but worked ok.

    CONCLUSION:
    Confirmed Fixed... so long as the icon is ok.
    Post edited by Bhryaen on
  • TanthalasTanthalas Member Posts: 6,738
    Hmm, I'll have to check once I'm back at my PC, but I could swear that the mage animate dead used a different icon.

    I'll have to check BG2 too.
  • TanthalasTanthalas Member Posts: 6,738
    @Bhryaen

    Yeah, I thought the difference was bigger, but it looks like its only that slight difference when you have the spell in your quickslot. So I think this one is confirmed fixed.
  • CamDawgCamDawg Member, Developer Posts: 3,438
    If I hadn't already been messing with the spell for other reasons, the icon would have been ignored as even too small of a fix for Fixpack. :)
    Tanthalas
Sign In or Register to comment.