Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Development

Development::Development Forum for development topics and for those interested in EQEMu development. (Not a support forum)

Reply
 
Thread Tools Display Modes
  #16  
Old 09-22-2008, 11:48 PM
Cantus
Fire Beetle
 
Join Date: Sep 2008
Location: New York
Posts: 18
Default

Discussion: http://eqemulator.net/forums/showthread.php?t=26262

Thanks again for all of your feedback! I've made several of the changes that were suggested, like adding in a simple preprocessor directive that lets a server administrator build a copy of the Emulator that never applies damage bonuses to weapons.

I have not yet added calculations for continuing to ramp up 2H damage bonuses for levels 81+, but we'll get that added soon!


To make compilation easier and also to speed things up a tiny bit, I've changed the way this function is deployed.


** Please disregard the first post in this thread, and instead follow the following steps to compile and test the proposed fixes **


1) Open Mob.h, and scroll down to line #646. It should look like the following:

Code:
int	GetWeaponDamageBonus(const Item_Struct* Weapon);

2) Please change the first word "int" to "int8", as follows:

Code:
int8	GetWeaponDamageBonus(const Item_Struct* Weapon);

3) Open Attack.cpp, and scroll down to lines #971 - 976. They should look like:

Code:
//if mainhand only, get the bonus damage from level
if(Hand==13){
	int damage_bonus = GetWeaponDamageBonus(weapon ? weapon->GetItem() : (const Item_Struct*)NULL);
	min_hit += damage_bonus;
	max_hit += damage_bonus;
}

4) Completely replace those lines with the following:

Code:
		// ***************************************************************
		// *** Calculate the damage bonus, if applicable, for this hit ***
		// ***************************************************************

#ifndef EQEMU_NO_WEAPON_DAMAGE_BONUS

		// If you include the preprocessor directive "#define EQEMU_NO_WEAPON_DAMAGE_BONUS", that indicates that you do not
		// want damage bonuses added to weapon damage at all. This feature was requested by ChaosSlayer on the EQEmu Forums.
		//
		// This is not recommended for normal usage, as the damage bonus represents a non-trivial component of the DPS output
		// of weapons wielded by higher-level melee characters (especially for two-handed weapons).

		if( Hand == 13 && GetLevel() >= 28 && IsWarriorClass() )
		{
			// Damage bonuses apply only to hits from the main hand (Hand == 13) by characters level 28 and above
			// who belong to a melee class. If we're here, then all of these conditions apply.

			int8 ucDamageBonus = GetWeaponDamageBonus( weapon ? weapon->GetItem() : (const Item_Struct*) NULL );

			min_hit += (int) ucDamageBonus;
			max_hit += (int) ucDamageBonus;
		}
#endif

5) Scroll further down in Attack.cpp to around line #1952 (assuming you added the code in step 4), and look for the function that starts with the line:

Code:
int Mob::GetWeaponDamageBonus(const Item_Struct* Weapon)

