/* * Ascent MMORPG Server * Copyright (C) 2005-2007 Ascent Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __STATS_H #define __STATS_H #include "Unit.h" #include "UpdateMask.h" #include "ItemInterface.h" enum Stats { STAT_STRENGTH, STAT_AGILITY, STAT_STAMINA, STAT_INTELLECT, STAT_SPIRIT, }; /*ZD = 5, when Char Level = 1 - 7 ZD = 6, when Char Level = 8 - 9 ZD = 7, when Char Level = 10 - 11 ZD = 8, when Char Level = 12 - 14 ZD = 9, when Char Level = 16 - 19 (15-19?) ZD = 11, when Char Level = 20 - 29 ZD = 12, when Char Level = 30 - 39 ZD = 13, when Char Level = 40 - 44 ZD = 14, when Char Level = 45 - 49 ZD = 15, when Char Level = 50 - 53 (50-54?) ZD = 16, when Char Level = 55 - 59 (guessed) ZD = 17, when Char Level = 60 (guessed) */ ///////////////////////////////////////////////////////////////////////// 30 // CalculateXpToGive // // Calculates XP given out by pVictim upon death. // XP=(MOB_LEVEL*5+45)*(1+0.05*(MOB_LEVEL-PLAYER_LEVEL)) // from http://wowwow.game-host.org/viewtopic.php?t=857&sid=07e3a117e26e43358dd23cf260c0c7ad // // //old formula xp = (SUM(health,power1,power2,power3,power4) / 5) * (lvl_of_monster*2) //////////////////////////////////////////////////////////////////////// // http://www.wowwiki.com/Formulas:Mob_XP latest formula //////////////////////////////////////////////////////////////////////// // Old Formula /* CreatureInfo *victimI = pVictim->GetCreatureName(); if(victimI) { if(victimI->Type == CRITTER) return 0; } if(pVictim->GetTypeId() == TYPEID_PLAYER) return 0; if(pAttacker->getLevel() >= 60) return 0; uint16 VictimLvl = pVictim->GetUInt32Value(UNIT_FIELD_LEVEL); uint16 AttackerLvl = pAttacker->GetUInt32Value(UNIT_FIELD_LEVEL); int xp = 0; int greylvl = 0; int ZD[61] = {1,5,5,5,5,5,5,5,6,6,7,7,8,8,8,9,9,9,9,9,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,14,14,14,14,14,15,15,15,15,15,16,16,16,16,16,17}; if( VictimLvl > AttackerLvl ) xp = (AttackerLvl*5+45)*(1+0.05*(VictimLvl-AttackerLvl)); else if( VictimLvl == AttackerLvl ) xp = AttackerLvl*5+45; else { if( AttackerLvl < 6 ) greylvl = 0; else if( AttackerLvl > 5 && AttackerLvl < 40 ) greylvl = AttackerLvl-5-(AttackerLvl/10); else greylvl = AttackerLvl-1-(AttackerLvl/5); if( VictimLvl > greylvl ) xp = (AttackerLvl*5+45)*(1-((AttackerLvl-VictimLvl)/ZD[AttackerLvl])); else xp = 0; } if( xp < 0 ) xp = 0; else xp *= sWorld.getRate(RATE_XP); if(victimI->Rank > 1) xp *= victimI->Rank; //Elite + Boss Extra XP return xp;*/ ////////////////////////////////////////////////////////////////// // Mob XP Functions (including Con Colors) // Colors will be numbers: // {grey = 0, green = 1, yellow = 2, orange = 3, red = 4, skull = 5} // NOTE: skull = red when working with anything OTHER than mobs! inline uint32 getConColor(uint16 AttackerLvl, uint16 VictimLvl) { // const uint32 grayLevel[sWorld.LevelCap+1] = {0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,20,21,22,22,23,24,25,26,27,28,29,30,31,31,32,33,34,35,35,36,37,38,39,39,40,41,42,43,43,44,45,46,47,47,48,49,50,51,51,52,53,54,55,55}; #define PLAYER_LEVEL_CAP 70 const uint32 grayLevel[PLAYER_LEVEL_CAP+1] = {0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,20,21,22,22,23,24,25,26,27,28,29,30,31,31,32,33,34,35,35,36,37,38,39,39,40,41,42,43,43,44,45,46,47,47,48,49,50,51,51,52,53,54,55,55}; if(AttackerLvl + 5 <= VictimLvl) { if(AttackerLvl + 10 <= VictimLvl) { return 5; } return 4; } else { switch(VictimLvl - AttackerLvl) { case 4: case 3: return 3; break; case 2: case 1: case 0: case -1: case -2: return 2; break; default: // More adv formula for grey/green lvls: if(AttackerLvl <= 6) { return 1; //All others are green. } else { if(AttackerLvl > sWorld.Expansion1LevelCap) return 1;//gm if(AttackerLvlIsPlayer()) return 0; if(((Creature*)pVictim)->IsTotem()) return 0; CreatureInfo *victimI; victimI = ((Creature*)pVictim)->GetCreatureName(); if(victimI) if(victimI->Type == CRITTER) return 0; //not wowwikilike but more balanced if ((int32)pVictim->getLevel()-(int32)pAttacker->getLevel()>10) return 0; uint32 max_level = sWorld.Expansion1LevelCap; if(pAttacker->IsPlayer()) max_level = pAttacker->GetUInt32Value(PLAYER_FIELD_MAX_LEVEL); else if(pAttacker->IsPet()) max_level = ((Pet*)pAttacker)->GetPetOwner()->GetUInt32Value(PLAYER_FIELD_MAX_LEVEL); if(pAttacker->getLevel() >= max_level) return 0; uint32 VictimLvl = pVictim->GetUInt32Value(UNIT_FIELD_LEVEL); uint32 AttackerLvl = pAttacker->GetUInt32Value(UNIT_FIELD_LEVEL); /*if(VictimLvl+7>AttackerLvl) VictimLvl = AttackerLvl + 7;*/ float zd = 5; float g = 5; // get zero diff if(AttackerLvl >= 70) zd = 19; else if(AttackerLvl >= 65) zd = 18; else if(AttackerLvl >= 60) zd = 17; else if(AttackerLvl >= 55) zd = 16; else if(AttackerLvl >= 50) zd = 15; else if(AttackerLvl >= 45) zd = 14; else if(AttackerLvl >= 40) zd = 13; else if(AttackerLvl >= 30) zd = 12; else if(AttackerLvl >= 20) zd = 11; else if(AttackerLvl >= 16) zd = 9; else if(AttackerLvl >= 12) zd = 8; else if(AttackerLvl >= 10) zd = 7; else if(AttackerLvl >= 8) zd = 6; else zd = 5; // get grey diff if(AttackerLvl >= 70) g = 15; else if(AttackerLvl >= 65) g = 14; else if(AttackerLvl >= 60) g = 13; else if(AttackerLvl >= 55) g = 12; else if(AttackerLvl >= 50) g = 11; else if(AttackerLvl >= 45) g = 10; else if(AttackerLvl >= 40) g = 9; else if(AttackerLvl >= 30) g = 8; else if(AttackerLvl >= 20) g = 7; else if(AttackerLvl >= 10) g = 6; else g = 5; float xp = 0.0f; float fVictim = VictimLvl; float fAttacker = AttackerLvl; if(VictimLvl == AttackerLvl) xp = float( ((fVictim * 5.0f) + 45.0f) ); else if(VictimLvl > AttackerLvl) { float j = 1.0f + (0.25f * (fVictim - fAttacker)); xp = float( ((AttackerLvl * 5.0f) + 45.0f) * j ); } else { if((AttackerLvl - VictimLvl) < g) { float j = (1.0f - float((fAttacker - fVictim) / zd)); xp = (AttackerLvl * 5.0f + 45.0f) * j; } } // multiply by global XP rate if(xp == 0.0f) return 0; xp *= sWorld.getRate(RATE_XP); // elite boss multiplier if(victimI) { switch(victimI->Rank) { case 0: // normal mob break; case 1: // elite xp *= 1.5f; break; case 2: // rare elite xp *= 3.0f; break; case 3: // world boss xp *= 10.0f; break; default: // rare or higher xp *= 7.0f; break; } } return (uint32)xp; /*const float ZD[PLAYER_LEVEL_CAP+1] = {1,5,5,5,5,5,5,5,6,6,7,7,8,8,8,9,9,9,9,9,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,14,14,14,14,14,15,15,15,15,15,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17}; float temp = 0; float tempcap = 0; float xp = 0; if(VictimLvl >= AttackerLvl) { temp = ((AttackerLvl * 5) + 45) * (1 + 0.05 * (VictimLvl - AttackerLvl)); tempcap = ((AttackerLvl * 5) + 45) * 1.2; if(temp > tempcap) { if( tempcap < 0 ) tempcap = 0; else tempcap *= sWorld.getRate(RATE_XP); xp = tempcap; } else { if( temp < 0 ) temp = 0; else temp *= sWorld.getRate(RATE_XP); xp = temp; } } else { if(getConColor(AttackerLvl, VictimLvl) == 0) { return (uint32)0; } else { if(AttackerLvl < PLAYER_LEVEL_CAP) temp = (((AttackerLvl * 5) + 45) * (1 - (AttackerLvl - VictimLvl)/ZD[AttackerLvl])); else temp = (((AttackerLvl * 5) + 45) * (1 - (AttackerLvl - VictimLvl)/17)); if( temp < 0 ) temp = 0; else temp *= sWorld.getRate(RATE_XP); xp = temp; } } if(victimI) { switch(victimI->Rank) { case 0: // normal mob break; case 1: // elite xp *= 1.5f; break; case 2: // rare elite xp *= 3.0f; break; case 3: // world boss xp *= 10.0f; break; default: // rare or higher xp *= 7.0f; break; } } return (uint32)(xp);*/ } //Taken from WoWWoW Source /* Author: pionere Calculate the stat increase. Using 3rd grade polynome. Parameter level The level the character reached. Parameter a3 The factor for x^3. Parameter a2 The factor for x^2. Parameter a1 The factor for x^1. Parameter a0 The constant factor for the polynome. Return stat gain */ inline uint32 CalculateStat(uint16 level,double a3, double a2, double a1, double a0) { int result1; int result2; int diff; result1 = (int)(a3*level*level*level + a2*level*level + a1*level + a0); result2 = (int)(a3*(level-1)*(level-1)*(level-1) + a2*(level-1)*(level-1) + a1*(level-1) + a0); //get diffrence diff = result1-result2; return diff; } //Partialy taken from WoWWoW Source inline uint32 GainStat(uint16 level, uint8 playerclass,uint8 Stat) { uint32 gain = 0; switch(playerclass) { case WARRIOR: { switch(Stat) { case STAT_STRENGTH: { gain = CalculateStat(level, 0.000039, 0.006902, 1.080040, -1.051701); }break; case STAT_AGILITY: { gain = CalculateStat(level, 0.000022, 0.004600, 0.655333, -0.600356); }break; case STAT_STAMINA: { gain = CalculateStat(level, 0.000059, 0.004044, 1.040000, -1.488504); }break; case STAT_INTELLECT: { gain = CalculateStat(level, 0.000002, 0.001003, 0.100890, -0.076055); }break; case STAT_SPIRIT: { gain = CalculateStat(level, 0.000006, 0.002031, 0.278360, -0.340077); }break; } }break; case PALADIN: { switch(Stat) { case STAT_STRENGTH: { gain = CalculateStat(level, 0.000037, 0.005455, 0.940039, -1.000090); }break; case STAT_AGILITY: { gain = CalculateStat(level, 0.000020, 0.003007, 0.505215, -0.500642); }break; case STAT_STAMINA: { gain = CalculateStat(level, 0.000038, 0.005145, 0.871006, -0.832029); }break; case STAT_INTELLECT: { gain = CalculateStat(level, 0.000023, 0.003345, 0.560050, -0.562058); }break; case STAT_SPIRIT: { gain = CalculateStat(level, 0.000032, 0.003025, 0.615890, -0.640307); }break; } }break; case HUNTER: { switch(Stat) { case STAT_STRENGTH: { gain = CalculateStat(level, 0.000022, 0.001800, 0.407867, -0.550889); }break; case STAT_AGILITY: { gain = CalculateStat(level, 0.000040, 0.007416, 1.125108, -1.003045); }break; case STAT_STAMINA: { gain = CalculateStat(level, 0.000031, 0.004480, 0.780040, -0.800471); }break; case STAT_INTELLECT: { gain = CalculateStat(level, 0.000020, 0.003007, 0.505215, -0.500642); }break; case STAT_SPIRIT: { gain = CalculateStat(level, 0.000017, 0.003803, 0.536846, -0.490026); }break; } }break; case ROGUE: { switch(Stat) { case STAT_STRENGTH: { gain = CalculateStat(level, 0.000025, 0.004170, 0.654096, -0.601491); }break; case STAT_AGILITY: { gain = CalculateStat(level, 0.000038, 0.007834, 1.191028, -1.203940); }break; case STAT_STAMINA: { gain = CalculateStat(level, 0.000032, 0.003025, 0.615890, -0.640307); }break; case STAT_INTELLECT: { gain = CalculateStat(level, 0.000008, 0.001001, 0.163190, -0.064280); }break; case STAT_SPIRIT: { gain = CalculateStat(level, 0.000024, 0.000981, 0.364935, -0.570900); }break; } }break; case PRIEST: { switch(Stat) { case STAT_STRENGTH: { gain = CalculateStat(level, 0.000008, 0.001001, 0.163190, -0.064280); }break; case STAT_AGILITY: { gain = CalculateStat(level, 0.000022, 0.000022, 0.260756, -0.494000); }break; case STAT_STAMINA: { gain = CalculateStat(level, 0.000024, 0.000981, 0.364935, -0.570900); }break; case STAT_INTELLECT: { gain = CalculateStat(level, 0.000039, 0.006981, 1.090090, -1.006070); }break; case STAT_SPIRIT: { gain = CalculateStat(level, 0.000040, 0.007416, 1.125108, -1.003045); }break; } }break; case SHAMAN: { switch(Stat) { case STAT_STRENGTH: { gain = CalculateStat(level, 0.000035, 0.003641, 0.734310, -0.800626); }break; case STAT_AGILITY: { gain = CalculateStat(level, 0.000022, 0.001800, 0.407867, -0.550889); }break; case STAT_STAMINA: { gain = CalculateStat(level, 0.000020, 0.006030, 0.809570, -0.809220); }break; case STAT_INTELLECT: { gain = CalculateStat(level, 0.000031, 0.004480, 0.780040, -0.800471); }break; case STAT_SPIRIT: { gain = CalculateStat(level, 0.000038, 0.005145, 0.871006, -0.832029); }break; } }break; case MAGE: { switch(Stat) { case STAT_STRENGTH: { gain = CalculateStat(level, 0.000002, 0.001003, 0.100890, -0.076055); }break; case STAT_AGILITY: { gain = CalculateStat(level, 0.000008, 0.001001, 0.163190, -0.064280); }break; case STAT_STAMINA: { gain = CalculateStat(level, 0.000006, 0.002031, 0.278360, -0.340077); }break; case STAT_INTELLECT: { gain = CalculateStat(level, 0.000040, 0.007416, 1.125108, -1.003045); }break; case STAT_SPIRIT: { gain = CalculateStat(level, 0.000039, 0.006981, 1.090090, -1.006070); }break; } }break; case WARLOCK: { switch(Stat) { case STAT_STRENGTH: { gain = CalculateStat(level, 0.000006, 0.002031, 0.278360, -0.340077); }break; case STAT_AGILITY: { gain = CalculateStat(level, 0.000024, 0.000981, 0.364935, -0.570900); }break; case STAT_STAMINA: { gain = CalculateStat(level, 0.000021, 0.003009, 0.486493, -0.400003); }break; case STAT_INTELLECT: { gain = CalculateStat(level, 0.000059, 0.004044, 1.040000, -1.488504); }break; case STAT_SPIRIT: { gain = CalculateStat(level, 0.000040, 0.006404, 1.038791, -1.039076); }break; } }break; case DRUID: { switch(Stat) { case STAT_STRENGTH: { gain = CalculateStat(level, 0.000021, 0.003009, 0.486493, -0.400003); }break; case STAT_AGILITY: { gain = CalculateStat(level, 0.000041, 0.000440, 0.512076, -1.000317); }break; case STAT_STAMINA: { gain = CalculateStat(level, 0.000023, 0.003345, 0.560050, -0.562058); }break; case STAT_INTELLECT: { gain = CalculateStat(level, 0.000038, 0.005145, 0.871006, -0.832029); }break; case STAT_SPIRIT: { gain = CalculateStat(level, 0.000059, 0.004044, 1.040000, -1.488504); }break; } }break; } return gain; } //TODO: Some awesome formula to determine how much damage to deal //consider this is melee damage //damage type =0 --melee, 1--dual wield, 2 - ranged inline uint32 CalculateDamage(Unit *pAttacker, Unit *pVictim, uint32 damage_type, uint32 spellgroup, SpellEntry *ability)//spellid is used only for 2-3 spells, that have AP bonus { // Attack Power increases your base damage-per-second (DPS) by 1 for every 14 attack power. // (c) wowwiki //type of this UNIT_FIELD_ATTACK_POWER_MODS is unknown, not even uint32 disabled for now. uint32 offset; Item *it = NULL; if(pAttacker->disarmed && pAttacker->IsPlayer()) { offset=UNIT_FIELD_MINDAMAGE; it = static_cast(pAttacker)->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND); } else if(damage_type == MELEE) offset=UNIT_FIELD_MINDAMAGE; else if(damage_type == DUALWIELD) offset=UNIT_FIELD_MINOFFHANDDAMAGE; else offset=UNIT_FIELD_MINRANGEDDAMAGE; float min_damage = pAttacker->GetFloatValue(offset); float max_damage = pAttacker->GetFloatValue(offset+1); if(it) { min_damage -= it->GetProto()->Damage[0].Min; max_damage -= it->GetProto()->Damage[0].Max; } float ap = 0; float bonus; float wspeed; if(offset == UNIT_FIELD_MINRANGEDDAMAGE) { // //starting from base attack power then we apply mods on it //ap += pAttacker->GetRAP(); ap += pVictim->RAPvModifier; if(!pVictim->IsPlayer()) if(((Creature*)pVictim)->GetCreatureName()) { // ap += (float)pAttacker->CreatureRangedAttackPowerMod[((Creature*)pVictim)->GetCreatureName()->Type]; uint32 creatType = ((Creature*)pVictim)->GetCreatureName()->Type; ap += (float)pAttacker->CreatureRangedAttackPowerMod[creatType]; if(pAttacker->IsPlayer()) { // min_damage += min_damage*static_cast(pAttacker)->IncreaseDamageByTypePCT[((Creature*)pVictim)->GetCreatureName()->Type]; // max_damage += max_damage*static_cast(pAttacker)->IncreaseDamageByTypePCT[((Creature*)pVictim)->GetCreatureName()->Type]; min_damage = (min_damage + static_cast(pAttacker)->IncreaseDamageByType[creatType]) * (1 + static_cast(pAttacker)->IncreaseDamageByTypePCT[creatType]); max_damage = (max_damage + static_cast(pAttacker)->IncreaseDamageByType[creatType]) * (1 + static_cast(pAttacker)->IncreaseDamageByTypePCT[creatType]); } } if(pAttacker->IsPlayer()) { if(!pAttacker->disarmed) { Item *it = static_cast(pAttacker)->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_RANGED); if(it) wspeed = (float)it->GetProto()->Delay; else wspeed = 2000; } else wspeed = (float)pAttacker->GetUInt32Value(UNIT_FIELD_RANGEDATTACKTIME); } else { wspeed = (float)pAttacker->GetUInt32Value(UNIT_FIELD_BASEATTACKTIME); } //ranged weapon normalization. if(pAttacker->IsPlayer() && ability) { if(ability->Effect[0] == SPELL_EFFECT_DUMMYMELEE || ability->Effect[1] == SPELL_EFFECT_DUMMYMELEE || ability->Effect[2] == SPELL_EFFECT_DUMMYMELEE) { wspeed = 2800; } } bonus = (wspeed*ap)/14000.0f; min_damage += bonus; max_damage += bonus; } else { //MinD = AP(28AS-(WS/7))-MaxD // //starting from base attack power then we apply mods on it // ap += pAttacker->GetAP(); ap += pVictim->APvModifier; if(!pVictim->IsPlayer()) if(((Creature*)pVictim)->GetCreatureName()) { // ap += (float)pAttacker->CreatureAttackPowerMod[((Creature*)pVictim)->GetCreatureName()->Type]; uint32 creatType = ((Creature*)pVictim)->GetCreatureName()->Type; ap += (float)pAttacker->CreatureAttackPowerMod[creatType]; if(pAttacker->IsPlayer()) { // min_damage += min_damage*static_cast(pAttacker)->IncreaseDamageByTypePCT[((Creature*)pVictim)->GetCreatureName()->Type]; // max_damage += max_damage*static_cast(pAttacker)->IncreaseDamageByTypePCT[((Creature*)pVictim)->GetCreatureName()->Type]; min_damage = (min_damage + static_cast(pAttacker)->IncreaseDamageByType[creatType]) * (1 + static_cast(pAttacker)->IncreaseDamageByTypePCT[creatType]); max_damage = (max_damage + static_cast(pAttacker)->IncreaseDamageByType[creatType]) * (1 + static_cast(pAttacker)->IncreaseDamageByTypePCT[creatType]); } } if(pAttacker->IsPlayer()) { if(!pAttacker->disarmed) { Item *it = static_cast(pAttacker)->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND); if(it) wspeed = (float)it->GetProto()->Delay; else wspeed = 2000; } else wspeed = (float)pAttacker->GetUInt32Value(UNIT_FIELD_BASEATTACKTIME); if(spellgroup) { int32 apall = pAttacker->GetAP(); int32 apb=0; SM_FIValue(pAttacker->SM_PAPBonus,&apb,spellgroup); if(apb) ap += apall*((float)apb/100); else ap = apall; } } else { wspeed = (float)pAttacker->GetUInt32Value(UNIT_FIELD_BASEATTACKTIME); } //Normalized weapon damage checks. if(pAttacker->IsPlayer() && ability) { if(ability->Effect[0] == SPELL_EFFECT_DUMMYMELEE || ability->Effect[1] == SPELL_EFFECT_DUMMYMELEE || ability->Effect[2] == SPELL_EFFECT_DUMMYMELEE) { it = static_cast(pAttacker)->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND); if(it) { if(it->GetProto()->Class == 2) //weapon { if(it->GetProto()->InventoryType == INVTYPE_2HWEAPON) wspeed = 3300; else if(it->GetProto()->SubClass == 15) wspeed = 1700; else wspeed = 2400; } } } } bonus = (wspeed*ap)/14000.0f; min_damage += bonus; max_damage += bonus; } // Ehh, sometimes min is bigger than max!?!? /*if (min_damage > max_damage) { float temp = max_damage; max_damage = min_damage; min_damage = temp; }*/ // Fix creatures that have no base attack damage. //this is shit, critter should not have attack damage! //if db is wrong we should fix db //if(max_damage==0) // max_damage=5; float diff = fabs(max_damage - min_damage); float result = min_damage; if(diff >= 1) result += sRand.rand(diff);; if(result >= 0) return FL2UINT(result); return 0; } inline bool isEven (int num) { if ((num%2)==0) { return true; } return false; } #endif