Shooting damage bug?
Moderator: Zyx
Forum rules
Please read the Forum rules and policies before posting.
Please read the Forum rules and policies before posting.
Shooting damage bug?
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!
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!
- Paul Stevens
- CSBwin Guru
- Posts: 4318
- Joined: Sun Apr 08, 2001 6:00 pm
- Location: Madison, Wisconsin, USA
Re: Shooting damage bug?
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:
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;
Re: Shooting damage bug?
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 Strong opening post!
Edit: And I apparently double posted with the creator of CSBwin, thus proving my point!
And welcome to the forums, btw Strong opening post!
Edit: And I apparently double posted with the creator of CSBwin, thus proving my point!
- Paul Stevens
- CSBwin Guru
- Posts: 4318
- Joined: Sun Apr 08, 2001 6:00 pm
- Location: Madison, Wisconsin, USA
Re: Shooting damage bug?
I suppose this belongs in the CSBwin/CSBuild folder?
If anyone wants to move it.....???
If anyone wants to move it.....???
Re: Shooting damage bug?
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!
- Paul Stevens
- CSBwin Guru
- Posts: 4318
- Joined: Sun Apr 08, 2001 6:00 pm
- Location: Madison, Wisconsin, USA
Re: Shooting damage bug?
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
And here is what I think the current code should
look like:
If we can reach a consensus on this, I will
install the change in CSBwin.
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
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 );
install the change in CSBwin.
- Sophia
- Concise and Honest
- Posts: 4240
- Joined: Thu Sep 12, 2002 9:50 pm
- Location: Nowhere in particular
- Contact:
Re: Shooting damage bug?
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:
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:
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.
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.
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.
So, for DSB, I ended up adding my own tweaks to work around the bug/weirdness/whatever.
Re: Shooting damage bug?
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).
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).
Re: Shooting damage bug?
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.
- Paul Stevens
- CSBwin Guru
- Posts: 4318
- Joined: Sun Apr 08, 2001 6:00 pm
- Location: Madison, Wisconsin, USA
Re: Shooting damage bug?
A byte is 8 bits; a word is 16 bits.What is the difference between uByte5 and word4
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.
These are integers, my friend. 7/2 = 3. Thereprocess are numbers rounded
is no rounding.