6) Replace that entire function (it should take up 36 lines, ranging from about line #1952 down to #1988 ) with the following (much larger!) function:

Code:
int8 Mob::GetWeaponDamageBonus( const Item_Struct *Weapon )
{
	// This function calculates and returns the damage bonus for the weapon identified by the parameter "Weapon".
	// Modified 9/21/2008 by Cantus


	// Assert: This function should only be called for hits by the mainhand, as damage bonuses apply only to the
	//         weapon in the primary slot. Be sure to check that Hand == 13 before calling.

	// Assert: The caller should ensure that Weapon is actually a weapon before calling this function.
	//         The ItemInst::IsWeapon() method can be used to quickly determine this.

	// Assert: This function should not be called if the player's level is below 28, as damage bonuses do not begin
	//         to apply until level 28.

	// Assert: This function should not be called unless the player is a melee class, as casters do not receive a damage bonus.


	if( Weapon == NULL || Weapon->ItemType == ItemType1HS || Weapon->ItemType == ItemType1HB || Weapon->ItemType == ItemTypeHand2Hand || Weapon->ItemType == ItemTypePierce )
	{
		// The weapon in the player's main (primary) hand is a one-handed weapon, or there is no item equipped at all.
		//
		// According to player posts on Allakhazam, 1H damage bonuses apply to bare fists (nothing equipped in the mainhand,
		// as indicated by Weapon == NULL).
		//
		// The following formula returns the correct damage bonus for all 1H weapons:

		return (int8) ((GetLevel() - 25) / 3);
	}

	// If we've gotten to this point, the weapon in the mainhand is a two-handed weapon.
	// Calculating damage bonuses for 2H weapons is more complicated, as it's based on PC level AND the delay of the weapon.
	// The formula to calculate 2H bonuses is HIDEOUS. It's a huge conglomeration of ternary operators and multiple operations.
	//
	// The following is a hybrid approach. In cases where the Level and Delay merit a formula that does not use many operators,
	// the formula is used. In other cases, lookup tables are used for speed.
	// Though the following code may look bloated and ridiculous, it's actually a very efficient way of calculating these bonuses.

	// Player Level is used several times in the code below, so save it into a variable.
	// If GetLevel() were an ordinary function, this would DEFINITELY make sense, as it'd cut back on all of the function calling
	// overhead involved with multiple calls to GetLevel(). But in this case, GetLevel() is a simple, inline accessor method.
	// So it probably doesn't matter. If anyone knows for certain that there is no overhead involved with calling GetLevel(),
	// as I suspect, then please feel free to delete the following line, and replace all occurences of "ucPlayerLevel" with "GetLevel()".
	int8 ucPlayerLevel = (int8) GetLevel();


	// The following may look cleaner, and would certainly be easier to understand, if it was
	// a simple 53x150 cell matrix.
	//
	// However, that would occupy 7,950 Bytes of memory (7.76 KB), and would likely result
	// in "thrashing the cache" when performing lookups.
	//
	// Initially, I thought the best approach would be to reverse-engineer the formula used by
	// Sony/Verant to calculate these 2H weapon damage bonuses. But the more than Reno and I
	// worked on figuring out this formula, the more we're concluded that the formula itself ugly
	// (that is, it contains so many operations and conditionals that it's fairly CPU intensive).
	// Because of that, we're decided that, in most cases, a lookup table is the most efficient way
	// to calculate these damage bonuses.
	//
	// The code below is a hybrid between a pure formulaic approach and a pure, brute-force
	// lookup table. In cases where a formula is the best bet, I use a formula. In other places
	// where a formula would be ugly, I use a lookup table in the interests of speed. 


	if( Weapon->Delay <= 27 )
	{
		// Damage Bonuses for all 2H weapons with delays of 27 or less are identical.
		// They are the same as the damage bonus would be for a corresponding 1H weapon, plus one.
		// This formula applies to all levels 28-80, and will probably continue to apply if
		// the level cap on Live ever is increased beyond 80.

		return (ucPlayerLevel - 22) / 3;
	}


	if( ucPlayerLevel == 65 && Weapon->Delay <= 59 )
	{
		// Consider these two facts:
		//   * Level 65 is the maximum level on many EQ Emu servers.
		//   * If you listed the levels of all characters logged on to a server, odds are that the number you'll
		//     see most frequently is level 65. That is, there are more level 65 toons than any other single level.
		//
		// Therefore, if we can optimize this function for level 65 toons, we're speeding up the server!
		//
		// With that goal in mind, I create an array of Damage Bonuses for level 65 characters wielding 2H weapons with
		// delays between 28 and 59 (inclusive). I suspect that this one small lookup array will therefore handle
		// many of the calls to this function.

		static const int8 ucLevel65DamageBonusesForDelays28to59[] = {35, 35, 36, 36, 37, 37, 38, 38, 39, 39, 40, 40, 42, 42, 42, 45, 45, 47, 48, 49, 49, 51, 51, 52, 53, 54, 54, 56, 56, 57, 58, 59};

		return ucLevel65DamageBonusesForDelays28to59[Weapon->Delay-28];
	}


	if( ucPlayerLevel > 65 )
	{
		if( ucPlayerLevel > 80 )
		{
			// As level 80 is currently the highest achievable level on Live, we only include
			// damage bonus information up to this level.
			//
			// If there is a custom EQEmu server that allows players to level beyond 80, the
			// damage bonus for their 2H weapons will simply not increase beyond their damage
			// bonus at level 80.

			ucPlayerLevel = 80;
		}

		// Lucy does not list a chart of damage bonuses for players levels 66+,
		// so my original version of this function just applied the level 65 damage
		// bonus for level 66+ toons. That sucked for higher level toons, as their
		// 2H weapons stopped ramping up in DPS as they leveled past 65.
		//
		// Thanks to the efforts of two guys, this is no longer the case:
		//
		// Janusd (Zetrakyl) ran a nifty query against the PEQ item database to list
		// the name of an example 2H weapon that represents each possible unique 2H delay.
		//
		// Romai then wrote an excellent script to automatically look up each of those
		// weapons, open the Lucy item page associated with it, and iterate through all
		// levels in the range 66 - 80. He saved the damage bonus for that weapon for
		// each level, and that forms the basis of the lookup tables below.

		if( Weapon->Delay <= 59 )
		{
			static const int8 ucDelay28to59Levels66to80[32][15]=
			{
			/*							Level:								*/
			/*	 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80	*/

				{36, 37, 38, 39, 41, 42, 43, 44, 45, 47, 49, 49, 49, 50, 53},	/* Delay = 28 */
				{36, 38, 38, 39, 42, 43, 43, 45, 46, 48, 49, 50, 51, 52, 54},	/* Delay = 29 */
				{37, 38, 39, 40, 43, 43, 44, 46, 47, 48, 50, 51, 52, 53, 55},	/* Delay = 30 */
				{37, 39, 40, 40, 43, 44, 45, 46, 47, 49, 51, 52, 52, 52, 54},	/* Delay = 31 */
				{38, 39, 40, 41, 44, 45, 45, 47, 48, 48, 50, 52, 53, 55, 57},	/* Delay = 32 */
				{38, 40, 41, 41, 44, 45, 46, 48, 49, 50, 52, 53, 54, 56, 58},	/* Delay = 33 */
				{39, 40, 41, 42, 45, 46, 47, 48, 49, 51, 53, 54, 55, 57, 58},	/* Delay = 34 */
				{39, 41, 42, 43, 46, 46, 47, 49, 50, 52, 54, 55, 56, 57, 59},	/* Delay = 35 */
				{40, 41, 42, 43, 46, 47, 48, 50, 51, 53, 55, 55, 56, 58, 60},	/* Delay = 36 */
				{40, 42, 43, 44, 47, 48, 49, 50, 51, 53, 55, 56, 57, 59, 61},	/* Delay = 37 */
				{41, 42, 43, 44, 47, 48, 49, 51, 52, 54, 56, 57, 58, 60, 62},	/* Delay = 38 */
				{41, 43, 44, 45, 48, 49, 50, 52, 53, 55, 57, 58, 59, 61, 63},	/* Delay = 39 */
				{43, 45, 46, 47, 50, 51, 52, 54, 55, 57, 59, 60, 61, 63, 65},	/* Delay = 40 */
				{43, 45, 46, 47, 50, 51, 52, 54, 55, 57, 59, 60, 61, 63, 65},	/* Delay = 41 */
				{44, 46, 47, 48, 51, 52, 53, 55, 56, 58, 60, 61, 62, 64, 66},	/* Delay = 42 */
				{46, 48, 49, 50, 53, 54, 55, 58, 59, 61, 63, 64, 65, 67, 69},	/* Delay = 43 */
				{47, 49, 50, 51, 54, 55, 56, 58, 59, 61, 64, 65, 66, 68, 70},	/* Delay = 44 */
				{48, 50, 51, 52, 56, 57, 58, 60, 61, 63, 65, 66, 68, 70, 72},	/* Delay = 45 */
				{50, 52, 53, 54, 57, 58, 59, 62, 63, 65, 67, 68, 69, 71, 74},	/* Delay = 46 */
				{50, 52, 53, 55, 58, 59, 60, 62, 63, 66, 68, 69, 70, 72, 74},	/* Delay = 47 */
				{51, 53, 54, 55, 58, 60, 61, 63, 64, 66, 69, 69, 71, 73, 75},	/* Delay = 48 */
				{52, 54, 55, 57, 60, 61, 62, 65, 66, 68, 70, 71, 73, 75, 77},	/* Delay = 49 */
				{53, 55, 56, 57, 61, 62, 63, 65, 67, 69, 71, 72, 74, 76, 78},	/* Delay = 50 */
				{53, 55, 57, 58, 61, 62, 64, 66, 67, 69, 72, 73, 74, 77, 79},	/* Delay = 51 */
				{55, 57, 58, 59, 63, 64, 65, 68, 69, 71, 74, 75, 76, 78, 81},	/* Delay = 52 */
				{57, 55, 59, 60, 63, 65, 66, 68, 70, 72, 74, 76, 77, 79, 82},	/* Delay = 53 */
				{56, 58, 59, 61, 64, 65, 67, 69, 70, 73, 75, 76, 78, 80, 82},	/* Delay = 54 */
				{57, 59, 61, 62, 66, 67, 68, 71, 72, 74, 77, 78, 80, 82, 84},	/* Delay = 55 */
				{58, 60, 61, 63, 66, 68, 69, 71, 73, 75, 78, 79, 80, 83, 85},	/* Delay = 56 */

				/* Important Note: Janusd's search for 2H weapons did not find	*/
				/* any 2H weapon with a delay of 57. Therefore the values below	*/
				/* are interpolated, not exact!									*/
				{59, 61, 62, 64, 67, 69, 70, 72, 74, 76, 77, 78, 81, 84, 86},	/* Delay = 57 INTERPOLATED */

				{60, 62, 63, 65, 68, 70, 71, 74, 75, 78, 80, 81, 83, 85, 88},	/* Delay = 58 */

				/* Important Note: Janusd's search for 2H weapons did not find	*/
				/* any 2H weapon with a delay of 59. Therefore the values below	*/
				/* are interpolated, not exact!									*/
				{60, 62, 64, 65, 69, 70, 72, 74, 76, 78, 81, 82, 84, 86, 89},	/* Delay = 59 INTERPOLATED */
			};

			return ucDelay28to59Levels66to80[Weapon->Delay-28][ucPlayerLevel-66];
		}
		else
		{
			// Delay is 60+ 

			const static int8 ucDelayOver59Levels66to80[6][15] =
			{
			/*							Level:								*/
			/*	 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80	*/

				{61, 63, 65, 66, 70, 71, 73, 75, 77, 79, 82, 83, 85, 87, 90},				/* Delay = 60 */
				{65, 68, 69, 71, 75, 76, 78, 80, 82, 85, 87, 89, 91, 93, 96},				/* Delay = 65 */

				/* Important Note: Currently, the only 2H weapon with a delay	*/
				/* of 66 is not player equippable (it's None/None). So I'm		*/
				/* leaving it commented out to keep this table smaller.			*/
				//{66, 68, 70, 71, 75, 77, 78, 81, 83, 85, 88, 90, 91, 94, 97},				/* Delay = 66 */

				{70, 72, 74, 76, 80, 81, 83, 86, 88, 88, 90, 95, 97, 99, 102},				/* Delay = 70 */
				{82, 85, 87, 89, 89, 94, 98, 101, 103, 106, 109, 111, 114, 117, 120},		/* Delay = 85 */
				{90, 93, 96, 98, 103, 105, 107, 111, 113, 116, 120, 122, 125, 128, 131},	/* Delay = 95 */

				/* Important Note: Currently, the only 2H weapons with delay	*/
				/* 100 are GM-only items purchased from vendors in Sunset Home	*/
				/* (cshome). Because they are highly unlikely to be used in		*/
				/* combat, I'm commenting it out to keep the table smaller.		*/
				//{95, 98, 101, 103, 108, 110, 113, 116, 119, 122, 126, 128, 131, 134, 138},/* Delay = 100 */

				{136, 140, 144, 148, 154, 157, 161, 166, 170, 174, 179, 183, 187, 191, 196}	/* Delay = 150 */
			};

			if( Weapon->Delay < 65 )
			{
				return ucDelayOver59Levels66to80[0][ucPlayerLevel-66];
			}
			else if( Weapon->Delay < 70 )
			{
				return ucDelayOver59Levels66to80[1][ucPlayerLevel-66];
			}
			else if( Weapon->Delay < 85 )
			{
				return ucDelayOver59Levels66to80[2][ucPlayerLevel-66];
			}
			else if( Weapon->Delay < 95 )
			{
				return ucDelayOver59Levels66to80[3][ucPlayerLevel-66];
			}
			else if( Weapon->Delay < 150 )
			{
				return ucDelayOver59Levels66to80[4][ucPlayerLevel-66];
			}
			else
			{
				return ucDelayOver59Levels66to80[5][ucPlayerLevel-66];
			}
		}
	}


	// If we've gotten to this point in the function without hitting a return statement,
	// we know that the character's level is between 28 and 65, and that the 2H weapon's
	// delay is 28 or higher.

	// The Damage Bonus values returned by this function (in the level 28-65 range) are
	// based on a table of 2H Weapon Damage Bonuses provided by Lucy at the following address:
	// http://lucy.allakhazam.com/dmgbonus.html

	if( Weapon->Delay <= 39 )
	{
		if( ucPlayerLevel <= 53)
		{
			// The Damage Bonus for all 2H weapons with delays between 28 and 39 (inclusive) is the same for players level 53 and below...
			static const int8 ucDelay28to39LevelUnder54[] = {1, 1, 2, 3, 3, 3, 4, 5, 5, 6, 6, 6, 8, 8, 8, 9, 9, 10, 11, 11, 11, 12, 13, 14, 16, 17};

			// As a note: The following formula accurately calculates damage bonuses for 2H weapons with delays in the range 28-39 (inclusive)
			// for characters levels 28-50 (inclusive):
			// return ( (ucPlayerLevel - 22) / 3 ) + ( (ucPlayerLevel - 25) / 5 );
			//
			// However, the small lookup array used above is actually much faster. So we'll just use it instead of the formula
			//
			// (Thanks to Reno for helping figure out the above formula!)

			return ucDelay28to39LevelUnder54[ucPlayerLevel-28];
		}
		else
		{
			// Use a matrix to look up the damage bonus for 2H weapons with delays between 28 and 39 wielded by characters level 54 and above.
			static const int8 ucDelay28to39Level54to64[12][11] =
			{
			/*						Level:					*/
			/*	 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64	*/

				{17, 21, 21, 23, 25, 26, 28, 30, 31, 31, 33},	/* Delay = 28 */
				{17, 21, 22, 23, 25, 26, 29, 30, 31, 32, 34},	/* Delay = 29 */
				{18, 21, 22, 23, 25, 27, 29, 31, 32, 32, 34},	/* Delay = 30 */
				{18, 21, 22, 23, 25, 27, 29, 31, 32, 33, 34},	/* Delay = 31 */
				{18, 21, 22, 24, 26, 27, 30, 32, 32, 33, 35},	/* Delay = 32 */
				{18, 21, 22, 24, 26, 27, 30, 32, 33, 34, 35},	/* Delay = 33 */
				{18, 22, 22, 24, 26, 28, 30, 32, 33, 34, 36},	/* Delay = 34 */
				{18, 22, 23, 24, 26, 28, 31, 33, 34, 34, 36},	/* Delay = 35 */
				{18, 22, 23, 25, 27, 28, 31, 33, 34, 35, 37},	/* Delay = 36 */
				{18, 22, 23, 25, 27, 29, 31, 33, 34, 35, 37},	/* Delay = 37 */
				{18, 22, 23, 25, 27, 29, 32, 34, 35, 36, 38},	/* Delay = 38 */
				{18, 22, 23, 25, 27, 29, 32, 34, 35, 36, 38}	/* Delay = 39 */
			};

			return ucDelay28to39Level54to64[Weapon->Delay-28][ucPlayerLevel-54];
		}
	}
	else if( Weapon->Delay <= 59 )
	{
		if( ucPlayerLevel <= 52 )
		{
			if( Weapon->Delay <= 45 )
			{
				static const int8 ucDelay40to45Levels28to52[6][25] =
				{
				/*												Level:														*/
				/*	 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52		*/

					{2,  2,  3,  4,  4,  4,  5,  6,  6,  7,  7,  7,  9,  9,  9,  10, 10, 11, 12, 12, 12, 13, 14, 16, 18},	/* Delay = 40 */
					{2,  2,  3,  4,  4,  4,  5,  6,  6,  7,  7,  7,  9,  9,  9,  10, 10, 11, 12, 12, 12, 13, 14, 16, 18},	/* Delay = 41 */
					{2,  2,  3,  4,  4,  4,  5,  6,  6,  7,  7,  7,  9,  9,  9,  10, 10, 11, 12, 12, 12, 13, 14, 16, 18},	/* Delay = 42 */
					{4,  4,  5,  6,  6,  6,  7,  8,  8,  9,  9,  9,  11, 11, 11, 12, 12, 13, 14, 14, 14, 15, 16, 18, 20},	/* Delay = 43 */
					{4,  4,  5,  6,  6,  6,  7,  8,  8,  9,  9,  9,  11, 11, 11, 12, 12, 13, 14, 14, 14, 15, 16, 18, 20},	/* Delay = 44 */
					{5,  5,  6,  7,  7,  7,  8,  9,  9,  10, 10, 10, 12, 12, 12, 13, 13, 14, 15, 15, 15, 16, 17, 19, 21} 	/* Delay = 45 */
				};

				return ucDelay40to45Levels28to52[Weapon->Delay-40][ucPlayerLevel-28];
			}
			else
			{
				static const int8 ucDelay46Levels28to52[] = {6,  6,  7,  8,  8,  8,  9,  10, 10, 11, 11, 11, 13, 13, 13, 14, 14, 15, 16, 16, 16, 17, 18, 20, 22};

				return ucDelay46Levels28to52[ucPlayerLevel-28] + ((Weapon->Delay-46) / 3);
			}
		}
		else
		{
			// Player is in the level range 53 - 64

			// Calculating damage bonus for 2H weapons with a delay between 40 and 59 (inclusive) involves, unforunately, a brute-force matrix lookup.
			static const int8 ucDelay40to59Levels53to64[20][37] = 
			{
			/*						Level:							*/
			/*	 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64		*/

				{19, 20, 24, 25, 27, 29, 31, 34, 36, 37, 38, 40},	/* Delay = 40 */
				{19, 20, 24, 25, 27, 29, 31, 34, 36, 37, 38, 40},	/* Delay = 41 */
				{19, 20, 24, 25, 27, 29, 31, 34, 36, 37, 38, 40},	/* Delay = 42 */
				{21, 22, 26, 27, 29, 31, 33, 37, 39, 40, 41, 43},	/* Delay = 43 */
				{21, 22, 26, 27, 29, 32, 34, 37, 39, 40, 41, 43},	/* Delay = 44 */
				{22, 23, 27, 28, 31, 33, 35, 38, 40, 42, 43, 45},	/* Delay = 45 */
				{23, 24, 28, 30, 32, 34, 36, 40, 42, 43, 44, 46},	/* Delay = 46 */
				{23, 24, 29, 30, 32, 34, 37, 40, 42, 43, 44, 47},	/* Delay = 47 */
				{23, 24, 29, 30, 32, 35, 37, 40, 43, 44, 45, 47},	/* Delay = 48 */
				{24, 25, 30, 31, 34, 36, 38, 42, 44, 45, 46, 49},	/* Delay = 49 */
				{24, 26, 30, 31, 34, 36, 39, 42, 44, 46, 47, 49},	/* Delay = 50 */
				{24, 26, 30, 31, 34, 36, 39, 42, 45, 46, 47, 49},	/* Delay = 51 */
				{25, 27, 31, 33, 35, 38, 40, 44, 46, 47, 49, 51},	/* Delay = 52 */
				{25, 27, 31, 33, 35, 38, 40, 44, 46, 48, 49, 51},	/* Delay = 53 */
				{26, 27, 32, 33, 36, 38, 41, 44, 47, 48, 49, 52},	/* Delay = 54 */
				{27, 28, 33, 34, 37, 39, 42, 46, 48, 50, 51, 53},	/* Delay = 55 */
				{27, 28, 33, 34, 37, 40, 42, 46, 49, 50, 51, 54},	/* Delay = 56 */
				{27, 28, 33, 34, 37, 40, 43, 46, 49, 50, 52, 54},	/* Delay = 57 */
				{28, 29, 34, 36, 39, 41, 44, 48, 50, 52, 53, 56},	/* Delay = 58 */
				{28, 29, 34, 36, 39, 41, 44, 48, 51, 52, 54, 56}	/* Delay = 59 */
			};

			return ucDelay40to59Levels53to64[Weapon->Delay-40][ucPlayerLevel-53];
		}
	}
	else
	{
		// The following table allows us to look up Damage Bonuses for weapons with delays greater than or equal to 60.
		//
		// There aren't a lot of 2H weapons with a delay greater than 60. In fact, both a database and Lucy search run by janusd confirm
		// that the only unique 2H delays greater than 60 are: 65, 70, 85, 95, and 150.
		//
		// To be fair, there are also weapons with delays of 66 and 100. But they are either not equippable (None/None), or are
		// only available to GMs from merchants in Sunset Home (cshome). In order to keep this table "lean and mean", I will not
		// include the values for delays 66 and 100. If they ever are wielded, the 66 delay weapon will use the 65 delay bonuses,
		// and the 100 delay weapon will use the 95 delay bonuses. So it's not a big deal.
		//
		// Still, if someone in the future decides that they do want to include them, here are the tables for these two delays:
		//
		// {12, 12, 13, 14, 14, 14, 15, 16, 16, 17, 17, 17, 19, 19, 19, 20, 20, 21, 22, 22, 22, 23, 24, 26, 29, 30, 32, 37, 39, 42, 45, 48, 53, 55, 57, 59, 61, 64}		/* Delay = 66 */
		// {24, 24, 25, 26, 26, 26, 27, 28, 28, 29, 29, 29, 31, 31, 31, 32, 32, 33, 34, 34, 34, 35, 36, 39, 43, 45, 48, 55, 57, 62, 66, 71, 77, 80, 83, 85, 89, 92}		/* Delay = 100 */
		//
		// In case there are 2H weapons added in the future with delays other than those listed above (and until the damage bonuses
		// associated with that new delay are added to this function), this function is designed to do the following:
		//
		//		For weapons with delays in the range 60-64, use the Damage Bonus that would apply to a 2H weapon with delay 60.
		//		For weapons with delays in the range 65-69, use the Damage Bonus that would apply to a 2H weapon with delay 65
		//		For weapons with delays in the range 70-84, use the Damage Bonus that would apply to a 2H weapon with delay 70.
		//		For weapons with delays in the range 85-94, use the Damage Bonus that would apply to a 2H weapon with delay 85.
		//		For weapons with delays in the range 95-149, use the Damage Bonus that would apply to a 2H weapon with delay 95.
		//		For weapons with delays 150 or higher, use the Damage Bonus that would apply to a 2H weapon with delay 150.

		static const int8 ucDelayOver59Levels28to65[6][38] =
		{
		/*																	Level:																					*/
		/*	 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64. 65	*/

			{10, 10, 11, 12, 12, 12, 13, 14, 14, 15, 15, 15, 17, 17, 17, 18, 18, 19, 20, 20, 20, 21, 22, 24, 27, 28, 30, 35, 36, 39, 42, 45, 49, 51, 53, 54, 57, 59},		/* Delay = 60 */
			{12, 12, 13, 14, 14, 14, 15, 16, 16, 17, 17, 17, 19, 19, 19, 20, 20, 21, 22, 22, 22, 23, 24, 26, 29, 30, 32, 37, 39, 42, 45, 48, 52, 55, 57, 58, 61, 63},		/* Delay = 65 */
			{14, 14, 15, 16, 16, 16, 17, 18, 18, 19, 19, 19, 21, 21, 21, 22, 22, 23, 24, 24, 24, 25, 26, 28, 31, 33, 35, 40, 42, 45, 48, 52, 56, 59, 61, 62, 65, 68},		/* Delay = 70 */
			{19, 19, 20, 21, 21, 21, 22, 23, 23, 24, 24, 24, 26, 26, 26, 27, 27, 28, 29, 29, 29, 30, 31, 34, 37, 39, 41, 47, 49, 54, 57, 61, 66, 69, 72, 74, 77, 80},		/* Delay = 85 */
			{22, 22, 23, 24, 24, 24, 25, 26, 26, 27, 27, 27, 29, 29, 29, 30, 30, 31, 32, 32, 32, 33, 34, 37, 40, 43, 45, 52, 54, 59, 62, 67, 73, 76, 79, 81, 84, 88},		/* Delay = 95 */
			{40, 40, 41, 42, 42, 42, 43, 44, 44, 45, 45, 45, 47, 47, 47, 48, 48, 49, 50, 50, 50, 51, 52, 56, 61, 65, 69, 78, 82, 89, 94, 102, 110, 115, 119, 122, 127, 132}	/* Delay = 150 */
		};

		if( Weapon->Delay < 65 )
		{
			return ucDelayOver59Levels28to65[0][ucPlayerLevel-28];
		}
		else if( Weapon->Delay < 70 )
		{
			return ucDelayOver59Levels28to65[1][ucPlayerLevel-28];
		}
		else if( Weapon->Delay < 85 )
		{
			return ucDelayOver59Levels28to65[2][ucPlayerLevel-28];
		}
		else if( Weapon->Delay < 95 )
		{
			return ucDelayOver59Levels28to65[3][ucPlayerLevel-28];
		}
		else if( Weapon->Delay < 150 )
		{
			return ucDelayOver59Levels28to65[4][ucPlayerLevel-28];
		}
		else
		{
			return ucDelayOver59Levels28to65[5][ucPlayerLevel-28];
		}
	}
}

7) Compile, and let me know if it works! I'm sorry, but I wound up working late and never got home to actually test this. If there are any errors, please let me know, and I'll fix them ASAP!
Reply With Quote
  #17  
