Shooting damage bug?

Discuss Chaos Strikes Back for Windows and Linux, an unofficial port of Chaos Strikes Back to PC by Paul Stevens, as well as CSBuild, an associated dungeon editor.

Moderator: Zyx

Forum rules
Please read the Forum rules and policies before posting.
Post Reply
Stastny
Neophyte
Posts: 3
Joined: 10-Feb-12 16:14

Shooting damage bug?

Post by Stastny » 10-Feb-12 16:43

Hi there. This is great. After 25 years I've found Dungeon Master again! Super. You know the story.
On to business: I've been poking around in the code to try and figure a few things out and I believe I've discovered (re-discovered?) a serious bug in how the Shoot action's damage is determined. I get that most people here aren't much interested in missile weapons but I'm a bit of an archery enthusiast so it disturbs me. The offending section looks like this (in Attack.cpp):

pParam->attdep.shoot.range = pWeapon_18->uByte3 + pWeapon_22->uByte3;
pParam->attdep.shoot.damage = 2 * (pWeapon_18->word4 + w_42);
pParam->attdep.shoot.damage = D4W;

The 'pWeapon' figures are just constants for the given weapons; 'D4W' has already been determined and is meant to be the decay rate - 4 for a bow, 7 for a sling, etc. You can see that shoot damage is being calculated in the middle line and then just thrown out on the next line and replaced with D4W. So, in my game, Gothmog(mastery 5) with a crossbow should be 2*(120+5)= 250, but instead the programme is giving 14 as the value both for his Damage Remaining and for the Decay Rate. So: a slayer is launched with RangeRemainig=208, DamageRemaining=14, DecayRate=14. The formula for determining damage later on is a bit complicated but in essence this is ruining his damage per shot: testing all this against some skeletons on level 5 (with slayers) I'm getting between 20 and 34 damage, when it should be between 32 and 64 damage. Moreover, the crossbow's damage constant and Gothmog's shoot mastery are disappearing into thin air. To think of the time I've spent picking up arrows!

User avatar
Paul Stevens
CSBwin Guru
Posts: 4100
Joined: 8-Apr-01 16:00
Location: Madison, Wisconsin, USA

Re: Shooting damage bug?

Post by Paul Stevens » 10-Feb-12 18:48

Looks a bit strange, does it not?

My first impression is that you are correct.
I will be studying it a bit more later today.

Here is the original code for the case of shooters:

Code: Select all

  case atk_SHOOT:
//01c2d8 302b 00d4                MOVE.W   212(A3),D0
//01c2dc c07c 3c00                AND.W    #15360,D0
//01c2e0 e448                     LSR.W    #2,D0
//01c2e2 e048                     LSR.W    #8,D0
//01c2e4 0c40 0005                CMP.W    #5,D0
//01c2e8 6702                     BEQ      $+4 (=0x01c2ec)
//01c2ea 606e                     BRA      $+112 (=0x01c35a)
      if (pChar->possessions[0].db() != 5) goto tag01c35a;

      //I guess the following is safe.  We could not have
      //selected 'SHOOT' if we did not have the proper
      //weapon in the weapon hand.
//01c2ec 302a 0002                MOVE.W   2(A2),D0
//01c2f0 c07c 007f                AND.W    #127,D0
//01c2f4 c1fc 0006                MULS     #6,D0
//01c2f8 41ec de2e                LEA      -8658(A4),A0
//01c2fc d0c0                     ADD.W    D0,A0
//01c2fe 41d0                     LEA      (A0),A0
//01c300 2d48 ffee                MOVE.L   A0,-18(A6)
      DB5A2 = dbA2->CastToDB5(); //We will crash if not weapon
      pWeapon_18 = &d.weapons[DB5A2->weaponType()];
//01c304 3f2b 00d4                MOVE.W   212(A3),-(A7)
//01c308 4ead 0294                JSR      660(A5)    (=0x0099d2)
//01c30c 548f                     ADDQ.L   #2,A7
//01c30e 2d40 ffea                MOVE.L   D0,-22(A6)
      pWeapon_22 = TAG0099d2(pChar->possessions[0]);
//01c312 206e ffee                MOVE.L   -18(A6),A0
//01c316 4244                     CLR.W    D4
//01c318 1828 0001                MOVE.B   1(A0),D4
      D4W = pWeapon_18->uByte1;
//01c31c 206e ffea                MOVE.L   -22(A6),A0
//01c320 4240                     CLR.W    D0
//01c322 1028 0001                MOVE.B   1(A0),D0
//01c326 3d40 fffc                MOVE.W   D0,-4(A6)
      wpnByte1_4 = pWeapon_22->uByte1;
