Diablo IIThe Lurker Lounge
      ABOUT · CHAT · COMMUNITY · FORUM · NEWS


The Workshop
Diablo II Mechanics and Statistics
Knowledge is power. In this forum, we discuss detailed game mechanics and statistics, from how Diablo II functions, to the probabilities of events happening.
Questions?  Read the Forum FAQ and please obey the Forum Rules.

Subject: "How does D2 really work?" Archived thread - Read only
 
  Previous Topic | Next Topic
Printer-friendly copy     Email this topic to a friend    
Conferences The Workshop Topic #1327
Reading Topic #1327
dkass
Member since 13-Feb-02
09-Jul-02, 09:49 PM (GMT)
Click to EMail dkass Click to send private message to dkass Click to view user profileClick to add this user to your buddy list  
"How does D2 really work?"
 
   Hi,

I have several questions for code readers (especially those who are disassembling things) that would really help as I try to figure out exactly how wereform weapon speeds work.

1) Has anyone found the actual table of werebase swing speeds? Note that unlike other characters, the wereforms do not have weapon specific animations, thus there is presumably somewhere a table listing the base speed for each weapon type. I suppose it could instead be hardcoded in the executable, but that sounds ugly (though this IS Blizzard).

2) I've assumed that Blizzard overloaded multiplication and division to do their 8 bit fixed point arithmetic, but was wondering exactly how they did it. This would show up as an 8 bit left/right shift before/after each multiplication or division in the assembly code (when dealing with 8 bit fixed point values). I was wondering if anyone had noticed it and could tell me the exact order Blizzard used for each operation (there are several "equivalent" ways of doing it that give slightly different rounded results--I've been assuming they used the "right" way, but...).

3) How are percentage values actually stored in the code? I'm not interested in the MPQ files (I know they're stored as decimal percentages). Given the use of 8 bit fixed point arithmetic, I can see three ways. a) a simple integer with the percentage (which is converted to a fraction before being used in calculations). b) a fixed point value with the decimal percentage (ie the percentage * 256). c) the actual fixed point value corresponding to the percentage. There is weak evidence from the werespeeds that it might be c). Note that (using integer arithmetic) y = x% * 256 / 100 + 1 done in 8 bit fixed point gives a good approximation (and converts back correctly using y * 100 / 256). The only really odd case is that 0 converts to 1, but I suspect that this is just forced to 0 upon conversion. I would suspect that the conversion is done when reading in the MPQ files (easy to do and the cost is irrelevant)--the back conversion would then only be for display purposes.

Ebony Flame


  Printer-friendly page | Top

  Subject     Author     Message Date     ID  
How does D2 really work? [View All] dkass 09-Jul-02 TOP
  RE: How does D2 really work? Ruvanal 10-Jul-02 1
     RE: How does D2 really work? dkass 14-Jul-02 2
         RE: How does D2 really work? T_Hawk 15-Jul-02 3
  RE: How does D2 really work? CelticHound 15-Jul-02 4
  RE: How does D2 really work? Jarulf 19-Jul-02 5

Conferences | Topics | Previous Topic | Next Topic
Ruvanal
Charter Member
10-Jul-02, 10:53 AM (GMT)
Click to EMail Ruvanal Click to send private message to Ruvanal Click to view user profileClick to add this user to your buddy list  
1. "RE: How does D2 really work?"
In response to message #0
 
   LAST EDITED ON 07-10-02 AT 10:55 AM (PDT)
 
You may get a better response on this at the Phrozen Keep. There are several posters there that are more familiar with the way the code is inside the DLLs and may already have that information in their notes (Hint: Jarulf is posting there semi-regularly). There has also been a considerable amount of progress with dealing with the animation files in the game in the last year also. The Keep has started a new forum that is devoted to dealing with the multimedia aspect of the game (there is a lot of swapping out of the animation files and importing of animation files from other games). You may be able to find out from them more on how some of the data for handling the animations is stored for the characters/monsters (the COF files).

1) I do not think that there is an imbedded table for these parameters. Most of this is handled through the standard animation engine in the game. Too many special cases to check and it can slow down the performance. There are animation files that can be viewed with CV5. The animation lengths in these usually correspond quite well with the frame speeds that have been built up over time for the orginal 5 characters. Generally the relationship of the the animation file length and the frame speed used in most of teh calculators is frame_speed=animation_length-1. The animation files for the WereForms have only 2 attack animations in them, a right handed swing (a1hth) and a left handed swing (a2hth). For the werebear form both of these are 12 cels in length for all directions that can be attacked in. Similarly the werewolf has both of these with an animation length of 13 cels. I would expect that any of the differences in speed would be from base weapon speed and the IAS effects of equipment and skills.