Old 09-23-2008, 01:49 AM
AndMetal
Developer
 
Join Date: Mar 2007
Location: Ohio
Posts: 648
Default

Quote:
Originally Posted by trevius View Post
So, where does Calculate2HDamageBonus function go? I can't figure out where to put it and it errors as shown in my previous post if I put it directly in my attack.cpp.
You should be able to define it in zone/mob.h, just like GetWeaponDamageBonus is. It's all a part of the Mob class, which is basically what the Mob:: prefix means.
__________________
GM-Impossible of 'A work in progress'
A non-legit PEQ DB server
How to create your own non-legit server

My Contributions to the Wiki
Reply With Quote
  #18  
Old 09-23-2008, 06:43 AM
renoofturks1
Sarnak
 
Join Date: Jan 2008
Posts: 60
Default

It's all merged together in that new set he posted. No need to define a new function.
Reply With Quote
  #19  
Old 09-23-2008, 07:02 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

if you wanted to add a rule in for damage bonuses, all you should have to do is add:

In attack.cpp
Code:
if(RuleB(Combat, UseDamageBonus)){

}
So that it encompasses your damage bonus calculations completely.

Then, just add it to the ruletypes.h file like this:

Code:
RULE_BOOL ( Combat, UseDamageBonus, true) //default is true to enable damage bonuses
into the combat section of the file.

