diff options
Diffstat (limited to 'src/game/Player.cpp')
-rw-r--r-- | src/game/Player.cpp | 1623 |
1 files changed, 1157 insertions, 466 deletions
diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 7096118f02f..cbf5c8dbfc5 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -64,6 +64,7 @@ #include "Spell.h" #include "SocialMgr.h" #include "GameEvent.h" +#include "AchievementMgr.h" #include <cmath> @@ -132,7 +133,7 @@ PlayerTaxi::PlayerTaxi() memset(m_taximask, 0, sizeof(m_taximask)); } -void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 level) +void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint32 level) { // capital and taxi hub masks switch(race) @@ -149,6 +150,13 @@ void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 level) case RACE_BLOODELF: SetTaximaskNode(82); break; // Blood Elf case RACE_DRAENEI: SetTaximaskNode(94); break; // Draenei } + + switch(chrClass) + { + case CLASS_DEATH_KNIGHT: // TODO: figure out initial known nodes + break; + } + // new continent starting masks (It will be accessible only at new map) switch(Player::TeamForRace(race)) { @@ -251,7 +259,7 @@ const int32 Player::ReputationRank_Length[MAX_REPUTATION_RANK] = {36000, 3000, 3 UpdateMask Player::updateVisualBits; -Player::Player (WorldSession *session): Unit() +Player::Player (WorldSession *session): Unit(), m_achievementMgr(this) { m_transport = 0; @@ -362,6 +370,7 @@ Player::Player (WorldSession *session): Unit() m_canParry = false; m_canBlock = false; m_canDualWield = false; + m_canTitanGrip = false; m_ammoDPS = 0.0f; m_temporaryUnsummonedPetNumber = 0; @@ -406,6 +415,9 @@ Player::Player (WorldSession *session): Unit() m_auraBaseMod[i][PCT_MOD] = 1.0f; } + for (int i = 0; i < MAX_COMBAT_RATING; i++) + m_baseRatingValue[i] = 0; + // Honor System m_lastHonorUpdateTime = time(NULL); @@ -419,6 +431,8 @@ Player::Player (WorldSession *session): Unit() //Default movement to run mode m_unit_movement_flags = 0; + m_mover = NULL; + m_miniPet = 0; m_bgAfkReportedTimer = 0; m_contestedPvPTimer = 0; @@ -428,6 +442,8 @@ Player::Player (WorldSession *session): Unit() m_isActive = true; m_farsightVision = false; + + m_runes = NULL; } Player::~Player () @@ -475,6 +491,7 @@ Player::~Player () RemovePossess(false); delete m_declinedname; + delete m_runes; } void Player::CleanupsBeforeDelete() @@ -520,9 +537,9 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 uint8 powertype = cEntry->powerType; - uint32 unitfield; + //uint32 unitfield; - switch(powertype) + /*switch(powertype) { case POWER_ENERGY: case POWER_MANA: @@ -531,13 +548,16 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 case POWER_RAGE: unitfield = 0x00110000; break; + case POWER_RUNIC_POWER: + unitfield = 0x0000EE00; //TODO: find correct unitfield here + break; default: sLog.outError("Invalid default powertype %u for player (class %u)",powertype,class_); return false; - } + }*/ - SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE ); - SetFloatValue(UNIT_FIELD_COMBATREACH, DEFAULT_COMBAT_REACH ); + SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE); + SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f); switch(gender) { @@ -560,12 +580,14 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 uint32 RaceClassGender = ( race ) | ( class_ << 8 ) | ( gender << 16 ); SetUInt32Value(UNIT_FIELD_BYTES_0, ( RaceClassGender | ( powertype << 24 ) ) ); - SetUInt32Value(UNIT_FIELD_BYTES_1, unitfield); - SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_UNK5 ); - SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE ); + //SetUInt32Value(UNIT_FIELD_BYTES_1, unitfield); + SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP ); + SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE ); + SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER); SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); // fix cast time showed in spell tooltip on client + SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); // default for players in 3.0.3 - //-1 is default value + // -1 is default value SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1)); SetUInt32Value(PLAYER_BYTES, (skin | (face << 8) | (hairStyle << 16) | (hairColor << 24))); @@ -577,6 +599,7 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 SetUInt32Value( PLAYER_GUILD_TIMESTAMP, 0 ); SetUInt64Value( PLAYER__FIELD_KNOWN_TITLES, 0 ); // 0=disabled + SetUInt64Value( PLAYER__FIELD_KNOWN_TITLES1, 0 ); // 0=disabled SetUInt32Value( PLAYER_CHOSEN_TITLE, 0 ); SetUInt32Value( PLAYER_FIELD_KILLS, 0 ); SetUInt32Value( PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 0 ); @@ -584,10 +607,20 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 SetUInt32Value( PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0 ); // set starting level + uint32 start_level = getClass() != CLASS_DEATH_KNIGHT + ? sWorld.getConfig(CONFIG_START_PLAYER_LEVEL) + : sWorld.getConfig(CONFIG_START_HEROIC_PLAYER_LEVEL); + if (GetSession()->GetSecurity() >= SEC_MODERATOR) - SetUInt32Value (UNIT_FIELD_LEVEL, sWorld.getConfig(CONFIG_START_GM_LEVEL)); - else - SetUInt32Value (UNIT_FIELD_LEVEL, sWorld.getConfig(CONFIG_START_PLAYER_LEVEL)); + { + uint32 gm_level = sWorld.getConfig(CONFIG_START_GM_LEVEL); + if(gm_level > start_level) + start_level = gm_level; + } + + SetUInt32Value(UNIT_FIELD_LEVEL, start_level); + + InitRunes(); SetUInt32Value (PLAYER_FIELD_COINAGE, sWorld.getConfig(CONFIG_START_PLAYER_MONEY)); SetUInt32Value (PLAYER_FIELD_HONOR_CURRENCY, sWorld.getConfig(CONFIG_START_HONOR_POINTS)); @@ -651,6 +684,7 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 // base stats and related field values InitStatsForLevel(); InitTaxiNodesForLevel(); + InitGlyphsForLevel(); InitTalentForLevel(); InitPrimaryProffesions(); // to max set before any spell added @@ -663,6 +697,14 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 SetPower(POWER_MANA,GetMaxPower(POWER_MANA)); } + if(getPowerType() == POWER_RUNIC_POWER) + { + SetPower(POWER_RUNE, 8); + SetMaxPower(POWER_RUNE, 8); + SetPower(POWER_RUNIC_POWER, 0); + SetMaxPower(POWER_RUNIC_POWER, 1000); + } + // original spells learnDefaultSpells(true); @@ -706,6 +748,11 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 uint32 item_id = oEntry->ItemId[j]; + + // Hack for not existed item id in dbc 3.0.3 + if(item_id==40582) + continue; + ItemPrototype const* iProto = objmgr.GetItemPrototype(item_id); if(!iProto) { @@ -713,7 +760,9 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 continue; } - uint32 count = iProto->Stackable; // max stack by default (mostly 1) + // max stack by default (mostly 1), 1 for infinity stackable + uint32 count = iProto->Stackable > 0 ? uint32(iProto->Stackable) : 1; + if(iProto->Class==ITEM_CLASS_CONSUMABLE && iProto->SubClass==ITEM_SUBCLASS_FOOD) { switch(iProto->Spells[0].SpellCategory) @@ -933,53 +982,29 @@ void Player::HandleDrowning() void Player::HandleLava() { - bool ValidArea = false; - if ((m_isunderwater & 0x80) && isAlive()) { - //Single trigger Set BreathTimer + // Single trigger Set BreathTimer if (!(m_isunderwater & 0x80)) { m_isunderwater|= 0x04; m_breathTimer = 1000; } - //Reset BreathTimer and still in the lava + + // Reset BreathTimer and still in the lava if (!m_breathTimer) { uint64 guid = GetGUID(); uint32 damage = urand(600, 700); // TODO: Get more detailed information about lava damage - uint32 dmgZone = GetZoneId(); // TODO: Find correct "lava dealing zone" flag in Area Table - // Deal lava damage only in lava zones. - switch(dmgZone) - { - case 0x8D: - ValidArea = false; - break; - case 0x94: - ValidArea = false; - break; - case 0x2CE: - ValidArea = false; - break; - case 0x2CF: - ValidArea = false; - break; - default: - if (dmgZone / 5 & 0x408) - ValidArea = true; - } - - // if is valid area and is not gamemaster then deal damage - if ( ValidArea && !isGameMaster() ) + // if not gamemaster then deal damage + if ( !isGameMaster() ) EnvironmentalDamage(guid, DAMAGE_LAVA, damage); m_breathTimer = 1000; } - } - //Death timer disabled and WaterFlags reset - else if (m_deathState == DEAD) + else if (m_deathState == DEAD) // Disable breath timer and reset underwater flags { m_breathTimer = 0; m_isunderwater = 0; @@ -1314,6 +1339,7 @@ void Player::Update( uint32 p_time ) Pet* pet = GetPet(); if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE) && !pet->isPossessed()) + //if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE) && (GetCharmGUID() && (pet->GetGUID() != GetCharmGUID()))) { RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true); return; @@ -1363,6 +1389,7 @@ void Player::setDeathState(DeathState s) // passive spell if(!ressSpellId) ressSpellId = GetResurrectionSpellId(); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP, 1); } Unit::setDeathState(s); @@ -1383,13 +1410,15 @@ void Player::setDeathState(DeathState s) void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) { - *p_data << GetGUID(); + Field *fields = result->Fetch(); + + *p_data << uint64(GetGUID()); *p_data << m_name; - *p_data << getRace(); + *p_data << uint8(getRace()); uint8 pClass = getClass(); - *p_data << pClass; - *p_data << getGender(); + *p_data << uint8(pClass); + *p_data << uint8(getGender()); uint32 bytes = GetUInt32Value(PLAYER_BYTES); *p_data << uint8(bytes); @@ -1402,16 +1431,17 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) *p_data << uint8(getLevel()); // player level // do not use GetMap! it will spawn a new instance since the bound instances are not loaded - uint32 zoneId = MapManager::Instance().GetZoneId(GetMapId(), GetPositionX(),GetPositionY()); + uint32 zoneId = MapManager::Instance().GetZoneId(GetMapId(), GetPositionX(),GetPositionY(),GetPositionZ()); sLog.outDebug("Player::BuildEnumData: m:%u, x:%f, y:%f, z:%f zone:%u", GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), zoneId); - *p_data << zoneId; - *p_data << GetMapId(); + *p_data << uint32(zoneId); + *p_data << uint32(GetMapId()); *p_data << GetPositionX(); *p_data << GetPositionY(); *p_data << GetPositionZ(); - *p_data << (result ? result->Fetch()[13].GetUInt32() : 0); + // guild id + *p_data << (result ? fields[13].GetUInt32() : 0); uint32 char_flags = 0; if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM)) @@ -1422,14 +1452,13 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) char_flags |= CHARACTER_FLAG_GHOST; if(HasAtLoginFlag(AT_LOGIN_RENAME)) char_flags |= CHARACTER_FLAG_RENAME; - // always send the flag if declined names aren't used - // to let the client select a default method of declining the name - if(!sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) || (result && result->Fetch()[14].GetCppString() != "")) + if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) && (fields[14].GetCppString() != "")) char_flags |= CHARACTER_FLAG_DECLINED; - *p_data << (uint32)char_flags; // character flags - - *p_data << (uint8)1; // unknown + *p_data << uint32(char_flags); // character flags + // character customize (flags?) + *p_data << uint32(HasAtLoginFlag(AT_LOGIN_CUSTOMIZE) ? 1 : 0); + *p_data << uint8(1); // unknown // Pets info { @@ -1440,8 +1469,6 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) // show pet at selection character in character list only for non-ghost character if(result && isAlive() && (pClass == CLASS_WARLOCK || pClass == CLASS_HUNTER)) { - Field* fields = result->Fetch(); - uint32 entry = fields[10].GetUInt32(); CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(entry); if(cInfo) @@ -1452,36 +1479,11 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) } } - *p_data << (uint32)petDisplayId; - *p_data << (uint32)petLevel; - *p_data << (uint32)petFamily; + *p_data << uint32(petDisplayId); + *p_data << uint32(petLevel); + *p_data << uint32(petFamily); } - /*ItemPrototype const *items[EQUIPMENT_SLOT_END]; - for (int i = 0; i < EQUIPMENT_SLOT_END; i++) - items[i] = NULL; - - QueryResult *result = CharacterDatabase.PQuery("SELECT slot,item_template FROM character_inventory WHERE guid = '%u' AND bag = 0",GetGUIDLow()); - if (result) - { - do - { - Field *fields = result->Fetch(); - uint8 slot = fields[0].GetUInt8() & 255; - uint32 item_id = fields[1].GetUInt32(); - if( slot >= EQUIPMENT_SLOT_END ) - continue; - - items[slot] = objmgr.GetItemPrototype(item_id); - if(!items[slot]) - { - sLog.outError( "Player::BuildEnumData: Player %s have unknown item (id: #%u) in inventory, skipped.", GetName(),item_id ); - continue; - } - } while (result->NextRow()); - delete result; - }*/ - for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; slot++) { uint32 visualbase = PLAYER_VISIBLE_ITEM_1_0 + (slot * MAX_VISIBLE_ITEM_OFFSET); @@ -1498,20 +1500,20 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) if (proto != NULL) { - *p_data << (uint32)proto->DisplayInfoID; - *p_data << (uint8)proto->InventoryType; - *p_data << (uint32)(enchant?enchant->aura_id:0); + *p_data << uint32(proto->DisplayInfoID); + *p_data << uint8(proto->InventoryType); + *p_data << uint32(enchant ? enchant->aura_id : 0); } else { - *p_data << (uint32)0; - *p_data << (uint8)0; - *p_data << (uint32)0; // enchant? + *p_data << uint32(0); + *p_data << uint8(0); + *p_data << uint32(0); // enchant? } } - *p_data << (uint32)0; // first bag display id - *p_data << (uint8)0; // first bag inventory type - *p_data << (uint32)0; // enchant? + *p_data << uint32(0); // first bag display id + *p_data << uint8(0); // first bag inventory type + *p_data << uint32(0); // enchant? } bool Player::ToggleAFK() @@ -1583,7 +1585,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati if(GetTransport()) RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :) - SendTransferAborted(mapid, TRANSFER_ABORT_INSUF_EXPAN_LVL1); + SendTransferAborted(mapid, TRANSFER_ABORT_INSUF_EXPAN_LVL, mEntry->Expansion()); return false; // normal client can't teleport to this map... } @@ -1892,13 +1894,20 @@ void Player::RegenerateAll() { RegenerateHealth(); if (!isInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN)) + { Regenerate(POWER_RAGE); + if(getClass() == CLASS_DEATH_KNIGHT) + Regenerate(POWER_RUNIC_POWER); + } } Regenerate( POWER_ENERGY ); Regenerate( POWER_MANA ); + if(getClass() == CLASS_DEATH_KNIGHT) + Regenerate( POWER_RUNE ); + m_regenTimer = regenDelay; } @@ -1918,11 +1927,11 @@ void Player::Regenerate(Powers power) if (recentCast) { // Trinity Updates Mana in intervals of 2s, which is correct - addvalue = GetFloatValue(PLAYER_FIELD_MOD_MANA_REGEN_INTERRUPT) * ManaIncreaseRate * 2.00f; + addvalue = GetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER) * ManaIncreaseRate * 2.00f; } else { - addvalue = GetFloatValue(PLAYER_FIELD_MOD_MANA_REGEN) * ManaIncreaseRate * 2.00f; + addvalue = GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER) * ManaIncreaseRate * 2.00f; } } break; case POWER_RAGE: // Regenerate rage @@ -1933,6 +1942,17 @@ void Player::Regenerate(Powers power) case POWER_ENERGY: // Regenerate energy (rogue) addvalue = 20; break; + case POWER_RUNIC_POWER: + { + float RunicPowerDecreaseRate = sWorld.getRate(RATE_POWER_RUNICPOWER_LOSS); + addvalue = 30 * RunicPowerDecreaseRate; // 3 RunicPower by tick + } break; + case POWER_RUNE: + { + for(uint32 i = 0; i < MAX_RUNES; ++i) + if(uint8 cd = GetRuneCooldown(i)) // if we have cooldown, reduce it... + SetRuneCooldown(i, cd - 1); // ... by 2 sec (because update is every 2 sec) + } break; case POWER_FOCUS: case POWER_HAPPINESS: break; @@ -1948,7 +1968,7 @@ void Player::Regenerate(Powers power) addvalue *= ((*i)->GetModifierValue() + 100) / 100.0f; } - if (power != POWER_RAGE) + if (power != POWER_RAGE && power != POWER_RUNIC_POWER) { curValue += uint32(addvalue); if (curValue > maxValue) @@ -2046,7 +2066,7 @@ void Player::SetGameMaster(bool on) setFaction(35); SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM); - RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP); + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); ResetContestedPvP(); getHostilRefManager().setOnlineOfflineState(false); @@ -2060,7 +2080,7 @@ void Player::SetGameMaster(bool on) // restore FFA PvP Server state if(sWorld.IsFFAPvPRealm()) - SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP); + SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); // restore FFA PvP area state, remove not allowed for GM mounts UpdateArea(m_areaUpdateId); @@ -2232,6 +2252,8 @@ void Player::GiveLevel(uint32 level) data << uint32(0); data << uint32(0); data << uint32(0); + data << uint32(0); + data << uint32(0); // end for for(int i = STAT_STRENGTH; i < MAX_STATS; ++i) // Stats loop (0-4) data << uint32(int32(info.stats[i]) - GetCreateStat(Stats(i))); @@ -2255,6 +2277,7 @@ void Player::GiveLevel(uint32 level) InitTalentForLevel(); InitTaxiNodesForLevel(); + InitGlyphsForLevel(); UpdateAllStats(); @@ -2274,6 +2297,7 @@ void Player::GiveLevel(uint32 level) Pet* pet = GetPet(); if(pet && pet->getPetType()==SUMMON_PET) pet->GivePetLevel(level); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL); } void Player::InitTalentForLevel() @@ -2410,6 +2434,9 @@ void Player::InitStatsForLevel(bool reapplyMods) SetFloatValue(UNIT_FIELD_POWER_COST_MODIFIER+i,0.0f); SetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+i,0.0f); } + // Reset no reagent cost field + for(int i = 0; i < 3; i++) + SetUInt32Value(PLAYER_NO_REAGENT_COST_1 + i, 0); // Init data for form but skip reapply item mods for form InitDataForForm(reapplyMods); @@ -2426,15 +2453,16 @@ void Player::InitStatsForLevel(bool reapplyMods) RemoveFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_SILENCED | UNIT_FLAG_PACIFIED | - UNIT_FLAG_DISABLE_ROTATE | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED | + UNIT_FLAG_STUNNED | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED | UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_SKINNABLE | UNIT_FLAG_MOUNT | UNIT_FLAG_TAXI_FLIGHT ); SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE ); // must be set // cleanup player flags (will be re-applied if need at aura load), to avoid have ghost flag without ghost aura, for example. - RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK | PLAYER_FLAGS_DND | PLAYER_FLAGS_GM | PLAYER_FLAGS_GHOST | PLAYER_FLAGS_FFA_PVP); + RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK | PLAYER_FLAGS_DND | PLAYER_FLAGS_GM | PLAYER_FLAGS_GHOST); SetByteValue(UNIT_FIELD_BYTES_1, 2, 0x00); // one form stealth modified bytes + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP | UNIT_BYTE2_FLAG_SANCTUARY); // restore if need some important flags SetUInt32Value(PLAYER_FIELD_BYTES2, 0 ); // flags empty by default @@ -2450,6 +2478,7 @@ void Player::InitStatsForLevel(bool reapplyMods) SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE)); SetPower(POWER_FOCUS, 0); SetPower(POWER_HAPPINESS, 0); + SetPower(POWER_RUNIC_POWER, 0); } void Player::SendInitialSpells() @@ -2864,8 +2893,6 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, continue; if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL || - // poison special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL - pSkill->id==SKILL_POISONS && _spell_idx->second->max_value==0 || // lockpicking special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL pSkill->id==SKILL_LOCKPICKING && _spell_idx->second->max_value==0 ) { @@ -2902,6 +2929,12 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, } } + if(!loading) + { + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS); + } + // return true (for send learn packet) only if spell active (in case ranked spells) and not replace old spell return active && !disabled && !superceded_old; } @@ -3051,8 +3084,6 @@ void Player::removeSpell(uint32 spell_id, bool disabled) continue; if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL || - // poison special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL - pSkill->id==SKILL_POISONS && _spell_idx->second->max_value==0 || // lockpicking special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL pSkill->id==SKILL_LOCKPICKING && _spell_idx->second->max_value==0 ) { @@ -3072,6 +3103,8 @@ void Player::removeSpell(uint32 spell_id, bool disabled) for(SpellLearnSpellMap::const_iterator itr2 = spell_begin; itr2 != spell_end; ++itr2) removeSpell(itr2->second.spell, disabled); + + // TODO: recast if need lesser ranks spell for passive with IsPassiveSpellStackableWithRanks } void Player::RemoveArenaSpellCooldowns() @@ -3352,50 +3385,46 @@ void Player::InitVisibleBits() { updateVisualBits.SetCount(PLAYER_END); - // TODO: really implement OWNER_ONLY and GROUP_ONLY. Flags can be found in UpdateFields.h - updateVisualBits.SetBit(OBJECT_FIELD_GUID); updateVisualBits.SetBit(OBJECT_FIELD_TYPE); + updateVisualBits.SetBit(OBJECT_FIELD_ENTRY); updateVisualBits.SetBit(OBJECT_FIELD_SCALE_X); - - updateVisualBits.SetBit(UNIT_FIELD_CHARM); - updateVisualBits.SetBit(UNIT_FIELD_CHARM+1); - - updateVisualBits.SetBit(UNIT_FIELD_SUMMON); - updateVisualBits.SetBit(UNIT_FIELD_SUMMON+1); - - updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY); - updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY+1); - - updateVisualBits.SetBit(UNIT_FIELD_TARGET); - updateVisualBits.SetBit(UNIT_FIELD_TARGET+1); - - updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT); - updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT+1); - + updateVisualBits.SetBit(UNIT_FIELD_CHARM + 0); + updateVisualBits.SetBit(UNIT_FIELD_CHARM + 1); + updateVisualBits.SetBit(UNIT_FIELD_SUMMON + 0); + updateVisualBits.SetBit(UNIT_FIELD_SUMMON + 1); + updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY + 0); + updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY + 1); + updateVisualBits.SetBit(UNIT_FIELD_TARGET + 0); + updateVisualBits.SetBit(UNIT_FIELD_TARGET + 1); + updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT + 0); + updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT + 1); + updateVisualBits.SetBit(UNIT_FIELD_BYTES_0); updateVisualBits.SetBit(UNIT_FIELD_HEALTH); updateVisualBits.SetBit(UNIT_FIELD_POWER1); updateVisualBits.SetBit(UNIT_FIELD_POWER2); updateVisualBits.SetBit(UNIT_FIELD_POWER3); updateVisualBits.SetBit(UNIT_FIELD_POWER4); updateVisualBits.SetBit(UNIT_FIELD_POWER5); - + updateVisualBits.SetBit(UNIT_FIELD_POWER6); + updateVisualBits.SetBit(UNIT_FIELD_POWER7); updateVisualBits.SetBit(UNIT_FIELD_MAXHEALTH); updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER1); updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER2); updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER3); updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER4); updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER5); - + updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER6); + updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER7); updateVisualBits.SetBit(UNIT_FIELD_LEVEL); updateVisualBits.SetBit(UNIT_FIELD_FACTIONTEMPLATE); - updateVisualBits.SetBit(UNIT_FIELD_BYTES_0); + updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID + 0); + updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID + 1); + updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID + 2); updateVisualBits.SetBit(UNIT_FIELD_FLAGS); updateVisualBits.SetBit(UNIT_FIELD_FLAGS_2); - for(uint16 i = UNIT_FIELD_AURA; i < UNIT_FIELD_AURASTATE; ++i) - updateVisualBits.SetBit(i); updateVisualBits.SetBit(UNIT_FIELD_AURASTATE); - updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME); + updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME + 0); updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME + 1); updateVisualBits.SetBit(UNIT_FIELD_BOUNDINGRADIUS); updateVisualBits.SetBit(UNIT_FIELD_COMBATREACH); @@ -3408,10 +3437,12 @@ void Player::InitVisibleBits() updateVisualBits.SetBit(UNIT_DYNAMIC_FLAGS); updateVisualBits.SetBit(UNIT_CHANNEL_SPELL); updateVisualBits.SetBit(UNIT_MOD_CAST_SPEED); + updateVisualBits.SetBit(UNIT_FIELD_BASE_MANA); updateVisualBits.SetBit(UNIT_FIELD_BYTES_2); + updateVisualBits.SetBit(UNIT_FIELD_HOVERHEIGHT); - updateVisualBits.SetBit(PLAYER_DUEL_ARBITER); - updateVisualBits.SetBit(PLAYER_DUEL_ARBITER+1); + updateVisualBits.SetBit(PLAYER_DUEL_ARBITER + 0); + updateVisualBits.SetBit(PLAYER_DUEL_ARBITER + 1); updateVisualBits.SetBit(PLAYER_FLAGS); updateVisualBits.SetBit(PLAYER_GUILDID); updateVisualBits.SetBit(PLAYER_GUILDRANK); @@ -3422,29 +3453,29 @@ void Player::InitVisibleBits() updateVisualBits.SetBit(PLAYER_GUILD_TIMESTAMP); // PLAYER_QUEST_LOG_x also visible bit on official (but only on party/raid)... - for(uint16 i = PLAYER_QUEST_LOG_1_1; i < PLAYER_QUEST_LOG_25_2; i+=4) + for(uint16 i = PLAYER_QUEST_LOG_1_1; i < PLAYER_QUEST_LOG_25_2; i += 4) updateVisualBits.SetBit(i); - //Players visible items are not inventory stuff - //431) = 884 (0x374) = main weapon - for(uint16 i = 0; i < EQUIPMENT_SLOT_END; i++) + // Players visible items are not inventory stuff + for(uint16 i = 0; i < EQUIPMENT_SLOT_END; ++i) { - // item creator - updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_CREATOR + (i*MAX_VISIBLE_ITEM_OFFSET) + 0); - updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_CREATOR + (i*MAX_VISIBLE_ITEM_OFFSET) + 1); + uint32 offset = i * MAX_VISIBLE_ITEM_OFFSET; - uint16 visual_base = PLAYER_VISIBLE_ITEM_1_0 + (i*MAX_VISIBLE_ITEM_OFFSET); + // item creator + updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_CREATOR + 0 + offset); + updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_CREATOR + 1 + offset); // item entry - updateVisualBits.SetBit(visual_base + 0); + updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_0 + 0 + offset); - // item enchantment IDs - for(uint8 j = 0; j < MAX_INSPECTED_ENCHANTMENT_SLOT; ++j) - updateVisualBits.SetBit(visual_base + 1 + j); + // item enchantments + for(uint8 j = 0; j < MAX_ENCHANTMENT_SLOT; ++j) + updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_0 + 1 + j + offset); // random properties - updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 0 + (i*MAX_VISIBLE_ITEM_OFFSET)); - updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 1 + (i*MAX_VISIBLE_ITEM_OFFSET)); + updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PROPERTIES + offset); + updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_SEED + offset); + updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PAD + offset); } updateVisualBits.SetBit(PLAYER_CHOSEN_TITLE); @@ -3462,7 +3493,6 @@ void Player::BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) if(target == this) { - for(int i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++) { if(m_items[i] == NULL) @@ -3470,7 +3500,7 @@ void Player::BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) m_items[i]->BuildCreateUpdateBlockForPlayer( data, target ); } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { if(m_items[i] == NULL) continue; @@ -3503,7 +3533,7 @@ void Player::DestroyForPlayer( Player *target ) const m_items[i]->DestroyForPlayer( target ); } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { if(m_items[i] == NULL) continue; @@ -3591,27 +3621,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC } // remove from arena teams - uint32 at_id = GetArenaTeamIdFromDB(playerguid,ARENA_TEAM_2v2); - if(at_id != 0) - { - ArenaTeam * at = objmgr.GetArenaTeamById(at_id); - if(at) - at->DelMember(playerguid); - } - at_id = GetArenaTeamIdFromDB(playerguid,ARENA_TEAM_3v3); - if(at_id != 0) - { - ArenaTeam * at = objmgr.GetArenaTeamById(at_id); - if(at) - at->DelMember(playerguid); - } - at_id = GetArenaTeamIdFromDB(playerguid,ARENA_TEAM_5v5); - if(at_id != 0) - { - ArenaTeam * at = objmgr.GetArenaTeamById(at_id); - if(at) - at->DelMember(playerguid); - } + LeaveAllArenaTeams(playerguid); // the player was uninvited already on logout so just remove from group QueryResult *resultGroup = CharacterDatabase.PQuery("SELECT leaderGuid FROM group_member WHERE memberGuid='%u'", guid); @@ -3652,15 +3662,16 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC MailItemsInfo mi; if(has_items) { - QueryResult *resultItems = CharacterDatabase.PQuery("SELECT item_guid,item_template FROM mail_items WHERE mail_id='%u'", mail_id); + // data needs to be at first place for Item::LoadFromDB + QueryResult *resultItems = CharacterDatabase.PQuery("SELECT data,item_guid,item_template FROM mail_items JOIN item_instance ON item_guid = guid WHERE mail_id='%u'", mail_id); if(resultItems) { do { Field *fields2 = resultItems->Fetch(); - uint32 item_guidlow = fields2[0].GetUInt32(); - uint32 item_template = fields2[1].GetUInt32(); + uint32 item_guidlow = fields2[1].GetUInt32(); + uint32 item_template = fields2[2].GetUInt32(); ItemPrototype const* itemProto = objmgr.GetItemPrototype(item_template); if(!itemProto) @@ -3670,7 +3681,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC } Item *pItem = NewItemOrBag(itemProto); - if(!pItem->LoadFromDB(item_guidlow, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER))) + if(!pItem->LoadFromDB(item_guidlow, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER),resultItems)) { pItem->FSetState(ITEM_REMOVED); pItem->SaveToDB(); // it also deletes item object ! @@ -3733,6 +3744,8 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC CharacterDatabase.PExecute("DELETE FROM mail_items WHERE receiver = '%u'",guid); CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u'",guid); CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u'",guid); + CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = '%u'",guid); + CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = '%u'",guid); CharacterDatabase.CommitTransaction(); //LoginDatabase.PExecute("UPDATE realmcharacters SET numchars = numchars - 1 WHERE acctid = %d AND realmid = %d", accountId, realmID); @@ -3763,6 +3776,10 @@ void Player::SetMovement(PlayerMovementType pType) */ void Player::BuildPlayerRepop() { + WorldPacket data(SMSG_PRE_RESURRECT, GetPackGUID().size()); + data.append(GetPackGUID()); + GetSession()->SendPacket(&data); + if(getRace() == RACE_NIGHTELF) CastSpell(this, 20584, true); // auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form) CastSpell(this, 8326, true); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?) @@ -3883,7 +3900,7 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness) if(Aura* Aur = GetAura(SPELL_ID_PASSIVE_RESURRECTION_SICKNESS,i)) { Aur->SetAuraDuration(delta*1000); - Aur->UpdateAuraDuration(); + Aur->SendAuraUpdate(false); } } } @@ -4421,7 +4438,7 @@ uint32 Player::GetShieldBlockValue() const { BaseModGroup modGroup = SHIELD_BLOCK_VALUE; - float value = GetTotalBaseModValue(modGroup) + GetStat(STAT_STRENGTH)/20 - 1; + float value = GetTotalBaseModValue(modGroup) + GetStat(STAT_STRENGTH) * 0.5f - 10; value = (value < 0) ? 0 : value; @@ -4610,7 +4627,18 @@ float Player::OCTRegenMPPerSpirit() void Player::ApplyRatingMod(CombatRating cr, int32 value, bool apply) { - ApplyModUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr, value, apply); + m_baseRatingValue[cr]+=(apply ? value : -value); + + int32 amount = uint32(m_baseRatingValue[cr]); + // Apply bonus from SPELL_AURA_MOD_RATING_FROM_STAT + // stat used stored in miscValueB for this aura + AuraList const& modRatingFromStat = GetAurasByType(SPELL_AURA_MOD_RATING_FROM_STAT); + for(AuraList::const_iterator i = modRatingFromStat.begin();i != modRatingFromStat.end(); ++i) + if ((*i)->GetMiscValue() & (1<<cr)) + amount += GetStat(Stats((*i)->GetMiscBValue())) * (*i)->GetModifier()->m_amount / 100.0f; + if (amount < 0) + amount = 0; + SetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr, uint32(amount)); float RatingCoeffecient = GetRatingCoefficient(cr); float RatingChange = 0.0f; @@ -4633,16 +4661,13 @@ void Player::ApplyRatingMod(CombatRating cr, int32 value, bool apply) UpdateBlockPercentage(); break; case CR_HIT_MELEE: - RatingChange = value / RatingCoeffecient; - m_modMeleeHitChance += apply ? RatingChange : -RatingChange; + UpdateMeleeHitChances(); break; case CR_HIT_RANGED: - RatingChange = value / RatingCoeffecient; - m_modRangedHitChance += apply ? RatingChange : -RatingChange; + UpdateRangedHitChances(); break; case CR_HIT_SPELL: - RatingChange = value / RatingCoeffecient; - m_modSpellHitChance += apply ? RatingChange : -RatingChange; + UpdateSpellHitChances(); break; case CR_CRIT_MELEE: if(affectStats) @@ -4740,6 +4765,7 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step) new_value = max; SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(new_value,max)); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL); return true; } @@ -4802,6 +4828,7 @@ bool Player::UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLeve case SKILL_HERBALISM: case SKILL_LOCKPICKING: case SKILL_JEWELCRAFTING: + case SKILL_INSCRIPTION: return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator,gathering_skill_gain); case SKILL_SKINNING: if( sWorld.getConfig(CONFIG_SKILL_CHANCE_SKINNING_STEPS)==0) @@ -4864,6 +4891,7 @@ bool Player::UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step) new_value = MaxValue; SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(new_value,MaxValue)); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL); sLog.outDebug("Player::UpdateSkillPro Chance=%3.1f%% taken", Chance/10.0); return true; } @@ -5051,7 +5079,10 @@ void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal) if(i<PLAYER_MAX_SKILLS) //has skill { if(currVal) + { SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal,maxVal)); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL); + } else //remove { // clear skill fields @@ -5099,6 +5130,7 @@ void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal) else SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,0)); SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal,maxVal)); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL); // apply skill bonuses SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0); @@ -5395,7 +5427,7 @@ void Player::CheckExploreSystem() if (isInFlight()) return; - uint16 areaFlag=MapManager::Instance().GetBaseMap(GetMapId())->GetAreaFlag(GetPositionX(),GetPositionY()); + uint16 areaFlag=MapManager::Instance().GetBaseMap(GetMapId())->GetAreaFlag(GetPositionX(),GetPositionY(),GetPositionZ()); if(areaFlag==0xffff) return; int offset = areaFlag / 32; @@ -5413,6 +5445,8 @@ void Player::CheckExploreSystem() { SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields | val)); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA); + AreaTableEntry const *p = GetAreaEntryByAreaFlagAndMap(areaFlag,GetMapId()); if(!p) { @@ -5509,6 +5543,7 @@ void Player::SendFactionState(FactionState const* faction) const { WorldPacket data(SMSG_SET_FACTION_STANDING, (16)); // last check 2.4.0 data << (float) 0; // unk 2.4.0 + data << (uint8) 0; // wotlk 8634 data << (uint32) 1; // count // for data << (uint32) faction->ReputationListID; @@ -5840,7 +5875,8 @@ bool Player::ModifyOneFactionReputation(FactionEntry const* factionEntry, int32 } } } - + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION); SendFactionState(&(itr->second)); return true; @@ -5906,6 +5942,8 @@ bool Player::SetOneFactionReputation(FactionEntry const* factionEntry, int32 sta SetFactionAtWar(&itr->second,true); SendFactionState(&(itr->second)); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION); return true; } return false; @@ -6104,12 +6142,7 @@ bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor, bool pvpt victim_guid = 0; // Don't show HK: <rank> message, only log. } - if(k_level <= 5) - k_grey = 0; - else if( k_level <= 39 ) - k_grey = k_level - 5 - k_level/10; - else - k_grey = k_level - 1 - k_level/5; + k_grey = Trinity::XP::GetGrayLevel(k_level); if(v_level<=k_grey) return false; @@ -6174,8 +6207,8 @@ bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor, bool pvpt { // Check if allowed to receive it in current map uint8 MapType = sWorld.getConfig(CONFIG_PVP_TOKEN_MAP_TYPE); - if( (MapType == 1 && !InBattleGround() && !HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP)) - || (MapType == 2 && !HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP)) + if( (MapType == 1 && !InBattleGround() && !HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP)) + || (MapType == 2 && !HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP)) || (MapType == 3 && !InBattleGround()) ) return true; @@ -6233,24 +6266,18 @@ void Player::ModifyArenaPoints( int32 value ) uint32 Player::GetGuildIdFromDB(uint64 guid) { - std::ostringstream ss; - ss<<"SELECT guildid FROM guild_member WHERE guid='"<<guid<<"'"; - QueryResult *result = CharacterDatabase.Query( ss.str().c_str() ); - if( result ) - { - uint32 v = result->Fetch()[0].GetUInt32(); - delete result; - return v; - } - else + QueryResult* result = CharacterDatabase.PQuery("SELECT guildid FROM guild_member WHERE guid='%u'", GUID_LOPART(guid)); + if(!result) return 0; + + uint32 id = result->Fetch()[0].GetUInt32(); + delete result; + return id; } uint32 Player::GetRankFromDB(uint64 guid) { - std::ostringstream ss; - ss<<"SELECT rank FROM guild_member WHERE guid='"<<guid<<"'"; - QueryResult *result = CharacterDatabase.Query( ss.str().c_str() ); + QueryResult *result = CharacterDatabase.PQuery( "SELECT rank FROM guild_member WHERE guid='%u'", GUID_LOPART(guid) ); if( result ) { uint32 v = result->Fetch()[0].GetUInt32(); @@ -6274,10 +6301,8 @@ uint32 Player::GetArenaTeamIdFromDB(uint64 guid, uint8 type) uint32 Player::GetZoneIdFromDB(uint64 guid) { - std::ostringstream ss; - - ss<<"SELECT zone FROM characters WHERE guid='"<<GUID_LOPART(guid)<<"'"; - QueryResult *result = CharacterDatabase.Query( ss.str().c_str() ); + uint32 guidLow = GUID_LOPART(guid); + QueryResult *result = CharacterDatabase.PQuery( "SELECT zone FROM characters WHERE guid='%u'", guidLow ); if (!result) return 0; Field* fields = result->Fetch(); @@ -6287,22 +6312,19 @@ uint32 Player::GetZoneIdFromDB(uint64 guid) if (!zone) { // stored zone is zero, use generic and slow zone detection - ss.str(""); - ss<<"SELECT map,position_x,position_y FROM characters WHERE guid='"<<GUID_LOPART(guid)<<"'"; - result = CharacterDatabase.Query(ss.str().c_str()); + result = CharacterDatabase.PQuery("SELECT map,position_x,position_y,position_z FROM characters WHERE guid='%u'", guidLow); if( !result ) return 0; fields = result->Fetch(); - uint32 map = fields[0].GetUInt32(); + uint32 map = fields[0].GetUInt32(); float posx = fields[1].GetFloat(); float posy = fields[2].GetFloat(); + float posz = fields[3].GetFloat(); delete result; - zone = MapManager::Instance().GetZoneId(map,posx,posy); + zone = MapManager::Instance().GetZoneId(map,posx,posy,posz); - ss.str(""); - ss << "UPDATE characters SET zone='"<<zone<<"' WHERE guid='"<<GUID_LOPART(guid)<<"'"; - CharacterDatabase.Execute(ss.str().c_str()); + CharacterDatabase.PExecute("UPDATE characters SET zone='%u' WHERE guid='%u'", zone, guidLow); } return zone; @@ -6319,14 +6341,14 @@ void Player::UpdateArea(uint32 newArea) if(area && (area->flags & AREA_FLAG_ARENA)) { if(!isGameMaster()) - SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP); + SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); } else { // remove ffa flag only if not ffapvp realm // removal in sanctuaries and capitals is handled in zone update - if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) && !sWorld.IsFFAPvPRealm()) - RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP); + if(HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP) && !sWorld.IsFFAPvPRealm()) + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); } UpdateAreaDependentAuras(newArea); @@ -6388,13 +6410,13 @@ void Player::UpdateZone(uint32 newZone) if(zone->flags & AREA_FLAG_SANCTUARY) // in sanctuary { - SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY); + SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); if(sWorld.IsFFAPvPRealm()) - RemoveFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP); + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); } else { - RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY); + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); } if(zone->flags & AREA_FLAG_CAPITAL) // in capital city @@ -6404,7 +6426,7 @@ void Player::UpdateZone(uint32 newZone) InnEnter(time(0),GetMapId(),0,0,0); if(sWorld.IsFFAPvPRealm()) - RemoveFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP); + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); } else // anywhere else { @@ -6418,7 +6440,7 @@ void Player::UpdateZone(uint32 newZone) SetRestType(REST_TYPE_NO); if(sWorld.IsFFAPvPRealm()) - SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP); + SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); } } else // not in tavern (leave city then) @@ -6428,7 +6450,7 @@ void Player::UpdateZone(uint32 newZone) // Set player to FFA PVP when not in rested environment. if(sWorld.IsFFAPvPRealm()) - SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP); + SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); } } } @@ -6616,19 +6638,43 @@ void Player::_ApplyItemMods(Item *item, uint8 slot,bool apply) sLog.outDebug("_ApplyItemMods complete."); } -void Player::_ApplyItemBonuses(ItemPrototype const *proto,uint8 slot,bool apply) +void Player::_ApplyItemBonuses(ItemPrototype const *proto, uint8 slot, bool apply) { if(slot >= INVENTORY_SLOT_BAG_END || !proto) return; for (int i = 0; i < 10; i++) { - float val = float (proto->ItemStat[i].ItemStatValue); + uint32 statType = 0; + int32 val = 0; + + if(proto->ScalingStatDistribution) + { + if(ScalingStatDistributionEntry const *ssd = sScalingStatDistributionStore.LookupEntry(proto->ScalingStatDistribution)) + { + statType = ssd->StatMod[i]; - if(val==0) + if(uint32 modifier = ssd->Modifier[i]) + { + uint32 level = ((getLevel() > ssd->MaxLevel) ? ssd->MaxLevel : getLevel()); + if(ScalingStatValuesEntry const *ssv = sScalingStatValuesStore.LookupEntry(level)) + { + int multiplier = ssv->Multiplier[proto->GetScalingStatValuesColumn()]; + val = (multiplier * modifier) / 10000; + } + } + } + } + else + { + statType = proto->ItemStat[i].ItemStatType; + val = float(proto->ItemStat[i].ItemStatValue); + } + + if(val == 0) continue; - switch (proto->ItemStat[i].ItemStatType) + switch (statType) { case ITEM_MOD_MANA: HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(val), apply); @@ -6746,6 +6792,9 @@ void Player::_ApplyItemBonuses(ItemPrototype const *proto,uint8 slot,bool apply) case ITEM_MOD_EXPERTISE_RATING: ApplyRatingMod(CR_EXPERTISE, int32(val), apply); break; + case ITEM_MOD_ARMOR_PENETRATION_RATING: + ApplyRatingMod(CR_ARMOR_PENETRATION, int32(val), apply); + break; } } @@ -7361,6 +7410,17 @@ void Player::SendLoot(uint64 guid, LootType loot_type) loot->FillLoot(item->GetEntry(), LootTemplates_Prospecting, this); } } + else if(loot_type == LOOT_MILLING) + { + loot = &item->loot; + + if(!item->m_lootGenerated) + { + item->m_lootGenerated = true; + loot->clear(); + loot->FillLoot(item->GetEntry(), LootTemplates_Milling, this); + } + } else { loot = &item->loot; @@ -7556,8 +7616,8 @@ void Player::SendLoot(uint64 guid, LootType loot_type) conditional_list = itr->second; } - // LOOT_PICKPOCKETING, LOOT_PROSPECTING, LOOT_DISENCHANTING and LOOT_INSIGNIA unsupported by client, sending LOOT_SKINNING instead - if(loot_type == LOOT_PICKPOCKETING || loot_type == LOOT_DISENCHANTING || loot_type == LOOT_PROSPECTING || loot_type == LOOT_INSIGNIA) + // LOOT_PICKPOCKETING, LOOT_PROSPECTING, LOOT_DISENCHANTING, LOOT_INSIGNIA and LOOT_MILLING unsupported by client, sending LOOT_SKINNING instead + if(loot_type == LOOT_PICKPOCKETING || loot_type == LOOT_DISENCHANTING || loot_type == LOOT_PROSPECTING || loot_type == LOOT_INSIGNIA || loot_type == LOOT_MILLING) loot_type = LOOT_SKINNING; if(loot_type == LOOT_FISHINGHOLE) @@ -8281,7 +8341,8 @@ uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap // (this will be replace mainhand weapon at auto equip instead unwonted "you don't known dual wielding" ... if(CanDualWield()) slots[1] = EQUIPMENT_SLOT_OFFHAND; - };break; + break; + }; case INVTYPE_SHIELD: slots[0] = EQUIPMENT_SLOT_OFFHAND; break; @@ -8290,6 +8351,8 @@ uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap break; case INVTYPE_2HWEAPON: slots[0] = EQUIPMENT_SLOT_MAINHAND; + if (CanDualWield() && CanTitanGrip()) + slots[1] = EQUIPMENT_SLOT_OFFHAND; break; case INVTYPE_TABARD: slots[0] = EQUIPMENT_SLOT_TABARD; @@ -8335,6 +8398,10 @@ uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap if (pClass == CLASS_WARLOCK) slots[0] = EQUIPMENT_SLOT_RANGED; break; + case ITEM_SUBCLASS_ARMOR_SIGIL: + if (pClass == CLASS_DEATH_KNIGHT) + slots[0] = EQUIPMENT_SLOT_RANGED; + break; } break; } @@ -8360,14 +8427,8 @@ uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap { if ( slots[i] != NULL_SLOT && !GetItemByPos( INVENTORY_SLOT_BAG_0, slots[i] ) ) { - // in case 2hand equipped weapon offhand slot empty but not free - if(slots[i]==EQUIPMENT_SLOT_OFFHAND) - { - Item* mainItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND ); - if(!mainItem || mainItem->GetProto()->InventoryType != INVTYPE_2HWEAPON) - return slots[i]; - } - else + // in case 2hand equipped weapon (without titan grip) offhand slot empty but not free + if(slots[i]!=EQUIPMENT_SLOT_OFFHAND || !IsTwoHandUsed()) return slots[i]; } } @@ -8417,7 +8478,7 @@ uint8 Player::CanUnequipItems( uint32 item, uint32 count ) const return EQUIP_ERR_OK; } } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); if( pItem && pItem->GetEntry() == item ) @@ -8459,7 +8520,7 @@ uint32 Player::GetItemCount( uint32 item, bool inBankAlso, Item* skipItem ) cons if( pItem && pItem != skipItem && pItem->GetEntry() == item ) count += pItem->GetCount(); } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); if( pItem && pItem != skipItem && pItem->GetEntry() == item ) @@ -8519,7 +8580,7 @@ Item* Player::GetItemByGuid( uint64 guid ) const if( pItem && pItem->GetGUID() == guid ) return pItem; } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); if( pItem && pItem->GetGUID() == guid ) @@ -8565,7 +8626,7 @@ Item* Player::GetItemByPos( uint16 pos ) const Item* Player::GetItemByPos( uint8 bag, uint8 slot ) const { - if( bag == INVENTORY_SLOT_BAG_0 && ( slot < BANK_SLOT_BAG_END || slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_END ) ) + if( bag == INVENTORY_SLOT_BAG_0 && ( slot < BANK_SLOT_BAG_END || slot >= KEYRING_SLOT_START && slot < QUESTBAG_SLOT_END ) ) return m_items[slot]; else if(bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END || bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END ) @@ -8643,7 +8704,7 @@ bool Player::IsInventoryPos( uint8 bag, uint8 slot ) return true; if( bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END ) return true; - if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_END ) ) + if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= KEYRING_SLOT_START && slot < QUESTBAG_SLOT_END ) ) return true; return false; } @@ -8764,7 +8825,7 @@ bool Player::HasItemCount( uint32 item, uint32 count, bool inBankAlso ) const return true; } } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); if( pItem && pItem->GetEntry() == item ) @@ -8862,12 +8923,12 @@ uint8 Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, } // no maximum - if(pProto->MaxCount == 0) + if(pProto->MaxCount <= 0) return EQUIP_ERR_OK; uint32 curcount = GetItemCount(pProto->ItemId,true,pItem); - if( curcount + count > pProto->MaxCount ) + if (curcount + count > uint32(pProto->MaxCount)) { if(no_space_count) *no_space_count = count +curcount - pProto->MaxCount; @@ -8886,7 +8947,7 @@ bool Player::HasItemTotemCategory( uint32 TotemCategory ) const if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory )) return true; } - for(uint8 i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i) + for(uint8 i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; ++i) { pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory )) @@ -8926,6 +8987,18 @@ uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountV if(slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_START+GetMaxKeyringSize() && !(pProto->BagFamily & BAG_FAMILY_MASK_KEYS)) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; + // vanitypet case (disabled until proper implement) + if(slot >= VANITYPET_SLOT_START && slot < VANITYPET_SLOT_END && !(false /*pProto->BagFamily & BAG_FAMILY_MASK_VANITY_PETS*/)) + return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; + + // currencytoken case (disabled until proper implement) + if(slot >= CURRENCYTOKEN_SLOT_START && slot < CURRENCYTOKEN_SLOT_END && !(false /*pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS*/)) + return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; + + // guestbag case (disabled until proper implement) + if(slot >= QUESTBAG_SLOT_START && slot < QUESTBAG_SLOT_END && !(false /*pProto->BagFamily & BAG_FAMILY_MASK_QUEST_ITEMS*/)) + return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; + // prevent cheating if(slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END || slot >= PLAYER_SLOT_END) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; @@ -8945,7 +9018,7 @@ uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountV } // non empty stack with space - need_space = pProto->Stackable; + need_space = pProto->GetMaxStackSize(); } // non empty slot, check item type else @@ -8955,10 +9028,11 @@ uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountV return EQUIP_ERR_ITEM_CANT_STACK; // check free space - if(pItem2->GetCount() >= pProto->Stackable) + if(pItem2->GetCount() >= pProto->GetMaxStackSize()) return EQUIP_ERR_ITEM_CANT_STACK; - need_space = pProto->Stackable - pItem2->GetCount(); + // free stack space or infinity + need_space = pProto->GetMaxStackSize() - pItem2->GetCount(); } if(need_space > count) @@ -9012,9 +9086,9 @@ uint8 Player::_CanStoreItem_InBag( uint8 bag, ItemPosCountVec &dest, ItemPrototy if( pItem2 ) { - if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->Stackable ) + if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->GetMaxStackSize()) { - uint32 need_space = pProto->Stackable - pItem2->GetCount(); + uint32 need_space = pProto->GetMaxStackSize() - pItem2->GetCount(); if(need_space > count) need_space = count; @@ -9031,7 +9105,7 @@ uint8 Player::_CanStoreItem_InBag( uint8 bag, ItemPosCountVec &dest, ItemPrototy } else { - uint32 need_space = pProto->Stackable; + uint32 need_space = pProto->GetMaxStackSize(); if(need_space > count) need_space = count; @@ -9069,9 +9143,9 @@ uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, if( pItem2 ) { - if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->Stackable ) + if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->GetMaxStackSize()) { - uint32 need_space = pProto->Stackable - pItem2->GetCount(); + uint32 need_space = pProto->GetMaxStackSize() - pItem2->GetCount(); if(need_space > count) need_space = count; ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space); @@ -9087,7 +9161,7 @@ uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, } else { - uint32 need_space = pProto->Stackable; + uint32 need_space = pProto->GetMaxStackSize(); if(need_space > count) need_space = count; @@ -9166,11 +9240,11 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 if( bag != NULL_BAG ) { // search stack in bag for merge to - if( pProto->Stackable > 1 ) + if( pProto->Stackable != 1 ) { if( bag == INVENTORY_SLOT_BAG_0 ) // inventory { - res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_END,dest,pProto,count,true,pItem,bag,slot); + res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,QUESTBAG_SLOT_END,dest,pProto,count,true,pItem,bag,slot); if(res!=EQUIP_ERR_OK) { if(no_space_count) @@ -9257,6 +9331,72 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } } + /* until proper implementation + else if(pProto->BagFamily & BAG_FAMILY_MASK_VANITY_PETS) + { + res = _CanStoreItem_InInventorySlots(VANITYPET_SLOT_START,VANITYPET_SLOT_END,dest,pProto,count,false,pItem,bag,slot); + if(res!=EQUIP_ERR_OK) + { + if(no_space_count) + *no_space_count = count + no_similar_count; + return res; + } + + if(count==0) + { + if(no_similar_count==0) + return EQUIP_ERR_OK; + + if(no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; + } + } + */ + /* until proper implementation + else if(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) + { + res = _CanStoreItem_InInventorySlots(CURRENCYTOKEN_SLOT_START,CURRENCYTOKEN_SLOT_END,dest,pProto,count,false,pItem,bag,slot); + if(res!=EQUIP_ERR_OK) + { + if(no_space_count) + *no_space_count = count + no_similar_count; + return res; + } + + if(count==0) + { + if(no_similar_count==0) + return EQUIP_ERR_OK; + + if(no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; + } + } + */ + /* until proper implementation + else if(pProto->BagFamily & BAG_FAMILY_MASK_QUEST_ITEMS) + { + res = _CanStoreItem_InInventorySlots(QUESTBAG_SLOT_START,QUESTBAG_SLOT_END,dest,pProto,count,false,pItem,bag,slot); + if(res!=EQUIP_ERR_OK) + { + if(no_space_count) + *no_space_count = count + no_similar_count; + return res; + } + + if(count==0) + { + if(no_similar_count==0) + return EQUIP_ERR_OK; + + if(no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; + } + } + */ res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot); if(res!=EQUIP_ERR_OK) @@ -9304,9 +9444,9 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 // not specific bag or have space for partly store only in specific bag // search stack for merge to - if( pProto->Stackable > 1 ) + if( pProto->Stackable != 1 ) { - res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_END,dest,pProto,count,true,pItem,bag,slot); + res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,QUESTBAG_SLOT_END,dest,pProto,count,true,pItem,bag,slot); if(res!=EQUIP_ERR_OK) { if(no_space_count) @@ -9404,6 +9544,72 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } } + /* until proper implementation + else if(false pProto->BagFamily & BAG_FAMILY_MASK_VANITY_PETS) + { + res = _CanStoreItem_InInventorySlots(VANITYPET_SLOT_START,VANITYPET_SLOT_END,dest,pProto,count,false,pItem,bag,slot); + if(res!=EQUIP_ERR_OK) + { + if(no_space_count) + *no_space_count = count + no_similar_count; + return res; + } + + if(count==0) + { + if(no_similar_count==0) + return EQUIP_ERR_OK; + + if(no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; + } + } + */ + /* until proper implementation + else if(false pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) + { + res = _CanStoreItem_InInventorySlots(CURRENCYTOKEN_SLOT_START,CURRENCYTOKEN_SLOT_END,dest,pProto,count,false,pItem,bag,slot); + if(res!=EQUIP_ERR_OK) + { + if(no_space_count) + *no_space_count = count + no_similar_count; + return res; + } + + if(count==0) + { + if(no_similar_count==0) + return EQUIP_ERR_OK; + + if(no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; + } + } + */ + /* until proper implementation + else if(false pProto->BagFamily & BAG_FAMILY_MASK_QUEST_ITEMS) + { + res = _CanStoreItem_InInventorySlots(QUESTBAG_SLOT_START,QUESTBAG_SLOT_END,dest,pProto,count,false,pItem,bag,slot); + if(res!=EQUIP_ERR_OK) + { + if(no_space_count) + *no_space_count = count + no_similar_count; + return res; + } + + if(count==0) + { + if(no_similar_count==0) + return EQUIP_ERR_OK; + + if(no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; + } + } + */ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) { @@ -9474,10 +9680,16 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const int inv_slot_items[INVENTORY_SLOT_ITEM_END-INVENTORY_SLOT_ITEM_START]; int inv_bags[INVENTORY_SLOT_BAG_END-INVENTORY_SLOT_BAG_START][MAX_BAG_SIZE]; int inv_keys[KEYRING_SLOT_END-KEYRING_SLOT_START]; + int inv_pets[VANITYPET_SLOT_END-VANITYPET_SLOT_START]; + int inv_tokens[CURRENCYTOKEN_SLOT_END-CURRENCYTOKEN_SLOT_START]; + int inv_quests[QUESTBAG_SLOT_END-QUESTBAG_SLOT_START]; memset(inv_slot_items,0,sizeof(int)*(INVENTORY_SLOT_ITEM_END-INVENTORY_SLOT_ITEM_START)); memset(inv_bags,0,sizeof(int)*(INVENTORY_SLOT_BAG_END-INVENTORY_SLOT_BAG_START)*MAX_BAG_SIZE); memset(inv_keys,0,sizeof(int)*(KEYRING_SLOT_END-KEYRING_SLOT_START)); + memset(inv_pets,0,sizeof(int)*(VANITYPET_SLOT_END-VANITYPET_SLOT_START)); + memset(inv_tokens,0,sizeof(int)*(CURRENCYTOKEN_SLOT_END-CURRENCYTOKEN_SLOT_START)); + memset(inv_quests,0,sizeof(int)*(QUESTBAG_SLOT_END-QUESTBAG_SLOT_START)); for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++) { @@ -9499,6 +9711,36 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const } } + for(int i = VANITYPET_SLOT_START; i < VANITYPET_SLOT_END; i++) + { + pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); + + if (pItem2 && !pItem2->IsInTrade()) + { + inv_pets[i-VANITYPET_SLOT_START] = pItem2->GetCount(); + } + } + + for(int i = CURRENCYTOKEN_SLOT_START; i < CURRENCYTOKEN_SLOT_END; i++) + { + pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); + + if (pItem2 && !pItem2->IsInTrade()) + { + inv_tokens[i-CURRENCYTOKEN_SLOT_START] = pItem2->GetCount(); + } + } + + for(int i = QUESTBAG_SLOT_START; i < QUESTBAG_SLOT_END; i++) + { + pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); + + if (pItem2 && !pItem2->IsInTrade()) + { + inv_quests[i-QUESTBAG_SLOT_START] = pItem2->GetCount(); + } + } + for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) { if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i )) @@ -9542,14 +9784,14 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const return res; // search stack for merge to - if( pProto->Stackable > 1 ) + if( pProto->Stackable != 1 ) { bool b_found = false; for(int t = KEYRING_SLOT_START; t < KEYRING_SLOT_END; t++) { pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t ); - if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_keys[t-KEYRING_SLOT_START] + pItem->GetCount() <= pProto->Stackable ) + if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_keys[t-KEYRING_SLOT_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) { inv_keys[t-KEYRING_SLOT_START] += pItem->GetCount(); b_found = true; @@ -9558,10 +9800,46 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const } if (b_found) continue; + for(int t = VANITYPET_SLOT_START; t < VANITYPET_SLOT_END; t++) + { + pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t ); + if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_pets[t-VANITYPET_SLOT_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) + { + inv_pets[t-VANITYPET_SLOT_START] += pItem->GetCount(); + b_found = true; + break; + } + } + if (b_found) continue; + + for(int t = CURRENCYTOKEN_SLOT_START; t < CURRENCYTOKEN_SLOT_END; t++) + { + pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t ); + if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_tokens[t-CURRENCYTOKEN_SLOT_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) + { + inv_tokens[t-CURRENCYTOKEN_SLOT_START] += pItem->GetCount(); + b_found = true; + break; + } + } + if (b_found) continue; + + for(int t = QUESTBAG_SLOT_START; t < QUESTBAG_SLOT_END; t++) + { + pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t ); + if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_quests[t-QUESTBAG_SLOT_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) + { + inv_quests[t-QUESTBAG_SLOT_START] += pItem->GetCount(); + b_found = true; + break; + } + } + if (b_found) continue; + for(int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; t++) { pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t ); - if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_slot_items[t-INVENTORY_SLOT_ITEM_START] + pItem->GetCount() <= pProto->Stackable ) + if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_slot_items[t-INVENTORY_SLOT_ITEM_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) { inv_slot_items[t-INVENTORY_SLOT_ITEM_START] += pItem->GetCount(); b_found = true; @@ -9578,7 +9856,7 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const for(uint32 j = 0; j < pBag->GetBagSize(); j++) { pItem2 = GetItemByPos( t, j ); - if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_bags[t-INVENTORY_SLOT_BAG_START][j] + pItem->GetCount() <= pProto->Stackable ) + if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_bags[t-INVENTORY_SLOT_BAG_START][j] + pItem->GetCount() <= pProto->GetMaxStackSize()) { inv_bags[t-INVENTORY_SLOT_BAG_START][j] += pItem->GetCount(); b_found = true; @@ -9610,6 +9888,55 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const if (b_found) continue; + /* until proper implementation + if(pProto->BagFamily & BAG_FAMILY_MASK_VANITY_PETS) + { + for(uint32 t = VANITYPET_SLOT_START; t < VANITYPET_SLOT_END; ++t) + { + if( inv_pets[t-VANITYPET_SLOT_START] == 0 ) + { + inv_pets[t-VANITYPET_SLOT_START] = 1; + b_found = true; + break; + } + } + } + + if (b_found) continue; + */ + /* until proper implementation + if(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) + { + for(uint32 t = CURRENCYTOKEN_SLOT_START; t < CURRENCYTOKEN_SLOT_END; ++t) + { + if( inv_tokens[t-CURRENCYTOKEN_SLOT_START] == 0 ) + { + inv_tokens[t-CURRENCYTOKEN_SLOT_START] = 1; + b_found = true; + break; + } + } + } + + if (b_found) continue; + */ + /* until proper implementation + if(pProto->BagFamily & BAG_FAMILY_MASK_QUEST_ITEMS) + { + for(uint32 t = QUESTBAG_SLOT_START; t < QUESTBAG_SLOT_END; ++t) + { + if( inv_quests[t-QUESTBAG_SLOT_START] == 0 ) + { + inv_quests[t-QUESTBAG_SLOT_START] = 1; + b_found = true; + break; + } + } + } + + if (b_found) continue; + */ + for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; t++) { pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t ); @@ -9795,33 +10122,42 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo if(eslot == EQUIPMENT_SLOT_OFFHAND) { - if( type == INVTYPE_WEAPON || type == INVTYPE_WEAPONOFFHAND ) + if (type == INVTYPE_WEAPON || type == INVTYPE_WEAPONOFFHAND) { if(!CanDualWield()) return EQUIP_ERR_CANT_DUAL_WIELD; } - - Item *mainItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND ); - if(mainItem) + else if (type == INVTYPE_2HWEAPON) { - if(mainItem->GetProto()->InventoryType == INVTYPE_2HWEAPON) - return EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED; + if(!CanDualWield() || !CanTitanGrip()) + return EQUIP_ERR_CANT_DUAL_WIELD; } + + if(IsTwoHandUsed()) + return EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED; } // equip two-hand weapon case (with possible unequip 2 items) if( type == INVTYPE_2HWEAPON ) { - if(eslot != EQUIPMENT_SLOT_MAINHAND) + if (eslot == EQUIPMENT_SLOT_OFFHAND) + { + if (!CanTitanGrip()) + return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED; + } + else if (eslot != EQUIPMENT_SLOT_MAINHAND) return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED; - // offhand item must can be stored in inventory for offhand item and it also must be unequipped - Item *offItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND ); - ItemPosCountVec off_dest; - if( offItem && (!not_loading || - CanUnequipItem(uint16(INVENTORY_SLOT_BAG_0) << 8 | EQUIPMENT_SLOT_OFFHAND,false) != EQUIP_ERR_OK || - CanStoreItem( NULL_BAG, NULL_SLOT, off_dest, offItem, false ) != EQUIP_ERR_OK ) ) - return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_INVENTORY_FULL; + if (!CanTitanGrip()) + { + // offhand item must can be stored in inventory for offhand item and it also must be unequipped + Item *offItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND ); + ItemPosCountVec off_dest; + if( offItem && (!not_loading || + CanUnequipItem(uint16(INVENTORY_SLOT_BAG_0) << 8 | EQUIPMENT_SLOT_OFFHAND,false) != EQUIP_ERR_OK || + CanStoreItem( NULL_BAG, NULL_SLOT, off_dest, offItem, false ) != EQUIP_ERR_OK ) ) + return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_INVENTORY_FULL; + } } dest = ((INVENTORY_SLOT_BAG_0 << 8) | eslot); return EQUIP_ERR_OK; @@ -9939,7 +10275,7 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p } // search stack in bag for merge to - if( pProto->Stackable > 1 ) + if( pProto->Stackable != 1 ) { if( bag == INVENTORY_SLOT_BAG_0 ) { @@ -9991,7 +10327,7 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p // not specific bag or have space for partly store only in specific bag // search stack for merge to - if( pProto->Stackable > 1 ) + if( pProto->Stackable != 1 ) { // in slots res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot); @@ -10741,7 +11077,7 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq } } } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); if( pItem && pItem->GetEntry() == item ) @@ -10844,7 +11180,7 @@ void Player::DestroyZoneLimitedItem( bool update, uint32 new_zone ) if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) ) DestroyItem( INVENTORY_SLOT_BAG_0, i, update); } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) ) @@ -11179,7 +11515,7 @@ void Player::SwapItem( uint16 src, uint16 dst ) // can be merge/fill if(msg == EQUIP_ERR_OK) { - if( pSrcItem->GetCount() + pDstItem->GetCount() <= pSrcItem->GetProto()->Stackable ) + if( pSrcItem->GetCount() + pDstItem->GetCount() <= pSrcItem->GetProto()->GetMaxStackSize()) { RemoveItem(srcbag, srcslot, true); @@ -11195,8 +11531,8 @@ void Player::SwapItem( uint16 src, uint16 dst ) } else { - pSrcItem->SetCount( pSrcItem->GetCount() + pDstItem->GetCount() - pSrcItem->GetProto()->Stackable ); - pDstItem->SetCount( pSrcItem->GetProto()->Stackable ); + pSrcItem->SetCount( pSrcItem->GetCount() + pDstItem->GetCount() - pSrcItem->GetProto()->GetMaxStackSize()); + pDstItem->SetCount( pSrcItem->GetProto()->GetMaxStackSize()); pSrcItem->SetState(ITEM_CHANGED, this); pDstItem->SetState(ITEM_CHANGED, this); if( IsInWorld() ) @@ -11841,6 +12177,10 @@ void Player::ApplyEnchantment(Item *item,EnchantmentSlot slot,bool apply, bool a ((Player*)this)->ApplyRatingMod(CR_EXPERTISE, enchant_amount, apply); sLog.outDebug("+ %u EXPERTISE", enchant_amount); break; + case ITEM_MOD_ARMOR_PENETRATION_RATING: + ((Player*)this)->ApplyRatingMod(CR_ARMOR_PENETRATION, enchant_amount, apply); + sLog.outDebug("+ %u ARMOR PENETRATION", enchant_amount); + break; default: break; } @@ -12563,7 +12903,10 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver } if(pQuest->IsDaily()) + { SetDailyQuestStatus(quest_id); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST, 1); + } if ( !pQuest->IsRepeatable() ) SetQuestStatus(quest_id, QUEST_STATUS_COMPLETE); @@ -12576,6 +12919,8 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver SendQuestReward( pQuest, XP, questGiver ); if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED; + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST); } void Player::FailQuest( uint32 quest_id ) @@ -13201,6 +13546,7 @@ void Player::ItemAddedQuestCheck( uint32 entry, uint32 count ) } } UpdateForQuestsGO(); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM, entry); } void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count ) @@ -13247,6 +13593,7 @@ void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count ) void Player::KilledMonster( uint32 entry, uint64 guid ) { uint32 addkillcount = 1; + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, entry, addkillcount); for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ ) { uint32 questid = GetQuestSlotQuestId(i); @@ -13525,13 +13872,12 @@ void Player::SendQuestReward( Quest const *pQuest, uint32 XP, Object * questGive uint32 questid = pQuest->GetQuestId(); sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_QUEST_COMPLETE quest = %u", questid ); gameeventmgr.HandleQuestComplete(questid); - WorldPacket data( SMSG_QUESTGIVER_QUEST_COMPLETE, (4+4+4+4+4+4+pQuest->GetRewItemsCount()*8) ); - data << questid; - data << uint32(0x03); + WorldPacket data( SMSG_QUESTGIVER_QUEST_COMPLETE, (4+4+4+4+4) ); + data << uint32(questid); if ( getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) ) { - data << XP; + data << uint32(XP); data << uint32(pQuest->GetRewOrReqMoney()); } else @@ -13539,16 +13885,9 @@ void Player::SendQuestReward( Quest const *pQuest, uint32 XP, Object * questGive data << uint32(0); data << uint32(pQuest->GetRewOrReqMoney() + int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY))); } - data << uint32(0); // new 2.3.0, HonorPoints? - data << uint32( pQuest->GetRewItemsCount() ); // max is 5 - for (uint32 i = 0; i < pQuest->GetRewItemsCount(); ++i) - { - if ( pQuest->RewItemId[i] > 0 ) - data << pQuest->RewItemId[i] << pQuest->RewItemCount[i]; - else - data << uint32(0) << uint32(0); - } + data << uint32(10*Trinity::Honor::hk_honor_at_level(getLevel(), pQuest->GetRewHonorableKills())); + data << uint32(pQuest->GetBonusTalents()); // bonus talents GetSession()->SendPacket( &data ); if (pQuest->GetQuestCompleteScript() != 0) @@ -13559,8 +13898,9 @@ void Player::SendQuestFailed( uint32 quest_id ) { if( quest_id ) { - WorldPacket data( SMSG_QUESTGIVER_QUEST_FAILED, 4 ); + WorldPacket data( SMSG_QUESTGIVER_QUEST_FAILED, 4+4 ); data << quest_id; + data << uint32(0); // failed reason (4 for inventory is full) GetSession()->SendPacket( &data ); sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_FAILED"); } @@ -13599,10 +13939,10 @@ void Player::SendPushToPartyResponse( Player *pPlayer, uint32 msg ) void Player::SendQuestUpdateAddItem( Quest const* pQuest, uint32 item_idx, uint32 count ) { - WorldPacket data( SMSG_QUESTUPDATE_ADD_ITEM, (4+4) ); + WorldPacket data( SMSG_QUESTUPDATE_ADD_ITEM, 0 ); sLog.outDebug( "WORLD: Sent SMSG_QUESTUPDATE_ADD_ITEM" ); - data << pQuest->ReqItemId[item_idx]; - data << count; + //data << pQuest->ReqItemId[item_idx]; + //data << count; GetSession()->SendPacket( &data ); } @@ -13648,7 +13988,7 @@ bool Player::MinimalLoadFromDB( QueryResult *result, uint32 guid ) if(!LoadValues( fields[1].GetString())) { - sLog.outError("ERROR: Player #%d have broken data in `data` field. Can't be loaded.",GUID_LOPART(guid)); + sLog.outError("ERROR: Player #%d have broken data in `data` field. Can't be loaded for character list.",GUID_LOPART(guid)); if(delete_result) delete result; return false; } @@ -13718,7 +14058,7 @@ void Player::_LoadArenaTeamInfo(QueryResult *result) ArenaTeam* aTeam = objmgr.GetArenaTeamById(arenateamid); if(!aTeam) { - sLog.outError("FATAL: couldn't load arenateam %u", arenateamid); + sLog.outError("Player::_LoadArenaTeamInfo: couldn't load arenateam %u, week %u, season %u, rating %u", arenateamid, played_week, played_season, personal_rating); continue; } uint8 arenaSlot = aTeam->GetSlot(); @@ -13921,7 +14261,6 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) uint32 transGUID = fields[24].GetUInt32(); Relocate(fields[6].GetFloat(),fields[7].GetFloat(),fields[8].GetFloat(),fields[10].GetFloat()); - SetFallInformation(0, fields[8].GetFloat()); SetMapId(fields[9].GetUInt32()); SetDifficulty(fields[32].GetUInt32()); // may be changed in _LoadGroup @@ -14010,6 +14349,16 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) // getmap calls won't create new maps SetInstanceId(map->GetInstanceId()); + // if the player is in an instance and it has been reset in the meantime teleport him to the entrance + if(GetInstanceId() && !sInstanceSaveManager.GetInstanceSave(GetInstanceId())) + { + AreaTrigger const* at = objmgr.GetMapEntranceTrigger(GetMapId()); + if(at) + Relocate(at->target_X, at->target_Y, at->target_Z, at->target_Orientation); + else + sLog.outError("Player %s(GUID: %u) logged in to a reset instance (map: %u) and there is no aretrigger leading to this map. Thus he can't be ported back to the entrance. This _might_ be an exploit attempt.", GetName(), GetGUIDLow(), GetMapId()); + } + SaveRecallPosition(); if (transGUID != 0) @@ -14120,10 +14469,10 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) uint32 extraflags = fields[25].GetUInt32(); m_stableSlots = fields[26].GetUInt32(); - if(m_stableSlots > 2) + if(m_stableSlots > 4) { - sLog.outError("Player can have not more 2 stable slots, but have in DB %u",uint32(m_stableSlots)); - m_stableSlots = 2; + sLog.outError("Player can have not more 4 stable slots, but have in DB %u",uint32(m_stableSlots)); + m_stableSlots = 4; } m_atLoginFlags = fields[27].GetUInt32(); @@ -14192,6 +14541,8 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) // reset stats before loading any modifiers InitStatsForLevel(); InitTaxiNodesForLevel(); + InitGlyphsForLevel(); + InitRunes(); // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods() @@ -14279,6 +14630,9 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) // flight will started later } + // has to be called after last Relocate() in Player::LoadFromDB + SetFallInformation(0, GetPositionZ()); + _LoadSpellCooldowns(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS)); // Spell code allow apply any auras to dead character in load time in aura/spell/item loading @@ -14337,6 +14691,8 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) _LoadDeclinedNames(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES)); + m_achievementMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS), holder->GetResult(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS)); + m_achievementMgr.CheckAllAchievementCriteria(); return true; } @@ -14390,10 +14746,6 @@ void Player::_LoadAuras(QueryResult *result, uint32 timediff) for (int i = 0; i < TOTAL_AURAS; i++) m_modAuras[i].clear(); - // all aura related fields - for(int i = UNIT_FIELD_AURA; i <= UNIT_FIELD_AURASTATE; ++i) - SetUInt32Value(i, 0); - //QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'",GetGUIDLow()); if(result) @@ -14639,15 +14991,16 @@ void Player::_LoadInventory(QueryResult *result, uint32 timediff) // load mailed item which should receive current player void Player::_LoadMailedItems(Mail *mail) { - QueryResult* result = CharacterDatabase.PQuery("SELECT item_guid, item_template FROM mail_items WHERE mail_id='%u'", mail->messageID); + // data needs to be at first place for Item::LoadFromDB + QueryResult* result = CharacterDatabase.PQuery("SELECT data, item_guid, item_template FROM mail_items JOIN item_instance ON item_guid = guid WHERE mail_id='%u'", mail->messageID); if(!result) return; do { Field *fields = result->Fetch(); - uint32 item_guid_low = fields[0].GetUInt32(); - uint32 item_template = fields[1].GetUInt32(); + uint32 item_guid_low = fields[1].GetUInt32(); + uint32 item_template = fields[2].GetUInt32(); mail->AddItem(item_guid_low, item_template); @@ -14663,7 +15016,7 @@ void Player::_LoadMailedItems(Mail *mail) Item *item = NewItemOrBag(proto); - if(!item->LoadFromDB(item_guid_low, 0)) + if(!item->LoadFromDB(item_guid_low, 0, result)) { sLog.outError( "Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail->messageID, item_guid_low); CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low); @@ -15305,7 +15658,7 @@ void Player::SaveToDB() SetByteValue(UNIT_FIELD_BYTES_1, 0, 0); // stand state SetByteValue(UNIT_FIELD_BYTES_2, 3, 0); // shapeshift SetByteValue(UNIT_FIELD_BYTES_1, 3, 0); // stand flags? - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); SetDisplayId(GetNativeDisplayId()); bool inworld = IsInWorld(); @@ -15456,7 +15809,7 @@ void Player::SaveToDB() pet->SavePetToDB(PET_SAVE_AS_CURRENT); //to prevent access to DB we should cache some data, which is used very often - CachePlayerInfoMap::iterator _iter = objmgr.m_mPlayerInfoMap.find(GetGUIDLow()); + /*CachePlayerInfoMap::iterator _iter = objmgr.m_mPlayerInfoMap.find(GetGUIDLow()); if(_iter != objmgr.m_mPlayerInfoMap.end())//skip new players { _iter->second->unLevel = getLevel(); @@ -15470,7 +15823,8 @@ void Player::SaveToDB() _iter->second->unArenaInfoId0 = GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (0 * 6)); _iter->second->unArenaInfoId1 = GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (1 * 6)); _iter->second->unArenaInfoId2 = GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (2 * 6)); - } + }*/ + m_achievementMgr.SaveToDB(); } // fast save function for item/money cheating preventing - save only inventory and money state @@ -15932,6 +16286,42 @@ void Player::SetFloatValueInDB(uint16 index, float value, uint64 guid) Player::SetUInt32ValueInDB(index, temp, guid); } +void Player::Customize(uint64 guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair) +{ + Tokens tokens; + if(!LoadValuesArrayFromDB(tokens, guid)) + return; + + uint32 unit_bytes0 = GetUInt32ValueFromArray(tokens, UNIT_FIELD_BYTES_0); + uint8 race = unit_bytes0 & 0xFF; + uint8 class_ = (unit_bytes0 >> 8) & 0xFF; + + PlayerInfo const* info = objmgr.GetPlayerInfo(race, class_); + if(!info) + return; + + unit_bytes0 &= ~(0xFF << 16); + unit_bytes0 |= (gender << 16); + SetUInt32ValueInArray(tokens, UNIT_FIELD_BYTES_0, unit_bytes0); + + SetUInt32ValueInArray(tokens, UNIT_FIELD_DISPLAYID, gender ? info->displayId_f : info->displayId_m); + SetUInt32ValueInArray(tokens, UNIT_FIELD_NATIVEDISPLAYID, gender ? info->displayId_f : info->displayId_m); + + SetUInt32ValueInArray(tokens, PLAYER_BYTES, (skin | (face << 8) | (hairStyle << 16) | (hairColor << 24))); + + uint32 player_bytes2 = GetUInt32ValueFromArray(tokens, PLAYER_BYTES_2); + player_bytes2 &= ~0xFF; + player_bytes2 |= facialHair; + SetUInt32ValueInArray(tokens, PLAYER_BYTES_2, player_bytes2); + + uint32 player_bytes3 = GetUInt32ValueFromArray(tokens, PLAYER_BYTES_3); + player_bytes3 &= ~0xFF; + player_bytes3 |= gender; + SetUInt32ValueInArray(tokens, PLAYER_BYTES_3, player_bytes3); + + SaveValuesArrayInDB(tokens, guid); +} + void Player::SendAttackSwingNotStanding() { WorldPacket data(SMSG_ATTACKSWING_NOTSTANDING, 0); @@ -15964,7 +16354,8 @@ void Player::SendAttackSwingBadFacingAttack() void Player::SendAutoRepeatCancel() { - WorldPacket data(SMSG_CANCEL_AUTO_REPEAT, 0); + WorldPacket data(SMSG_CANCEL_AUTO_REPEAT, GetPackGUID().size()); + data.append(GetPackGUID()); // may be it's target guid GetSession()->SendPacket( &data ); } @@ -16193,6 +16584,7 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent) { WorldPacket data(SMSG_PET_SPELLS, 8); data << uint64(0); + data << uint32(0); GetSession()->SendPacket(&data); if(GetGroup()) @@ -16324,65 +16716,79 @@ void Player::PetSpellInitialize() { Pet* pet = GetPet(); - if(pet) - { - uint8 addlist = 0; + if(!pet) + return; - sLog.outDebug("Pet Spells Groups"); + sLog.outDebug("Pet Spells Groups"); - CreatureInfo const *cinfo = pet->GetCreatureInfo(); + CharmInfo *charmInfo = pet->GetCharmInfo(); - if(pet->isControlled() && (pet->getPetType() == HUNTER_PET || cinfo && cinfo->type == CREATURE_TYPE_DEMON && getClass() == CLASS_WARLOCK)) - { - for(PetSpellMap::iterator itr = pet->m_spells.begin();itr != pet->m_spells.end();++itr) - { - if(itr->second->state == PETSPELL_REMOVED) - continue; - ++addlist; - } - } + WorldPacket data(SMSG_PET_SPELLS, 8+4+4+4+10*4); + data << uint64(pet->GetGUID()); + data << uint32(pet->GetCreatureInfo()->family); // creature family (required for pet talents) + data << uint32(0); + data << uint8(charmInfo->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0); - // first line + actionbar + spellcount + spells + last adds - WorldPacket data(SMSG_PET_SPELLS, 16+40+1+4*addlist+25); + // action bar loop + for(uint32 i = 0; i < 10; i++) + { + data << uint32(charmInfo->GetActionBarEntry(i)->Raw); + } - CharmInfo *charmInfo = pet->GetCharmInfo(); + size_t spellsCountPos = data.wpos(); - //16 - data << (uint64)pet->GetGUID() << uint32(0x00000000) << uint8(charmInfo->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0); + // spells count + uint8 addlist = 0; + data << uint8(addlist); // placeholder - for(uint32 i = 0; i < 10; i++) //40 + if(pet->isControlled() && ((pet->getPetType() == HUNTER_PET) || ((pet->GetCreatureInfo()->type == CREATURE_TYPE_DEMON) && (getClass() == CLASS_WARLOCK)))) + { + // spells loop + for (PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr) { - data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type); + if(itr->second->state == PETSPELL_REMOVED) + continue; + + data << uint16(itr->first); + data << uint16(itr->second->active); // pet spell active state isn't boolean + ++addlist; } + } - data << uint8(addlist); //1 + data.put<uint8>(spellsCountPos, addlist); - if(addlist && pet->isControlled()) - { - for (PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr) - { - if(itr->second->state == PETSPELL_REMOVED) - continue; + uint8 cooldownsCount = pet->m_CreatureSpellCooldowns.size() + pet->m_CreatureCategoryCooldowns.size(); + data << uint8(cooldownsCount); - data << uint16(itr->first); - data << uint16(itr->second->active); // pet spell active state isn't boolean - } - } + time_t curTime = time(NULL); - //data << uint8(0x01) << uint32(0x6010) << uint32(0x01) << uint32(0x05) << uint16(0x00); //15 - uint8 count = 3; //1+8+8+8=25 + for(CreatureSpellCooldowns::const_iterator itr = pet->m_CreatureSpellCooldowns.begin(); itr != pet->m_CreatureSpellCooldowns.end(); ++itr) + { + time_t cooldown = 0; - // if count = 0, then end of packet... - data << count; - // uint32 value is spell id... - // uint64 value is constant 0, unknown... - data << uint32(0x6010) << uint64(0); // if count = 1, 2 or 3 - //data << uint32(0x5fd1) << uint64(0); // if count = 2 - data << uint32(0x8e8c) << uint64(0); // if count = 3 - data << uint32(0x8e8b) << uint64(0); // if count = 3 + if(itr->second > curTime) + cooldown = (itr->second - curTime) * 1000; - GetSession()->SendPacket(&data); + data << uint16(itr->first); // spellid + data << uint16(0); // spell category? + data << uint32(itr->second); // cooldown + data << uint32(0); // category cooldown } + + for(CreatureSpellCooldowns::const_iterator itr = pet->m_CreatureCategoryCooldowns.begin(); itr != pet->m_CreatureCategoryCooldowns.end(); ++itr) + { + time_t cooldown = 0; + + if(itr->second > curTime) + cooldown = (itr->second - curTime) * 1000; + + data << uint16(itr->first); // spellid + data << uint16(0); // spell category? + data << uint32(0); // cooldown + data << uint32(itr->second); // category cooldown + } + + GetSession()->SendPacket(&data); } void Player::PossessSpellInitialize() @@ -16404,7 +16810,10 @@ void Player::PossessSpellInitialize() WorldPacket data(SMSG_PET_SPELLS, 16+40+1+4*addlist+25);// first line + actionbar + spellcount + spells + last adds //16 - data << (uint64)charm->GetGUID() << uint32(0x00000000) << uint8(0) << uint8(0) << uint16(0); + data << uint64(charm->GetGUID()); + data << uint32(0x00000000); + data << uint32(0); + data << uint8(0) << uint8(0) << uint16(0); for(uint32 i = 0; i < 10; i++) //40 { @@ -16413,11 +16822,8 @@ void Player::PossessSpellInitialize() data << uint8(addlist); //1 - uint8 count = 3; - data << count; - data << uint32(0x6010) << uint64(0); // if count = 1, 2 or 3 - data << uint32(0x8e8c) << uint64(0); // if count = 3 - data << uint32(0x8e8b) << uint64(0); // if count = 3 + uint8 count = 0; + data << uint8(count); // cooldowns count GetSession()->SendPacket(&data); } @@ -16454,13 +16860,13 @@ void Player::CharmSpellInitialize() WorldPacket data(SMSG_PET_SPELLS, 16+40+1+4*addlist+25);// first line + actionbar + spellcount + spells + last adds - data << (uint64)charm->GetGUID() << uint32(0x00000000); - + data << uint64(charm->GetGUID()); + data << uint32(0x00000000); + data << uint32(0); if(charm->GetTypeId() != TYPEID_PLAYER) data << uint8(charmInfo->GetReactState()) << uint8(charmInfo->GetCommandState()); else data << uint8(0) << uint8(0); - data << uint16(0); for(uint32 i = 0; i < 10; i++) //40 @@ -16483,51 +16889,12 @@ void Player::CharmSpellInitialize() } } - uint8 count = 3; - data << count; - data << uint32(0x6010) << uint64(0); // if count = 1, 2 or 3 - data << uint32(0x8e8c) << uint64(0); // if count = 3 - data << uint32(0x8e8b) << uint64(0); // if count = 3 + uint8 count = 0; + data << uint8(count); // cooldowns count GetSession()->SendPacket(&data); } -int32 Player::GetTotalFlatMods(uint32 spellId, SpellModOp op) -{ - SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); - if (!spellInfo) return 0; - int32 total = 0; - for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr) - { - SpellModifier *mod = *itr; - - if(!IsAffectedBySpellmod(spellInfo,mod)) - continue; - - if (mod->type == SPELLMOD_FLAT) - total += mod->value; - } - return total; -} - -int32 Player::GetTotalPctMods(uint32 spellId, SpellModOp op) -{ - SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); - if (!spellInfo) return 0; - int32 total = 0; - for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr) - { - SpellModifier *mod = *itr; - - if(!IsAffectedBySpellmod(spellInfo,mod)) - continue; - - if (mod->type == SPELLMOD_PCT) - total += mod->value; - } - return total; -} - bool Player::IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mod, Spell const* spell) { if (!mod || !spellInfo) @@ -16545,22 +16912,25 @@ bool Player::IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mo return false; } - return spellmgr.IsAffectedBySpell(spellInfo,mod->spellId,mod->effectId,mod->mask); + return spellmgr.IsAffectedByMod(spellInfo, mod); } void Player::AddSpellMod(SpellModifier* mod, bool apply) { uint16 Opcode= (mod->type == SPELLMOD_FLAT) ? SMSG_SET_FLAT_SPELL_MODIFIER : SMSG_SET_PCT_SPELL_MODIFIER; - for(int eff=0;eff<64;++eff) + for(int eff=0;eff<96;++eff) { - uint64 _mask = uint64(1) << eff; - if ( mod->mask & _mask) + uint64 _mask = 0; + uint64 _mask2= 0; + if (eff<64) _mask = uint64(1) << (eff- 0); + else _mask2= uint64(1) << (eff-64); + if ( mod->mask & _mask || mod->mask2 & _mask2) { int32 val = 0; for (SpellModList::iterator itr = m_spellMods[mod->op].begin(); itr != m_spellMods[mod->op].end(); ++itr) { - if ((*itr)->type == mod->type && (*itr)->mask & _mask) + if ((*itr)->type == mod->type && ((*itr)->mask & _mask || (*itr)->mask2 & _mask2)) val += (*itr)->value; } val += apply ? mod->value : -(mod->value); @@ -16659,6 +17029,27 @@ void Player::RemovePetitionsAndSigns(uint64 guid, uint32 type) CharacterDatabase.CommitTransaction(); } +void Player::LeaveAllArenaTeams(uint64 guid) +{ + QueryResult *result = CharacterDatabase.PQuery("SELECT arena_team_member.arenateamid FROM arena_team_member JOIN arena_team ON arena_team_member.arenateamid = arena_team.arenateamid WHERE guid='%u'", GUID_LOPART(guid)); + if(!result) + return; + + do + { + Field *fields = result->Fetch(); + uint32 at_id = fields[0].GetUInt32(); + if(at_id != 0) + { + ArenaTeam * at = objmgr.GetArenaTeamById(at_id); + if(at) + at->DelMember(guid); + } + } while (result->NextRow()); + + delete result; +} + void Player::SetRestBonus (float rest_bonus_new) { // Prevent resting on max level @@ -16708,7 +17099,7 @@ void Player::HandleStealthedUnitsDetection() // send data at target visibility change (adding to client) if((*i)!=this && (*i)->isType(TYPEMASK_UNIT)) { - SendAuraDurationsForTarget(*i); + SendAurasForTarget(*i); //if(((Unit*)(*i))->isAlive()) //should be always alive { if((*i)->GetTypeId()==TYPEID_UNIT) @@ -17702,7 +18093,7 @@ void Player::UpdateVisibilityOf(WorldObject* target) // send data at target visibility change (adding to client) if(target!=this && target->isType(TYPEMASK_UNIT)) { - SendAuraDurationsForTarget((Unit*)target); + SendAurasForTarget((Unit*)target); if(((Unit*)target)->isAlive()) { if(target->GetTypeId()==TYPEID_UNIT) @@ -17881,7 +18272,7 @@ void Player::SetGroup(Group *group, int8 subgroup) void Player::SendInitialPacketsBeforeAddToMap() { - WorldPacket data(SMSG_SET_REST_START, 4); + WorldPacket data(SMSG_SET_REST_START_OBSOLETE, 4); data << uint32(0); // unknown, may be rest state time or experience GetSession()->SendPacket(&data); @@ -17909,6 +18300,7 @@ void Player::SendInitialPacketsBeforeAddToMap() SendInitialActionButtons(); SendInitialReputations(); + m_achievementMgr.SendAllAchievementData(); UpdateZone(GetZoneId()); SendInitWorldStates(); @@ -17919,13 +18311,23 @@ void Player::SendInitialPacketsBeforeAddToMap() data << (float)0.01666667f; // game speed GetSession()->SendPacket( &data ); + data.Initialize(SMSG_TIME_SYNC_REQ, 4); // new 2.0.x, enable movement + data << uint32(0x00000000); // on blizz it increments periodically + GetSession()->SendPacket(&data); + // set fly flag if in fly form or taxi flight to prevent visually drop at ground in showup moment if(HasAuraType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED) || isInFlight()) AddUnitMovementFlag(MOVEMENTFLAG_FLYING2); + + m_mover = this; } void Player::SendInitialPacketsAfterAddToMap() { + WorldPacket data(SMSG_TIME_SYNC_REQ, 4); // new 2.0.x, enable movement + data << uint32(0x00000000); // on blizz it increments periodically + GetSession()->SendPacket(&data); + CastSpell(this, 836, true); // LOGINEFFECT // set some aura effects that send packet to player client after add player to map @@ -17956,6 +18358,7 @@ void Player::SendInitialPacketsAfterAddToMap() SendMessageToSet(&data,true); } + SendAurasForTarget(this); SendEnchantmentDurations(); // must be after add to map SendItemDurations(); // must be after add to map } @@ -17973,11 +18376,19 @@ void Player::SendUpdateToOutOfRangeGroupMembers() pet->ResetAuraUpdateMask(); } -void Player::SendTransferAborted(uint32 mapid, uint16 reason) +void Player::SendTransferAborted(uint32 mapid, uint8 reason, uint8 arg) { WorldPacket data(SMSG_TRANSFER_ABORTED, 4+2); data << uint32(mapid); - data << uint16(reason); // transfer abort reason + data << uint8(reason); // transfer abort reason + switch(reason) + { + case TRANSFER_ABORT_INSUF_EXPAN_LVL: + case TRANSFER_ABORT_DIFFICULTY: + case TRANSFER_ABORT_UNIQUE_MESSAGE: + data << uint8(arg); + break; + } GetSession()->SendPacket(&data); } @@ -18187,16 +18598,51 @@ void Player::learnSkillRewardedSpells() } } -void Player::SendAuraDurationsForTarget(Unit* target) +void Player::SendAurasForTarget(Unit *target) { - for(Unit::AuraMap::const_iterator itr = target->GetAuras().begin(); itr != target->GetAuras().end(); ++itr) + if(target->GetVisibleAuras()->empty()) // speedup things + return; + + WorldPacket data(SMSG_AURA_UPDATE_ALL); + data.append(target->GetPackGUID()); + + Unit::VisibleAuraMap const *visibleAuras = target->GetVisibleAuras(); + for(Unit::VisibleAuraMap::const_iterator itr = visibleAuras->begin(); itr != visibleAuras->end(); ++itr) { - Aura* aura = itr->second; - if(aura->GetAuraSlot() >= MAX_AURAS || aura->IsPassive() || aura->GetCasterGUID()!=GetGUID()) - continue; + for(uint32 j = 0; j < 3; ++j) + { + if(Aura *aura = target->GetAura(itr->second, j)) + { + data << uint8(aura->GetAuraSlot()); + data << uint32(aura->GetId()); - aura->SendAuraDurationForCaster(this); + if(aura->GetId()) + { + uint8 auraFlags = aura->GetAuraFlags(); + // flags + data << uint8(auraFlags); + // level + data << uint8(aura->GetAuraLevel()); + // charges + data << uint8(aura->m_procCharges >= 0 ? aura->m_procCharges : 0 ); + + if(!(auraFlags & AFLAG_NOT_CASTER)) + { + data << uint8(0); // packed GUID of someone (caster?) + } + + if(auraFlags & AFLAG_DURATION) // include aura duration + { + data << uint32(aura->GetAuraMaxDuration()); + data << uint32(aura->GetAuraDuration()); + } + } + break; + } + } } + + GetSession()->SendPacket(&data); } void Player::SetDailyQuestStatus( uint32 quest_id ) @@ -18258,15 +18704,15 @@ uint32 Player::GetMinLevelForBattleGroundQueueId(uint32 queue_id) if(queue_id < 1) return 0; - if(queue_id >=6) - queue_id = 6; + if(queue_id >=7) + queue_id = 7; return 10*(queue_id+1); } uint32 Player::GetMaxLevelForBattleGroundQueueId(uint32 queue_id) { - if(queue_id >=6) + if(queue_id >=7) return 255; // hardcoded max level return 10*(queue_id+2)-1; @@ -18278,8 +18724,8 @@ uint32 Player::GetBattleGroundQueueIdFromLevel() const uint32 level = getLevel(); if(level <= 19) return 0; - else if (level > 69) - return 6; + else if (level > 79) + return 7; else return level/10 - 1; // 20..29 -> 1, 30-39 -> 2, ... /* @@ -18425,9 +18871,8 @@ void Player::AutoUnequipOffhandIfNeed() if(!offItem) return; - Item *mainItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND ); - - if(!mainItem || mainItem->GetProto()->InventoryType != INVTYPE_2HWEAPON) + // need unequip for 2h-weapon without TitanGrip + if (!IsTwoHandUsed()) return; ItemPosCountVec off_dest; @@ -18493,6 +18938,23 @@ bool Player::HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item cons return false; } +bool Player::CanNoReagentCast(SpellEntry const* spellInfo) const +{ + // don't take reagents for spells with SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP + if (spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP && + HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION)) + return true; + + // Check no reagent use mask + uint64 noReagentMask_0_1 = GetUInt64Value(PLAYER_NO_REAGENT_COST_1); + uint32 noReagentMask_2 = GetUInt64Value(PLAYER_NO_REAGENT_COST_1+2); + if (spellInfo->SpellFamilyFlags & noReagentMask_0_1 || + spellInfo->SpellFamilyFlags2 & noReagentMask_2) + return true; + + return false; +} + void Player::RemoveItemDependentAurasAndCasts( Item * pItem ) { AuraMap& auras = GetAuras(); @@ -18538,7 +19000,7 @@ uint32 Player::GetResurrectionSpellId() for(AuraList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr) { // Soulstone Resurrection // prio: 3 (max, non death persistent) - if( prio < 2 && (*itr)->GetSpellProto()->SpellVisual == 99 && (*itr)->GetSpellProto()->SpellIconID == 92 ) + if( prio < 2 && (*itr)->GetSpellProto()->SpellVisual[0] == 99 && (*itr)->GetSpellProto()->SpellIconID == 92 ) { switch((*itr)->GetId()) { @@ -18570,6 +19032,26 @@ uint32 Player::GetResurrectionSpellId() return spell_id; } +// Used in triggers for check "Only to targets that grant experience or honor" req +bool Player::isHonorOrXPTarget(Unit* pVictim) +{ + uint32 v_level = pVictim->getLevel(); + uint32 k_grey = Trinity::XP::GetGrayLevel(getLevel()); + + // Victim level less gray level + if(v_level<=k_grey) + return false; + + if(pVictim->GetTypeId() == TYPEID_UNIT) + { + if (((Creature*)pVictim)->isTotem() || + ((Creature*)pVictim)->isPet() || + ((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL) + return false; + } + return true; +} + bool Player::RewardPlayerAndGroupAtKill(Unit* pVictim) { bool PvP = pVictim->isCharmedOwnedByPlayerOrPlayer(); @@ -18697,6 +19179,9 @@ uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const void Player::ResurectUsingRequestData() { + /// Teleport before resurrecting, otherwise the player might get attacked from creatures near his corpse + TeleportTo(m_resurrectMap, m_resurrectX, m_resurrectY, m_resurrectZ, GetOrientation()); + ResurrectPlayer(0.0f,false); if(GetMaxHealth() > m_resurrectHealth) @@ -18714,8 +19199,6 @@ void Player::ResurectUsingRequestData() SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY) ); SpawnCorpseBones(); - - TeleportTo(m_resurrectMap, m_resurrectX, m_resurrectY, m_resurrectZ, GetOrientation()); } void Player::SetClientControl(Unit* target, uint8 allowMove) @@ -18729,8 +19212,8 @@ void Player::SetClientControl(Unit* target, uint8 allowMove) void Player::UpdateZoneDependentAuras( uint32 newZone ) { // remove new continent flight forms - if( !isGameMaster() && - GetVirtualMapForMapAndZone(GetMapId(),newZone) != 530) + uint32 v_map = GetVirtualMapForMapAndZone(GetMapId(), newZone); + if( !isGameMaster() && v_map != 530 && v_map != 571) { RemoveSpellsCausingAura(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED); RemoveSpellsCausingAura(SPELL_AURA_FLY); @@ -18760,25 +19243,34 @@ void Player::UpdateAreaDependentAuras( uint32 newArea ) for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();) { // use m_zoneUpdateId for speed: UpdateArea called from UpdateZone or instead UpdateZone in both cases m_zoneUpdateId up-to-date - if(!IsSpellAllowedInLocation(iter->second->GetSpellProto(),GetMapId(),m_zoneUpdateId,newArea)) + if(GetSpellAllowedInLocationError(iter->second->GetSpellProto(),GetMapId(),m_zoneUpdateId,newArea)!=0) RemoveAura(iter); else ++iter; } - // unmount if enter in this subzone - if( newArea == 35) - RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); - // Dragonmaw Illusion - else if( newArea == 3759 || newArea == 3966 || newArea == 3939 ) + // some auras applied at subzone enter + switch(newArea) { - if( GetDummyAura(40214) ) - { - if( !HasAura(40216,0) ) - CastSpell(this,40216,true); - if( !HasAura(42016,0) ) - CastSpell(this,42016,true); - } + // Dragonmaw Illusion + case 3759: // Netherwing Ledge + case 3939: // Dragonmaw Fortress + case 3966: // Dragonmaw Base Camp + if( GetDummyAura(40214) ) + { + if( !HasAura(40216,0) ) + CastSpell(this,40216,true); + if( !HasAura(42016,0) ) + CastSpell(this,42016,true); + } + break; + // Dominion Over Acherus + case 4281: // Acherus: The Ebon Hold + case 4342: // Acherus: The Ebon Hold + if( HasSpell(51721) ) + if( !HasAura(51721,0) ) + CastSpell(this,51721,true); + break; } } @@ -18955,8 +19447,8 @@ bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const void Player::HandleFallDamage(MovementInfo& movementInfo) { - if(movementInfo.fallTime < 1500) - return; + //if(movementInfo.fallTime < 1500) + // return; // calculate total z distance of the fall float z_diff = m_lastFallZ - movementInfo.z; @@ -19262,6 +19754,157 @@ bool Player::isAllowUseBattleGroundObject() ); } +uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 newfacialhair) +{ + uint32 level = getLevel(); + + if(level > GT_MAX_LEVEL) + level = GT_MAX_LEVEL; // max level in this dbc + + uint8 hairstyle = GetByteValue(PLAYER_BYTES, 2); + uint8 haircolor = GetByteValue(PLAYER_BYTES, 3); + uint8 facialhair = GetByteValue(PLAYER_BYTES_2, 0); + + if((hairstyle == newhairstyle) && (haircolor == newhaircolor) && (facialhair == newfacialhair)) + return 0; + + GtBarberShopCostBaseEntry const *bsc = sGtBarberShopCostBaseStore.LookupEntry(level - 1); + + if(!bsc) // shouldn't happen + return 0xFFFFFFFF; + + float cost = 0; + + if(hairstyle != newhairstyle) + cost += bsc->cost; // full price + + if((haircolor != newhaircolor) && (hairstyle == newhairstyle)) + cost += bsc->cost * 0.5f; // +1/2 of price + + if(facialhair != newfacialhair) + cost += bsc->cost * 0.75f; // +3/4 of price + + return uint32(cost); +} + +void Player::InitGlyphsForLevel() +{ + for(uint32 i = 0; i < sGlyphSlotStore.GetNumRows(); ++i) + if(GlyphSlotEntry const * gs = sGlyphSlotStore.LookupEntry(i)) + if(gs->Order) + SetGlyphSlot(gs->Order - 1, gs->Id); + + uint32 level = getLevel(); + uint32 value = 0; + + // 0x3F = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 for 80 level + if(level >= 15) + value |= (0x01 | 0x02); + if(level >= 30) + value |= 0x04; + if(level >= 50) + value |= 0x08; + if(level >= 70) + value |= 0x10; + if(level >= 80) + value |= 0x20; + + SetUInt32Value(PLAYER_GLYPHS_ENABLED, value); +} + +void Player::EnterVehicle(Vehicle *vehicle) +{ + VehicleEntry const *ve = sVehicleStore.LookupEntry(vehicle->GetVehicleId()); + if(!ve) + return; + + VehicleSeatEntry const *veSeat = sVehicleSeatStore.LookupEntry(ve->m_seatID[0]); + if(!veSeat) + return; + + vehicle->SetCharmerGUID(GetGUID()); + vehicle->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); + vehicle->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN5); + vehicle->setFaction(getFaction()); + + SetCharm(vehicle); // charm + SetFarSight(vehicle->GetGUID()); // set view + + SetClientControl(vehicle, 1); // redirect controls to vehicle + + WorldPacket data(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, 0); + GetSession()->SendPacket(&data); + + data.Initialize(MSG_MOVE_TELEPORT_ACK, 30); + data.append(GetPackGUID()); + data << uint32(0); // counter? + data << uint32(MOVEMENTFLAG_ONTRANSPORT); // transport + data << uint16(0); // special flags + data << uint32(getMSTime()); // time + data << vehicle->GetPositionX(); // x + data << vehicle->GetPositionY(); // y + data << vehicle->GetPositionZ(); // z + data << vehicle->GetOrientation(); // o + // transport part, TODO: load/calculate seat offsets + data << uint64(vehicle->GetGUID()); // transport guid + data << float(veSeat->m_attachmentOffsetX); // transport offsetX + data << float(veSeat->m_attachmentOffsetY); // transport offsetY + data << float(veSeat->m_attachmentOffsetZ); // transport offsetZ + data << float(0); // transport orientation + data << uint32(getMSTime()); // transport time + data << uint8(0); // seat + // end of transport part + data << uint32(0); // fall time + GetSession()->SendPacket(&data); + + data.Initialize(SMSG_PET_SPELLS, 8+4+4+4+4*10+1+1); + data << uint64(vehicle->GetGUID()); + data << uint32(0x00000000); + data << uint32(0x00000000); + data << uint32(0x00000101); + + for(uint32 i = 0; i < 10; ++i) + data << uint16(0) << uint8(0) << uint8(i+8); + + data << uint8(0); + data << uint8(0); + GetSession()->SendPacket(&data); +} + +void Player::ExitVehicle(Vehicle *vehicle) +{ + vehicle->SetCharmerGUID(0); + vehicle->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); + vehicle->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN5); + vehicle->setFaction((GetTeam() == ALLIANCE) ? vehicle->GetCreatureInfo()->faction_A : vehicle->GetCreatureInfo()->faction_H); + + SetCharm(NULL); + SetFarSight(NULL); + + SetClientControl(vehicle, 0); + + WorldPacket data(MSG_MOVE_TELEPORT_ACK, 30); + data.append(GetPackGUID()); + data << uint32(0); // counter? + data << uint32(MOVEMENTFLAG_FLY_UNK1); // fly unk + data << uint16(0x40); // special flags + data << uint32(getMSTime()); // time + data << vehicle->GetPositionX(); // x + data << vehicle->GetPositionY(); // y + data << vehicle->GetPositionZ(); // z + data << vehicle->GetOrientation(); // o + data << uint32(0); // fall time + GetSession()->SendPacket(&data); + + data.Initialize(SMSG_PET_SPELLS, 8+4); + data << uint64(0); + data << uint32(0); + GetSession()->SendPacket(&data); + + // only for flyable vehicles? + CastSpell(this, 45472, true); // Parachute +} + bool Player::HasTitle(uint32 bitIndex) { if (bitIndex > 128) @@ -19279,7 +19922,6 @@ void Player::SetTitle(CharTitlesEntry const* title) SetFlag(PLAYER__FIELD_KNOWN_TITLES+fieldIndexOffset, flag); } - /*-----------------------TRINITY--------------------------*/ bool Player::isTotalImmunity() { @@ -19336,3 +19978,52 @@ void Player::UpdateCharmedAI() Attack(target, true); } } + +void Player::ConvertRune(uint8 index, uint8 newType) +{ + SetCurrentRune(index, newType); + + WorldPacket data(SMSG_CONVERT_RUNE, 2); + data << uint8(index); + data << uint8(newType); + GetSession()->SendPacket(&data); +} + +void Player::ResyncRunes(uint8 count) +{ + WorldPacket data(SMSG_RESYNC_RUNES, count * 2); + for(uint32 i = 0; i < count; ++i) + { + data << uint8(GetCurrentRune(i)); // rune type + data << uint8(255 - (GetRuneCooldown(i) * 51)); // passed cooldown time (0-255) + } + GetSession()->SendPacket(&data); +} + +void Player::AddRunePower(uint8 index) +{ + WorldPacket data(SMSG_ADD_RUNE_POWER, 4); + data << uint32(1 << index); // mask (0x00-0x3F probably) + GetSession()->SendPacket(&data); +} + +void Player::InitRunes() +{ + if(getClass() != CLASS_DEATH_KNIGHT) + return; + + m_runes = new Runes; + + m_runes->runeState = 0; + + for(uint32 i = 0; i < MAX_RUNES; ++i) + { + SetBaseRune(i, i / 2); // init base types + SetCurrentRune(i, i / 2); // init current types + SetRuneCooldown(i, 0); // reset cooldowns + m_runes->SetRuneState(i); + } + + for(uint32 i = 0; i < NUM_RUNE_TYPES; ++i) + SetFloatValue(PLAYER_RUNE_REGEN_1 + i, 0.1f); +} |