2) This could be better answered at the Keeps Code Editing forum. Bear in mind that the final executable are more the result of the compilers and assemblers optimzation routines than than what the programers had orginally written. There are many different ways that parts of this could have been optimaised to take advantage of the internal workings of the Intel processor design (bit shifting is not always the optimal method for the newer processors).

3)From what I gather from many of Jarulf's posts and others the percentages are stored as straight values. When the game is working with the percentages, it is dealing with attributes such as damage, life, mana and such. These values are stored in a 4 byte form where the lowest byte is used for the fractional portion of a point and the highest three bytes are used for the whole values (only the highest 3 bytes would normally be displayed on the character screen). An example would be a character has 200 life, the value for their life would be stored as (highest byte to lowest) 00 00 c8 00. If they took 2.5 points of damage it would then be 00 00 c5 80. This is how most of the fractional aspect is handled in the game. Many of the slow acting effect over time (poison, open wounds, firewalls, etc.) have ther systems set up to tracking the damage in 1/256ths of a point of damage per frame of game time. There are similar setups for some of the other 'fractional' things that happen in the game (mana usage and recovery for example).

edit:
links to the main Keep site
http://dynamic2.gamespy.com/~phrozenkeep/site/
and the forums
http://dynamic2.gamespy.com/~phrozenkeep/forum/index.php


  Printer-friendly page | Top
dkass
Member since 13-Feb-02
14-Jul-02, 08:30 PM (GMT)
Click to EMail dkass Click to send private message to dkass Click to view user profileClick to add this user to your buddy list  
2. "RE: How does D2 really work?"
In response to message #1
 
   >You may get a better response on this at the Phrozen Keep.

Thanks for the suggestion. I'll go investigate there when I next have some time.

>1) I do not think that there is an imbedded table for these
>parameters. Most of this is handled through the standard
>animation engine in the game. Too many special cases to
>check and it can slow down the performance. There are
>animation files that can be viewed with CV5. The animation
>lengths in these usually correspond quite well with the
>frame speeds that have been built up over time for the
>orginal 5 characters. Generally the relationship of the the
>animation file length and the frame speed used in most of
>teh calculators is frame_speed=animation_length-1. The
>animation files for the WereForms have only 2 attack
>animations in them, a right handed swing (a1hth) and a left
>handed swing (a2hth). For the werebear form both of these
>are 12 cels in length for all directions that can be
>attacked in. Similarly the werewolf has both of these with
>an animation length of 13 cels. I would expect that any of
>the differences in speed would be from base weapon speed and
>the IAS effects of equipment and skills.

There is definitely a difference in wereform swing speed between different types of weapons. This is easy to see. Swing a Spear and a Large Axe (both -10 base speed weapons) while in wereform. The Large Axe will be much faster than the spear (note that this is among the more obvious examples--especially if no other speed gear is used, the most egrigious examply might be a pike and a great maul).

Given the existence of only two sets of animations for the wereforms, there is has to be a table somewhere. Now it might be in the code itself (ie as a set of conditionals). And the use of the table is the entire problem with the wereform speeds.

>2) This could be better answered at the Keeps Code Editing
>forum. Bear in mind that the final executable are more the
>result of the compilers and assemblers optimzation routines
>than than what the programers had orginally written. There
>are many different ways that parts of this could have been
>optimaised to take advantage of the internal workings of the
>Intel processor design (bit shifting is not always the
>optimal method for the newer processors).

I suppose I should have been more complete and mentioned that a multiplication/division by 256 would also do the job. Now it may be that an optimizing compiler might do something odd, but I don't see moving this very far--especially since doing other operations before the shift (even if mathematically cancelling the shift) will result in bugged answers (due to rounding effects).

>3)From what I gather from many of Jarulf's posts and others
>the percentages are stored as straight values. When the
>game is working with the percentages, it is dealing with
>attributes such as damage, life, mana and such. These
>values are stored in a 4 byte form where the lowest byte is
>used for the fractional portion of a point and the highest
>three bytes are used for the whole values (only the highest
>3 bytes would normally be displayed on the character
>screen).

OK, I'm confused by your answer. I understand the use of 256ths. This what is formally called 8 bit fixed point arithmetic (as opposed to floating point because the decimal point is always after the same bit of the number). Maybe my terminology was confusing (my background involves significant computational theory and discrete mathematics)? I'll try again.

From comments Jarulf has made previously and the forms of the equations themselves, it is obvious that the frame counter used to determine the speed of animations is stored in the same way as life, with 3 bytes for the whole nubmer and the fourth for fractional parts (or in formal mathematical parlance, in 8 bit fixed point conventions). Now, one of the important parts of weapon speed is IAS. This is shown to the player as a percentage (more or less) and appears to be stored that way in the MPQ files. For example, "of Quickness" is a 40% increase in speed. Now the key is how is this value stored internally in the code. There are three possibilities:

a) as the integer 40 ie 00 00 00 28
b) as the integer 40 but in 256ths ie 00 00 28 00
c) as the fraction 0.4, but in 256ths. It probably converts to 103 / 256 (depending on the conversion routine used, it might be 102 / 256 instead) ie 00 00 00 67 (or 00 00 00 66).

The exact form used has implications for how the calculation is probably done and the location of breakpoints in trying to optimize weapon speeds.

Ebony Flame


  Printer-friendly page | Top
T_Hawk
Charter Member
15-Jul-02, 08:45 AM (GMT)
Click to EMail T_Hawk Click to send private message to T_Hawk Click to add this user to your buddy list  
3. "RE: How does D2 really work?"
In response to message #2
 
   LAST EDITED ON 07-15-02 AT 08:45 AM (PDT)
 
>There is definitely a difference in wereform swing speed
>between different types of weapons. This is easy to see.
>Swing a Spear and a Large Axe (both -10 base speed weapons)
>while in wereform. The Large Axe will be much faster than
>the spear (note that this is among the more obvious
>examples--especially if no other speed gear is used, the
>most egrigious examply might be a pike and a great maul).

The best anyone came up with is that it's not a table, but a formula that attempts to match the speed of the single-animation wereform swing with the corresponding unshifted-Druid weapon-specific animation. There's no clear way to figure it all out, and we've pretty much given up on solving it for good. There's several approximations, but none that work 100%.

Yes, Blizzard developed a half-dozen animations of Druids swinging (which are used how often?) as opposed to one of each wereform swinging (which is used FAR more often.)

Back to playing more Civ 3...


  Printer-friendly page | Top
CelticHound
Charter Member
15-Jul-02, 12:11 PM (GMT)
Click to EMail CelticHound Click to send private message to CelticHound Click to view user profileClick to add this user to your buddy list  
4. "RE: How does D2 really work?"
In response to message #0
 
   LAST EDITED ON 07-15-02 AT 12:12 PM (PDT)
 
If I recall correctly, the reason you see all the truncations to integer in the formulas is that they were trying to avoid doing any divisions at all.

Here is a simple (and slightly inaccurate) example:

Let's say you have a 16-bit integer you want to divide by 100. What you can do instead is multiply it by 656 into a four byte result and then just take the highest 16-bits for your answer. This works because discarding the lower 16 bits is like right shifting them away, which is like dividing by 65536.

While I'm on the subject, this was a little test program I wrote to try and duplicate the Random Number Generator in D2. I think the algorithm is correct. I forget who I stole the comments at the bottom from, but that's what I based the code on.



#include "stdio.h"
#include "stdlib.h"

int main(int argc, char** argv) {

if (argc < 2) {
printf("\nUsage: %s <seed in hex> <<iterations>>\n");
return 0;
}

char* dummy;
unsigned int num = strtoul(argv<1>, &dummy, 16);
unsigned __int64 carry = 666;
unsigned __int64 num64 = (num & 0xffffffff) | (carry << 32);

int iterations = 20;
if (argc > 2) {
iterations = atoi(argv<2>);
}

printf("\nResults for seed of 0x%08.8x over %d iterations\n",
num, iterations);

printf("%d. output = 0x%08.8x, carry = 0x%08.8x\n",
0, num, (unsigned int) carry);

for (int i = 0; i < iterations; ++i) {
num64 = (num64 * 0x6AC690C5) + carry;
unsigned int output = (num64 & 0xffffffff);
carry = (num64 >> 32) & 0xffffffff;
printf("%d. output = 0x%08.8x, carry = 0x%08.8x\n",
i+1, output, (unsigned int) carry);
}

return 0;
}

// The first thing to know about how all the rare and magic item
// decoding works is the random number generator function. The random
// number generator stores a 64 bit number. Call the low 32 bits Seed
// and high 32 Carry.

// To initialize the random number generator with a 32 bit number
// (DWORD1, or DWORD2), set the seed value to the appropriate DWORD,
// and set the carry to 0x29A (666).

// To get an output from the random number generator, multiply the
// value in Seed by 0x6AC690C5 and then add the carry.

// The low 32 bits of this new result is the new value of Seed in the
// random # generator, and is also the output random value. The high
// 32 bits is the new value to put in Carry.

-- CH


  Printer-friendly page | Top
Jarulf
Charter Member
19-Jul-02, 04:11 PM (GMT)
Click to EMail Jarulf Click to send private message to Jarulf Click to add this user to your buddy list  
5. "RE: How does D2 really work?"
In response to message #0
 
   >2) I've assumed that Blizzard overloaded multiplication and
>division to do their 8 bit fixed point arithmetic, but was
>wondering exactly how they did it.

No idea if they actually made some suchthings or if they just multiply some values and handles them at larger numbers.


>This would show up as an
>8 bit left/right shift before/after each multiplication or
>division in the assembly code (when dealing with 8 bit fixed
>point values).