Then just add the optional SQL if you want to disable it. But you don't even need the SQL if you want to leave it enabled, since the default is true:

Optional SQL:
Code:
Insert into rule_values values (0, 'Combat:UseDamageBonus', true);
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #20  
Old 09-23-2008, 08:30 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Oh and I got the latest submission in this thread compiled and tested on my server. It looks good to me! Definitely better than the current code in the emulator lol.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #21  
Old 09-23-2008, 10:45 AM
ChaosSlayer
Demi-God
 
Join Date: May 2007
Posts: 1,032
Default

very good Trev! I hope it makes in with the Rule you described
Reply With Quote
  #22  
Old 09-23-2008, 11:35 AM
Cantus
Fire Beetle
 
Join Date: Sep 2008
Location: New York
Posts: 18
Default

Quote:
Originally Posted by ChaosSlayer View Post
very good Trev! I hope it makes in with the Rule you described
Heya, Chaos and Trev!

ChaosSlayer's suggestion of a flag to allow server administrators to turn damage bonuses on and off is already in the code I posted above. It is not implemented as a standard type of rule as Trevius suggests, but there's a reason for that. Please let me explain, but first, here's the code snippet that mentions the change:

Code:
#ifndef EQEMU_NO_WEAPON_DAMAGE_BONUS