//01c32a 0c44 0010                CMP.W    #16,D4
//01c32e 6d16                     BLT      $+24 (=0x01c346)
//01c330 0c44 001f                CMP.W    #31,D4
//01c334 6e10                     BGT      $+18 (=0x01c346)
      if ( (D4W >= 16) && (D4W <= 31) )
      {
//01c336 0c6e 000a fffc           CMP.W    #10,-4(A6)
//01c33c 6702                     BEQ      $+4 (=0x01c340)
        if (wpnByte1_4 != 10)
        {
//01c33e 601a                     BRA      $+28 (=0x01c35a)
          goto tag01c35a;
        };
//01c340 987c 0010                SUB.W    #16,D4
        D4W -= 16;
//01c344 6028                     BRA      $+42 (=0x01c36e)
      }
      else
      {
//01c346 0c44 0020                CMP.W    #32,D4
//01c34a 6d22                     BLT      $+36 (=0x01c36e)
//01c34c 0c44 002f                CMP.W    #47,D4
//01c350 6e1c                     BGT      $+30 (=0x01c36e)
        if ( (D4W >= 32) && (D4W <= 47) )
        {
//01c352 0c6e 000b fffc           CMP.W    #11,-4(A6)
//01c358 6710                     BEQ      $+18 (=0x01c36a)
          if (wpnByte1_4 != 11)
          {
tag01c35a:
//01c35a 397c fffe b0d8           MOVE.W   #65534,-20264(A4)
            d.Word20264 = (unsigned)0xfffe;
//01c360 426e fff2                CLR.W    -14(A6)
            w_14 = 0;
//01c364 4245                     CLR.W    D5
            D5W = 0;
//01c366 6000 0472                BRA      $+1140 (=0x01c7da)
            break;
          };
//01c36a 987c 0020                SUB.W    #32,D4
          D4W -= 32;
        };
      };
//01c36e 2f0b                     MOVE.L   A3,-(A7)
//01c370 4eba fbfe                JSR      $-1024 (=0x01bf70)
//01c374 588f                     ADDQ.L   #4,A7
      SetCharToPartyFacing(pChar);
//01c376 4267                     CLR.W    -(A7)
//01c378 3f07                     MOVE.W   D7,-(A7)
//01c37a 4ead 04f2                JSR      1266(A5)    (=0x015a66)
//01c37e 588f                     ADDQ.L   #4,A7
//01c380 3d40 fffc                MOVE.W   D0,-4(A6)
      obj_4 = RemovePossession(chIdx, 0);
//01c384 3f3c 0001                MOVE.W   #1,-(A7)
//01c388 3f2c d248                MOVE.W   -11704(A4),-(A7)
//01c38c 3f2c d24a                MOVE.W   -11702(A4),-(A7)
//01c390 3f3c 0010                MOVE.W   #16,-(A7)
//01c394 4ead 0120                JSR      288(A5)    (=0x00219a)
//01c398 508f                     ADDQ.L   #0,A7
      QueueSound(16, d.partyX, d.partyY, 1);
//01c39a 3f04                     MOVE.W   D4,-(A7)
//01c39c 3f3c 000b                MOVE.W   #11,-(A7)
//01c3a0 3f07                     MOVE.W   D7,-(A7)
//01c3a2 4ead 03f6                JSR      1014(A5)    (=0x015f1e=DetermineMastery)
//01c3a6 588f                     ADDQ.L   #4,A7
//01c3a8 3d40 ffd6                MOVE.W   D0,-42(A6)
      w_42 = sw(DetermineMastery(chIdx, 11));
//01c3ac 206e ffee                MOVE.L   -18(A6),A0
//01c3b0 3028 0004                MOVE.W   4(A0),D0
//01c3b4 c07c 00ff                AND.W    #255,D0
//01c3b8 d06e ffd6                ADD.W    -42(A6),D0
//01c3bc e340                     ASL.W    #1,D0
//01c3be 3f00                     MOVE.W   D0,-(A7)
//01c3c0 206e ffee                MOVE.L   -18(A6),A0
//01c3c4 1028 0003                MOVE.B   3(A0),D0
//01c3c8 206e ffea                MOVE.L   -22(A6),A0
//01c3cc c07c 00ff                AND.W    #255,D0
//01c3d0 4243                     CLR.W    D3
//01c3d2 1628 0003                MOVE.B   3(A0),D3
//01c3d6 d043                     ADD.W    D3,D0
//01c3d8 3f00                     MOVE.W   D0,-(A7)
//01c3da 3f2e fffc                MOVE.W   -4(A6),-(A7)
//01c3de 2f0b                     MOVE.L   A3,-(A7)
//01c3e0 4ead 042c                JSR      1068(A5)    (=0x017498)
//01c3e4 defc 000c                ADD.W    #12,A7
      LaunchObject(
                   pChar,
                   obj_4,
                   pWeapon_18->uByte3 + pWeapon_22->uByte3,
                   2 * (pWeapon_18->word4 + w_42),
                   D4W );