That doesn't happen, the values are stored left shifted (for example mana and life) and then the normal calcs are performed on those values. You will see the shift prior for any value that will be manipulating the life or mana. The same applies for damage that is at some state shifted left by 8 too for example.

> I was wondering if anyone had noticed it and
>could tell me the exact order Blizzard used for each
>operation (there are several "equivalent" ways of doing it
>that give slightly different rounded results--I've been
>assuming they used the "right" way, but...).

Well, the values are stored and saved shifted and thus treat it like any other value, just with, for example the extra 8 bits of precision.

Almost all values in the game (at least game play wise, no idea about graphics and such) are integer ones. There are some (very few) places that use floating point calcs, usually some intermidiate calcs where the value is then converted back to integer, otherwise, all calcs can for practical purposes be considered as integer ones. How it actually ends up in assembler is up to the compiler, in this case MS Visual Studio 6. So if you are interested in the exact nature of for example a /3, you may if you have acces to it, check how it ends up compiled. Usually such divisions will be compiled into a multiplication by a huge 31 bit value (which ends up in eax/edx) and then some sort (if needed) right shift of the edx register (whic was explained in another reply).

In addition for keeping some stats at higher precision (like life and mana), some other values will also be stored at for example 10 extra bits precision. If you look at the treasure class file, you will see values that modify the chance for an item to be unique and such as fractions of 1024. Basically the game will thus multiply by this value and then divide by 1024 (often only at the end after other modifications are done too), thus resulting in higher precision and avoiding round down issues for low values. This happened for example in earlier versions of the game where the chance being modified could be in the order of 1 in 3 and then the 3 was modified by some value, for example cut in half, resulting in a value of 1 due to truncation in integer calcs which caused "jumps" in the resulting item drops.

The same applies for the frame counter which you seem to be interested in. It also use 8 extra bits of precision. Hence if an animation will take 8 frames to display, it is stored as 8*256. The frame counter which counts current frame will similary be updated by 1*256 each frame. Modifiers to the speed the frames are shown will simply affect this frame counter allready multiplied by 256 (it is stored multiplied by 256 as well). So, if you have a 10% increase in frame speed (like faster attack), the game will take 1*256 and multiply it by 10 and then dividing it by 100 (resulting in 25). This value is then added to the counter (which is 1*256) for a final frame counter value of 281 and so on. The exact way it ends up might vary some, probably due to the compiler working.

>3) How are percentage values actually stored in the code?

As an integer value. Especially since only some stats and values are stored with extra bits of precision this is usefull. The game then typically multiply by this integer value and divide by 100 (through the way I described above.


>I'm not interested in the MPQ files (I know they're stored
>as decimal percentages). Given the use of 8 bit fixed point
>arithmetic, I can see three ways. a) a simple integer with
>the percentage (which is converted to a fraction before
>being used in calculations).

Not really, as I said, the game handle it (regardless of if the value being manipulated is using some sort of extra bits of precision) by muliplying the value by it and then dividing by 100.


> b) a fixed point value with
>the decimal percentage (ie the percentage * 256). c) the
>actual fixed point value corresponding to the percentage.
>There is weak evidence from the werespeeds that it might be
>c).

It really depend on how exactly they wrote the formula in the source and how they emplyed the parentesis and such. As I said above though, the frame counter will bea value allready multiplied by 256.

As for how the frame counter is then used, well in the following way. The game keep the end frame number (if it is 8, it stores it as 8*256), it then calculate the frame counter which is 256 as a base value which is then modified by various effects (typically some sort of summed +% value that is multiplied with it and divided by 100) resulting in a modified frame counter. Each frame, the frame counter is then updated by this value, so if it was 281 from our example above, it would be updated as 281, 562, 843 and so on. WHen it reaches (or goes above) 8*256 (it was 8 frames), it has reached the end. Since the game really never calculate the actual number of frames something will take but uses this formula, one end up with some quite complex formulas (as far as rounding and such is concerned) if one want to write a formula that calculate the actual number of frames it will take.

This method has the nice effect of to be able to know which picture of the animation to show, one simply takes the currnt frame counter and divide by 256 and then has the animation pict to show, this works both for faster and slower animation speeds than default where the game will skip or show the same pic multiple times.


Does this answer your questions?

> Note that (using integer arithmetic) y = x% * 256 / 100
>+ 1 done in 8 bit fixed point gives a good approximation
>(and converts back correctly using y * 100 / 256). The only
>really odd case is that 0 converts to 1, but I suspect that
>this is just forced to 0 upon conversion. I would suspect
>that the conversion is done when reading in the MPQ files
>(easy to do and the cost is irrelevant)--the back conversion
>would then only be for display purposes.


  Printer-friendly page | Top

Conferences | Topics | Previous Topic | Next Topic