// If you include the preprocessor directive "#define EQEMU_NO_WEAPON_DAMAGE_BONUS", that indicates that you do not
// want damage bonuses added to weapon damage at all. This feature was requested by ChaosSlayer on the EQEmu Forums.
//
// This is not recommended for normal usage, as the damage bonus represents a non-trivial component of the DPS output
// of weapons wielded by higher-level melee characters (especially for two-handed weapons).
All you'd have to do to disable Damage Bonuses, Chaos, is include the line ""#define EQEMU_NO_WEAPON_DAMAGE_BONUS" anywhere in a file that gets parsed before Attack.cpp (or even at the top of Attack.cpp), and damage bonuses will never apply on your server. If you want to enable them in the future, just remove that line and re-compile.


While I understand ChaosSlayer's points that it would be nice to try things out without damage bonuses, and rely entirely upon the native damage/delay of the weapon, the fact is that most servers are not going to choose to disable damage bonuses.

If we implemented the check for whether or not the server administrator wants damage bonuses at runtime, using the standard Rule method that Trevius suggests, that means that every time a melee toon lands a hit with a 2H weapon, there's the extra overhead of calling that Rule function.

I know, that doesn't seem like a big deal. But keep in mind that if someone Rampages with an Earthshaker, this code could potentially be called hundreds of times in a split second, for example. Or if someone pops a riposte discipline like Furious or Whirlwind with lots of mobs hitting them. Or if a lot of toons are raiding a mob, and they're landing many hits at once (double, triple, quad hits against the target). In all of these cases, this function will be called many times in rapid succession, so even a small difference -- like the function call overhead involved with checking a rule in the traditional way -- may become significant.