//01c3e8 6000 03f0                BRA      $+1010 (=0x01c7da)
      break;

User avatar
beowuuf
Archmastiff
Posts: 20660
Joined: 16-Sep-00 12:00
Location: Basingstoke, UK

Re: Shooting damage bug?

Post by beowuuf » 10-Feb-12 18:49

This one might be interesting to CSBwin and DSB, clones of the original that use the original calculations. It would certainly beef up the thrown weapons that later one people discard as it takes away space for potions!


And welcome to the forums, btw :D Strong opening post!


Edit: And I apparently double posted with the creator of CSBwin, thus proving my point!

User avatar
Paul Stevens
CSBwin Guru
Posts: 4100
Joined: 8-Apr-01 16:00
Location: Madison, Wisconsin, USA

Re: Shooting damage bug?

Post by Paul Stevens » 10-Feb-12 18:50

I suppose this belongs in the CSBwin/CSBuild folder?
If anyone wants to move it.....???

User avatar
beowuuf
Archmastiff
Posts: 20660
Joined: 16-Sep-00 12:00
Location: Basingstoke, UK

Re: Shooting damage bug?

Post by beowuuf » 10-Feb-12 19:19

Oh, is this from CSBwin and not DM itself? I assumed Sophia might liek to look in to the code for DSB too. If this is CSBwin only, I can move it easily!

User avatar
Paul Stevens
CSBwin Guru
Posts: 4100
Joined: 8-Apr-01 16:00
Location: Madison, Wisconsin, USA

Re: Shooting damage bug?

Post by Paul Stevens » 10-Feb-12 21:19

As I see it, there is more than one thing
wrong here!
1)The last parameter to 'LaunchObject'
should be decay, not damage.
2) The reference to pWeapon_18->word4
should be pWeapon_18->uByte5. This could
dramatically change the damage computation :!:
Like change it by a factor of 256 or make it
negative depending on the contents of
pWeapon_18->uByte4.

Here is the original code again with my comments

Code: Select all

//01c39a 3f04                     MOVE.W   D4,-(A7)     ; Parameter[5] = D4W
//01c39c 3f3c 000b                MOVE.W   #11,-(A7)
//01c3a0 3f07                     MOVE.W   D7,-(A7)
//01c3a2 4ead 03f6                JSR      1014(A5)    (=0x015f1e=DetermineMastery)
//01c3a6 588f                     ADDQ.L   #4,A7        
//01c3a8 3d40 ffd6                MOVE.W   D0,-42(A6)
      w_42 = sw(DetermineMastery(chIdx, 11));
//01c3ac 206e ffee                MOVE.L   -18(A6),A0   ; A0 = pWeapon_18
//01c3b0 3028 0004                MOVE.W   4(A0),D0     ; D0=pWeapon_18->word4
//01c3b4 c07c 00ff                AND.W    #255,D0      ; D0 = pWeapon_18->uByte5
//01c3b8 d06e ffd6                ADD.W    -42(A6),D0   ; D0 = pWeapon_18->uByte5 + w_42
//01c3bc e340                     ASL.W    #1,D0        ; D0 = 2*(pWeapon_18->uByte5 + w_42)
//01c3be 3f00                     MOVE.W   D0,-(A7)     ; Parameter[4] = 2*(pWeapon_18->uByte5 + w_42)
//01c3c0 206e ffee                MOVE.L   -18(A6),A0   ; A0 = pWeapon_18
//01c3c4 1028 0003                MOVE.B   3(A0),D0     ; D0 = pWeapon_18->Byte3
//01c3c8 206e ffea                MOVE.L   -22(A6),A0   ; A0 = pWeapon_22
//01c3cc c07c 00ff                AND.W    #255,D0      ; D0 = pWeapon_18->uByte3
//01c3d0 4243                     CLR.W    D3   
//01c3d2 1628 0003                MOVE.B   3(A0),D3     ; D3 = pWeapon_22->uByte3
//01c3d6 d043                     ADD.W    D3,D0        ; D0 = pWeapon_18->uByte3 + pWeapon_22->uByte3
//01c3d8 3f00                     MOVE.W   D0,-(A7)     ; Parameter[3] = pWeapon_18->uByte3+pWeapon_22->uByte3
//01c3da 3f2e fffc                MOVE.W   -4(A6),-(A7) ; Parameter[2] = obj_4
//01c3de 2f0b                     MOVE.L   A3,-(A7)     ; Parameter[1] = pChar
//01c3e0 4ead 042c                JSR      1068(A5)    (=0x017498)
//01c3e4 defc 000c                ADD.W    #12,A7
And here is what I think the current code should
look like:

Code: Select all

      pParam->attdep.shoot.range = pWeapon_18->uByte3 + pWeapon_22->uByte3;
      pParam->attdep.shoot.damage = 2 * (pWeapon_18->uByte5 + w_42);
      pParam->attdep.shoot.decay = D4W;
      pParam->attdep.shoot.success = 1;
      CallAttackFilter(&filter, pParam, 1);
      LaunchObject(
                   pChar,
                   obj_4,
                   pParam->attdep.shoot.range,
                   pParam->attdep.shoot.damage,
                   pParam->attdep.shoot.decay );
If we can reach a consensus on this, I will
install the change in CSBwin.

User avatar
Sophia
Concise and Honest
Posts: 4002
Joined: 12-Sep-02 19:50
Location: Nowhere in particular
Contact:

Re: Shooting damage bug?

Post by Sophia » 11-Feb-12 00:48

For what it's worth, I remember running across this while extracting the functions for DSB.
However, I figured it to be just original DM engine weirdness and not a CSBwin bug because the "damage" value for thrown objects is almost completely irrelevant. Most of the impact power is derived from how much "range" is left.

While I don't really feel like sifting through the original CSB code to find it, there is commentary in DSB's code to that effect. Around line 911 of methods.lua I wrote:

Code: Select all

-- DM sets damage, clobbers it, and then sets damage and delta to the
-- same value. Completely insane! I doubt if it mattered much to the DM
-- engine, though, since the "damage" value rarely got used in the code
-- for when flying things impacted the party or monsters. However, since
-- I've made it count, I'll have to improvise these a bit.
This appears to be the same phenomenon you noticed.

The 'doubting it matters much' comes into play later on. In DSB, around line 768 of damage.lua:

Code: Select all

-- DM's default function was IMO broken for objects: "damagepower," which
-- was heavily affected by ninjaskill was used as follows:
--     sub_damage = (32 - damagepower/8)
--     if (dmg/2 < dmg - sub_damage) then return (dmg - sub_damage)
-- This is (almost?) never true, and hardly ever matters, anyway...
-- I've added "power_factor" as a tweak to make the skill matter more here.
-- It factors in the damagepower more heavily.
The variable dmg on the other hand is derived from the "range," not the "damage."

So, for DSB, I ended up adding my own tweaks to work around the bug/weirdness/whatever.

Stastny
Neophyte
Posts: 3
Joined: 10-Feb-12 16:14

Re: Shooting damage bug?

Post by Stastny » 11-Feb-12 19:32

Oops! Sorry if I put this in the wrong forum, I didn't see this one.
What is the difference between uByte5 and word4? I've been using the values 120 for crossbow and 125 for speedbow as seen in the DMencyclopedia and getting a match with in-game results (if this is a dumb question, sorry, I'm not a programmer - also, out of curiosity, at what point in the process are numbers rounded? Just for display, at each step, or somewhere in the middle?)
Meanwhile I've looked at various cases and how they play out with your formula (dmg/2 < dmg - sub_damage) - expressed as D6W = sw(Larger (D6W/2, D6W - D1W)) in the CSBwin code under DetermineMagicDamage: it turns out that, as you say, in most cases D6W/2 is the larger value. For example, it is always so for bow or sling, no matter the ninja's mastery (unless this can be increased above 16?). But in the case of the crossbow or speedbow the opposite is true, at short range anyhow. Because D1W is derived by subtracting from 32, its value progressively grows in the course of the arrow's flight, while D6W gets smaller, and therefore at a certain point D6W/2 (which can never be less than 1) 'takes over' as the final result. I guess this is a funny way of simulating the inherent 'punchiness' of the crossbow (and extreme accuracy of the ninja at point-blank).

Stastny
Neophyte
Posts: 3
Joined: 10-Feb-12 16:14

Re: Shooting damage bug?

Post by Stastny » 11-Feb-12 19:38

I should have said that with crossbow and speedbow it ought to be true, that damage is more important than range, but as things are now it is not.

User avatar
Paul Stevens
CSBwin Guru
Posts: 4100
Joined: 8-Apr-01 16:00
Location: Madison, Wisconsin, USA

Re: Shooting damage bug?

Post by Paul Stevens » 11-Feb-12 21:17

What is the difference between uByte5 and word4
A byte is 8 bits; a word is 16 bits.

Word4 = uByte4*256 + uByte5.

A word is made up of two bytes. On the Atari,
the first byte is the high byte of the word. On the
Intel machines, the first byte is the low byte
of the word. What fun.

Evidently, in this particular case, uByte4 is
generally (or always) zero so the answer is the
same either way. I have seen cases in the
Atari code where the programmers or the compiler
or whatever got confused about bytes and words.
This may be such a case.
process are numbers rounded
These are integers, my friend. 7/2 = 3. There
is no rounding.

Post Reply