So, as this is one of the places where we want things to be as efficient as possible, I used a C/C++ preprocessor directive to allow folks like ChaosSlayer to turn on or off damage bonuses. It is not as slick as Trev's suggestion, as it must be done by adding a line of code and then re-compiling the server, vs. making a database change during runtime, but I still think it's in everyone's best interests -- as it does not slow down processing by involving needless function call overhead every time a 2H weapon hits.

I may be wrong... that happens a lot! So if you prefer, feel free to make it a rule, or just ask me and I'll do it for you!
Reply With Quote
  #23  
Old 09-23-2008, 11:46 AM
Cantus
Fire Beetle
 
Join Date: Sep 2008
Location: New York
Posts: 18
Default

Quote:
Originally Posted by trevius View Post
Oh and I got the latest submission in this thread compiled and tested on my server. It looks good to me! Definitely better than the current code in the emulator lol.
That's great to hear, Trevius!

It may already be too late, as you've re-built your server using the new code. But if you still have an old build around on a test machine, it'd be neat to run a quick test to make sure things are working as expected.

What I'd recommend is either use your GM to twink a toon with a nice, slow 2H weapon like "A Weighted Axe", or just farm one in the Burning Woods. They drop commonly.

That's a perfect 2H weapon to test with, as its native ratio sucks, but due to its slow speed, it receives a massive damage bonus.

Fight a mob with it on a server compiled with the old code. Watch for the average and maximum hit (or just go all out and parse it).

Then re-build that server with the new damage bonus code. Attack a similar mob, and watch the damage output. It should be greatly increased if things are working well.
Reply With Quote
  #24  
Old 09-23-2008, 01:35 PM
Derision
Developer
 
Join Date: Feb 2004
Location: UK
Posts: 1,540
Default

Quote:
Originally Posted by renoofturks1 View Post
doesn't that involve a call to MySQL to get the rule? Or does it store them somewhere else at start-up? So, every time someone make's a hit, it calls MySQL, checks the rule, then parses the DMGB code? Correct me if i am wrong?
I won't make any comment on whether you should use a rule in this particular case, but the rules are all setup when the zone loads, so there is no database access required once the zone is initialised.

Out of interest, I compiled zone with VC++ 2008 Express with optimise for speed selected, and used IDA to look at the code it generates when a boolean rule is checked. It is only 4 machine instructions:

Code:
    mov     edx, dword_A48964   ; Load Rule base pointer
    add     esp, 18h
    cmp     [edx+162h], bl      ; Compare Rule offset 0x162 to 0 (register bl is set to zero earlier in the code)
    jz      short loc_5F4950    ; Jump if Rule set to false
which would probably take a few microseconds to execute, so there wouldn't be any performance hit if you chose to implement this using a rule.

Last edited by Derision; 09-23-2008 at 09:37 PM..
Reply With Quote
  #25  
Old 09-23-2008, 01:38 PM
renoofturks1
Sarnak
 
Join Date: Jan 2008
Posts: 60
Default

That answer's my question. Thanks!
Reply With Quote
  #26  
Old 09-23-2008, 05:33 PM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Cantus, your way would work fine for people who compile their own source. But, many people use the win32 binaries if they are running a windows server.

If the rule did have any performance impact at all, I don't think it would be worth making 99% of all servers suffer so 1% could have a special rule for it :P

But if there is no performance impact, the rule would be best. If there is, then your way would be best, so there is still an option available if someone absolutely must disable damage bonus for any reason.

Again, I am personally against having a rule in place for this. I just don't see a need. Unless you are running a server with 100% custom created weapons, then your weapon balance will be off if you don't have damage bonuses enabled.

I only posted the rule, so it could be added if needed. It is a pretty simple option. Though, it would be nice if there was a way to store that weapon bonus calculation and use it over and over instead of having to calculate it with every swing. I know for sure on my server that even before this change, anyone rampaging more than like 20 mobs is risking a zone crash. I haven't had feedback on the new code yet to see if it still causes that, but not many people use 2H weapons on my server due to them sucking in the first place. I don't think it is only damage bonus that is messed up with 2H weapons. I think there seems to be some weird damage capping going on as well which makes them almost pointless.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #27  
Old 09-23-2008, 06:46 PM
renoofturks1
Sarnak
 
Join Date: Jan 2008
Posts: 60
Default

All the testing I have done put's the 2 hander's where they should be now. If there are other issue's, bring them up and I'll see about getting them kinked out
Reply With Quote
  #28  
Old 09-24-2008, 10:43 AM
Cantus
Fire Beetle
 
Join Date: Sep 2008
Location: New York
Posts: 18
Default

Derision,

Excellent information there. I apologize that I don't have the code in front of me... are the rule-checking functions inline? In other words, does the ASM that you posted also take into account function call overhead, unwinding the stack, etc. that would result from invoking a non-inline function?

Either way, I think you're right: The performance hit for implementing a rule is trivial. Though your point that the rule is queried from the DB only when a zone first boots is important for folks like ChaosSlayer to remember. If you toggle Damage Bonuses on or off in the DB, you'll have to reboot the zone for it to take effect.



Trevius,

Excellent points, as always! I think the info that Derision provided shows that adding in a rule adds only a tiny amount of overhead to the code, so let's go that route. I'll make the addition of that rule when I also add the code to continue ramping up Damage Bonus for levels 81+. Hopefully should have that posted in a day or two (work's been killing me this week -- very little free time. Sorry.)

You make another excellent point here. I may be able to put your fears to rest:

Quote:
Originally Posted by trevius View Post
it would be nice if there was a way to store that weapon bonus calculation and use it over and over instead of having to calculate it with every swing. I know for sure on my server that even before this change, anyone rampaging more than like 20 mobs is risking a zone crash. I haven't had feedback on the new code yet to see if it still causes that
That was a big concern for me, too. So I ran some tests. There's no problem at all. In fact, storing damage bonuses in an associative array along with character and/or weapon name would be MUCH slower and more complicated than just looking them up for each hit. At that point, you may as well go with a full-size, brute-force lookup table. Lookup tables are great and fast, but only if they're relatively small and cacheable.

Here is some data from a test I ran, as posted to our guild messageboard:

Quote:
I was worried about whether this function would slow down the server when, for example, a Warrior Shakerpages (uses an Earthshaker, which is a 2H weapon, along with Rampage to simultaneously hit a large number of mobs around him/her). So I ran a test.

On a Pentium II 450 MHz with 256 MB of RAM (the worst computer I have available), it takes:

227 milliseconds (0.227 seconds) to call the function 12,231,000 times. That's twelve million times.

That means that 122,310 Warriors could Rampage on 100 mobs each at precisely the same moment and only delay a server running on fifteen year old hardware for less than a quarter of a second.

At its peak, there were 430,000 subscriptions to Live EQ. Note that not all of those people who had an account were logged on at once. And they were certainly not all logged onto the same server. So we're never going to have anywhere close to 122,310 people logged on to the same EQEmu server at the same time.

Not to mention that there aren't 12 million mobs available across all of the zones in EQ to Shakerpage. And there aren't enough Earthshakers to be distributed to those 122,310 warriors.

Long story short: The function is smokin' fast. If anyone tells us that they don't like the fact that it uses some lookup tables, well, they're kinda showing their ignorance .
I can assure you that there is no problem with this function, and no added risk of server crash or latency. The Damage Bonus solution as posted in this thread is very stable and fast, and should have no noticible impact on a server (other than making players who like 2H weapons happy!)


One last point you made:

Quote:
Originally Posted by trevius View Post
not many people use 2H weapons on my server due to them sucking in the first place. I don't think it is only damage bonus that is messed up with 2H weapons. I think there seems to be some weird damage capping going on as well which makes them almost pointless.
Damage Bonus really does make up a significant chunk of the DPS output of 2H weapons... especially slower 2H weaps wielded by high level toons.

Please do give it another shot after compiling a version of the server using the new code in this thread. I bet you'll be a lot more pleased with 2H weapons than before! In fact, troll the Allakhazam pages for some popular 2H weapons to get people's feedback on how hard these weapons hit. Then use a GM to twink a toon out with that same weapon at the same player level, and see if it hits for about the same on EQEmu.

I believe Reno already ran this test, and concluded that, with these fixes, 2H weapons perform just like they did on Live.

If that's not the case, well then, we'll look at the code more carefully, find the problem, and fix it! Sound good?
Reply With Quote
  #29  
Old 09-24-2008, 11:54 AM
Derision
Developer
 
Join Date: Feb 2004
Location: UK
Posts: 1,540
Default

Quote:
Originally Posted by Cantus View Post
Derision,

Excellent information there. I apologize that I don't have the code in front of me... are the rule-checking functions inline? In other words, does the ASM that you posted also take into account function call overhead, unwinding the stack, etc. that would result from invoking a non-inline function?
The rule checking functions are declared inline, however I discovered that if zone is built using the 'debug build' in VC++ 2008, with optimisations disabled, the function is not actually inlined and is compiled as a subroutine and accessed via a CALL. With Optimise for speed selected, the function is inlined and there is no subroutine call. I didn't check what GCC produces under Linux as I don't have any experience in dissasembly in that environment.

Quote:
Though your point that the rule is queried from the DB only when a zone first boots is important for folks like ChaosSlayer to remember. If you toggle Damage Bonuses on or off in the DB, you'll have to reboot the zone for it to take effect.
I was simplifying things a bit, what I should have said was that there is no further DB access for rules, unless you issue the #rules reload command to force them to be re-read.
Reply With Quote
  #30  
Old 09-27-2008, 03:14 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

I moved all of the off-topic discussion comments here:

http://www.eqemulator.net/forums/showthread.php?t=26348

Continue the discussion there instead of here, please.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 05:16 AM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3