diff options
Diffstat (limited to 'src/game/Player.cpp')
-rw-r--r-- | src/game/Player.cpp | 2048 |
1 files changed, 1155 insertions, 893 deletions
diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 17203faadde..8a3a4bffd93 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -316,10 +316,6 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa m_nextSave = sWorld.getConfig(CONFIG_INTERVAL_SAVE); - // randomize first save time in range [CONFIG_INTERVAL_SAVE] around [CONFIG_INTERVAL_SAVE] - // this must help in case next save after mass player load after server startup - m_nextSave = urand(m_nextSave/2,m_nextSave*3/2); - clearResurrectRequestData(); memset(m_items, 0, sizeof(Item*)*PLAYER_SLOTS_COUNT); @@ -341,6 +337,11 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa mSemaphoreTeleport_Near = false; mSemaphoreTeleport_Far = false; + m_DelayedOperations = 0; + m_bCanDelayTeleport = false; + m_bHasDelayedTeleport = false; + m_teleport_options = 0; + pTrader = 0; ClearTrade(); @@ -471,6 +472,8 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa m_lastFallZ = 0; m_ControlledByPlayer = true; + + sWorld.IncreasePlayerCount(); } Player::~Player () @@ -478,6 +481,10 @@ Player::~Player () // it must be unloaded already in PlayerLogout and accessed only for loggined player //m_social = NULL; + // Player may still set map - remove it to correctly unload instances + if (FindMap()) + ResetMap(); + // Note: buy back item already deleted from DB when player was saved for(uint8 i = 0; i < PLAYER_SLOTS_COUNT; ++i) { @@ -503,6 +510,8 @@ Player::~Player () delete m_declinedname; delete m_runes; + + sWorld.DecreasePlayerCount(); } void Player::CleanupsBeforeDelete() @@ -539,10 +548,6 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 for (uint8 i = 0; i < PLAYER_SLOTS_COUNT; i++) m_items[i] = NULL; - m_race = race; - m_class = class_; - - SetMapId(info->mapId); Relocate(info->positionX,info->positionY,info->positionZ); ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(class_); @@ -552,32 +557,19 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 return false; } + SetMap(MapManager::Instance().CreateMap(info->mapId, this, 0)); + uint8 powertype = cEntry->powerType; SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE); SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f); - switch(gender) - { - case GENDER_FEMALE: - SetDisplayId(info->displayId_f ); - SetNativeDisplayId(info->displayId_f ); - break; - case GENDER_MALE: - SetDisplayId(info->displayId_m ); - SetNativeDisplayId(info->displayId_m ); - break; - default: - sLog.outError("Invalid gender %u for player",gender); - return false; - break; - } - - setFactionForRace(m_race); + setFactionForRace(race); uint32 RaceClassGender = ( race ) | ( class_ << 8 ) | ( gender << 16 ); SetUInt32Value(UNIT_FIELD_BYTES_0, ( RaceClassGender | ( powertype << 24 ) ) ); + InitDisplayIds(); 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); @@ -676,8 +668,8 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 // Played time m_Last_tick = time(NULL); - m_Played_time[0] = 0; - m_Played_time[1] = 0; + m_Played_time[PLAYED_TIME_TOTAL] = 0; + m_Played_time[PLAYED_TIME_LEVEL] = 0; // base stats and related field values InitStatsForLevel(); @@ -707,21 +699,8 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 learnDefaultSpells(); // original action bar - std::list<uint16>::const_iterator action_itr[4]; - for(uint8 i=0; i<4; i++) - action_itr[i] = info->action[i].begin(); - - for (; action_itr[0]!=info->action[0].end() && action_itr[1]!=info->action[1].end();) - { - uint16 taction[4]; - for(uint8 i=0; i<4 ;i++) - taction[i] = (*action_itr[i]); - - addActionButton((uint8)taction[0], taction[1], (uint8)taction[2], (uint8)taction[3]); - - for(uint8 i=0; i<4 ;i++) - ++action_itr[i]; - } + for (PlayerCreateInfoActions::const_iterator action_itr = info->action.begin(); action_itr != info->action.end(); ++action_itr) + addActionButton(action_itr->button,action_itr->action,action_itr->type); // original items CharStartOutfitEntry const* oEntry = NULL; @@ -746,7 +725,6 @@ 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; @@ -758,22 +736,23 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 continue; } - // max stack by default (mostly 1), 1 for infinity stackable - uint32 count = iProto->Stackable > 0 ? uint32(iProto->Stackable) : 1; + // BuyCount by default + uint32 count = iProto->BuyCount; + // special amount for foor/drink if(iProto->Class==ITEM_CLASS_CONSUMABLE && iProto->SubClass==ITEM_SUBCLASS_FOOD) { switch(iProto->Spells[0].SpellCategory) { case 11: // food - if(iProto->Stackable > 4) - count = 4; + count = getClass()==CLASS_DEATH_KNIGHT ? 10 : 4; break; case 59: // drink - if(iProto->Stackable > 2) - count = 2; + count = 2; break; } + if(iProto->Stackable < count) + count = iProto->Stackable; } StoreNewItemInBestSlots(item_id, count); @@ -1138,7 +1117,10 @@ void Player::Update( uint32 p_time ) // Having this would prevent more aura charges to be dropped, so let's crash assert (!m_spellModTakingSpell); + //used to implement delayed far teleports + SetCanDelayTeleport(true); Unit::Update( p_time ); + SetCanDelayTeleport(false); time_t now = time (NULL); @@ -1346,8 +1328,8 @@ void Player::Update( uint32 p_time ) { if (p_time >= m_DetectInvTimer) { - m_DetectInvTimer = 3000; HandleStealthedUnitsDetection(); + m_DetectInvTimer = 3000; } else m_DetectInvTimer -= p_time; @@ -1357,8 +1339,8 @@ void Player::Update( uint32 p_time ) if (now > m_Last_tick) { uint32 elapsed = uint32(now - m_Last_tick); - m_Played_time[0] += elapsed; // Total played time - m_Played_time[1] += elapsed; // Level played time + m_Played_time[PLAYED_TIME_TOTAL] += elapsed; // Total played time + m_Played_time[PLAYED_TIME_LEVEL] += elapsed; // Level played time m_Last_tick = now; } @@ -1394,8 +1376,12 @@ void Player::Update( uint32 p_time ) //if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE) && (GetCharmGUID() && (pet->GetGUID() != GetCharmGUID()))) { RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true); - return; } + + //we should execute delayed teleports only for alive(!) players + //because we don't want player's ghost teleported from graveyard + if(IsHasDelayedTeleport() && isAlive()) + TeleportTo(m_teleport_dest, m_teleport_options); } void Player::setDeathState(DeathState s) @@ -1452,53 +1438,67 @@ void Player::setDeathState(DeathState s) } } -void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) +bool Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) { + // 0 1 2 3 4 5 6 7 + // "SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.playerBytes, characters.playerBytes2, characters.level, " + // 8 9 10 11 12 13 14 + // "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, " + // 15 16 17 18 19 20 + // "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.data, character_declinedname.genitive " + Field *fields = result->Fetch(); - *p_data << uint64(GetGUID()); - *p_data << m_name; + uint32 guid = fields[0].GetUInt32(); + uint8 pRace = fields[2].GetUInt8(); + uint8 pClass = fields[3].GetUInt8(); - *p_data << uint8(getRace()); - uint8 pClass = getClass(); - *p_data << uint8(pClass); - *p_data << uint8(getGender()); + PlayerInfo const *info = objmgr.GetPlayerInfo(pRace, pClass); + if(!info) + { + sLog.outError("Player %u has incorrect race/class pair. Don't build enum.", guid); + return false; + } + + *p_data << uint64(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER)); + *p_data << fields[1].GetString(); // name + *p_data << uint8(pRace); // race + *p_data << uint8(pClass); // class + *p_data << uint8(fields[4].GetUInt8()); // gender - uint32 bytes = GetUInt32Value(PLAYER_BYTES); - *p_data << uint8(bytes); - *p_data << uint8(bytes >> 8); - *p_data << uint8(bytes >> 16); - *p_data << uint8(bytes >> 24); + uint32 playerBytes = fields[5].GetUInt32(); + *p_data << uint8(playerBytes); // skin + *p_data << uint8(playerBytes >> 8); // face + *p_data << uint8(playerBytes >> 16); // hair style + *p_data << uint8(playerBytes >> 24); // hair color - bytes = GetUInt32Value(PLAYER_BYTES_2); - *p_data << uint8(bytes); + uint32 playerBytes2 = fields[6].GetUInt32(); + *p_data << uint8(playerBytes2 & 0xFF); // facial hair - *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 = fields[10].GetUInt32(); - sLog.outDebug("Player::BuildEnumData: map:%u, x:%f, y:%f, z:%f zone:%u", GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), zoneId); - *p_data << uint32(zoneId); - *p_data << uint32(GetMapId()); + *p_data << uint8(fields[7].GetUInt8()); // level + *p_data << uint32(fields[8].GetUInt32()); // zone + *p_data << uint32(fields[9].GetUInt32()); // map - *p_data << GetPositionX(); - *p_data << GetPositionY(); - *p_data << GetPositionZ(); + *p_data << fields[10].GetFloat(); // x + *p_data << fields[11].GetFloat(); // y + *p_data << fields[12].GetFloat(); // z - // guild id - *p_data << uint32(fields[14].GetUInt32()); + *p_data << uint32(fields[13].GetUInt32()); // guild id uint32 char_flags = 0; - if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM)) + uint32 playerFlags = fields[14].GetUInt32(); + uint32 atLoginFlags = fields[15].GetUInt32(); + if(playerFlags & PLAYER_FLAGS_HIDE_HELM) char_flags |= CHARACTER_FLAG_HIDE_HELM; - if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK)) + if(playerFlags & PLAYER_FLAGS_HIDE_CLOAK) char_flags |= CHARACTER_FLAG_HIDE_CLOAK; - if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) + if(playerFlags & PLAYER_FLAGS_GHOST) char_flags |= CHARACTER_FLAG_GHOST; - if(HasAtLoginFlag(AT_LOGIN_RENAME)) + if(atLoginFlags & AT_LOGIN_RENAME) char_flags |= CHARACTER_FLAG_RENAME; if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED)) { - if(!fields[15].GetCppString().empty()) + if(!fields[20].GetCppString().empty()) char_flags |= CHARACTER_FLAG_DECLINED; } else @@ -1506,7 +1506,7 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) *p_data << uint32(char_flags); // character flags // character customize (flags?) - *p_data << uint32(HasAtLoginFlag(AT_LOGIN_CUSTOMIZE) ? 1 : 0); + *p_data << uint32(atLoginFlags & AT_LOGIN_CUSTOMIZE ? 1 : 0); *p_data << uint8(1); // unknown // Pets info @@ -1515,15 +1515,15 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) uint32 petLevel = 0; uint32 petFamily = 0; - // show pet at selection character in character list only for non-ghost character - if (result && isAlive() && (pClass == CLASS_WARLOCK || pClass == CLASS_HUNTER || pClass == CLASS_DEATH_KNIGHT)) + // show pet at selection character in character list only for non-ghost character + if (result && !(playerFlags & PLAYER_FLAGS_GHOST) && (pClass == CLASS_WARLOCK || pClass == CLASS_HUNTER || pClass == CLASS_DEATH_KNIGHT)) { - uint32 entry = fields[11].GetUInt32(); + uint32 entry = fields[16].GetUInt32(); CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(entry); if(cInfo) { - petDisplayId = fields[12].GetUInt32(); - petLevel = fields[13].GetUInt32(); + petDisplayId = fields[17].GetUInt32(); + petLevel = fields[18].GetUInt32(); petFamily = cInfo->family; } } @@ -1533,36 +1533,45 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) *p_data << uint32(petFamily); } + // TODO: do not access data field here + Tokens data = StrSplit(fields[19].GetCppString(), " "); + for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; slot++) { uint32 visualbase = PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2); - uint32 item_id = GetUInt32Value(visualbase); + uint32 item_id = GetUInt32ValueFromArray(data, visualbase); const ItemPrototype * proto = objmgr.GetItemPrototype(item_id); + if(!proto) + { + *p_data << uint32(0); + *p_data << uint8(0); + *p_data << uint32(0); + continue; + } + SpellItemEnchantmentEntry const *enchant = NULL; + uint32 enchants = GetUInt32ValueFromArray(data, visualbase + 1); for(uint8 enchantSlot = PERM_ENCHANTMENT_SLOT; enchantSlot <= TEMP_ENCHANTMENT_SLOT; ++enchantSlot) { - uint32 enchantId = GetUInt16Value(visualbase + 1, enchantSlot); + // values stored in 2 uint16 + uint32 enchantId = 0x0000FFFF & (enchants >> enchantSlot*16); + if(!enchantId) + continue; + if(enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId)) break; } - if (proto != NULL) - { - *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(proto->DisplayInfoID); + *p_data << uint8(proto->InventoryType); + *p_data << uint32(enchant ? enchant->aura_id : 0); } *p_data << uint32(0); // first bag display id *p_data << uint8(0); // first bag inventory type *p_data << uint32(0); // enchant? + + return true; } bool Player::ToggleAFK() @@ -1611,6 +1620,27 @@ void Player::SendTeleportAckMsg() GetSession()->SendPacket(&data); } +void Player::TeleportOutOfMap(Map *oldMap) +{ + while(IsBeingTeleportedFar()) + GetSession()->HandleMoveWorldportAckOpcode(); + + if(FindMap() != oldMap) + return; + + TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, GetOrientation()); + + while(IsBeingTeleportedFar()) + GetSession()->HandleMoveWorldportAckOpcode(); + + if(FindMap() == oldMap) + { + sLog.outCrash("Cannot teleport player out of map!"); + ResetMap(); + assert(false); + } +} + bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options) { if(!MapManager::IsValidMapCoord(mapid, x, y, z, orientation)) @@ -1683,6 +1713,21 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati if ((GetMapId() == mapid) && (!m_transport)) { + //lets reset far teleport flag if it wasn't reset during chained teleports + SetSemaphoreTeleportFar(false); + //setup delayed teleport flag + SetDelayedTeleportFlag(IsCanDelayTeleport()); + //if teleport spell is casted in Unit::Update() func + //then we need to delay it until update process will be finished + if(IsHasDelayedTeleport()) + { + SetSemaphoreTeleportNear(true); + //lets save teleport destination for player + m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); + m_teleport_options = options; + return true; + } + if (!(options & TELE_TO_NOT_UNSUMMON_PET)) { //same map, only remove pet if out of range for new position @@ -1697,7 +1742,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); SetFallInformation(0, z); - // code for finish transfer called in WorldSession::HandleMovementOpcodes() + // code for finish transfer called in WorldSession::HandleMovementOpcodes() // at client packet MSG_MOVE_TELEPORT_ACK SetSemaphoreTeleportNear(true); // near teleport, triggering send MSG_MOVE_TELEPORT_ACK from client at landing @@ -1712,6 +1757,10 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati } else { + if(getClass() == CLASS_DEATH_KNIGHT && GetMapId() == 609 && !isGameMaster() + && !IsActiveQuest(13165)) + return false; + // far teleport to another map Map* oldmap = IsInWorld() ? GetMap() : NULL; // check if we can enter before stopping combat / removing pet / totems / interrupting spells @@ -1726,6 +1775,21 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati Map *map = MapManager::Instance().FindMap(mapid); if (!map || map->CanEnter(this)) { + //lets reset near teleport flag if it wasn't reset during chained teleports + SetSemaphoreTeleportNear(false); + //setup delayed teleport flag + SetDelayedTeleportFlag(IsCanDelayTeleport()); + //if teleport spell is casted in Unit::Update() func + //then we need to delay it until update process will be finished + if(IsHasDelayedTeleport()) + { + SetSemaphoreTeleportFar(true); + //lets save teleport destination for player + m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); + m_teleport_options = options; + return true; + } + SetSelection(0); CombatStop(); @@ -1755,6 +1819,9 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati if(IsNonMeleeSpellCasted(true)) InterruptNonMeleeSpells(true); + //remove auras before removing from map... + RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP | AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING); + if(!GetSession()->PlayerLogout()) { // send transfer packets @@ -1801,8 +1868,6 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati // if the player is saved before worldportack (at logout for example) // this will be used instead of the current location in SaveToDB - RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP | AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING); - // move packet sent by client always after far teleport // code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet SetSemaphoreTeleportFar(true); @@ -1813,6 +1878,53 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati return true; } +void Player::ProcessDelayedOperations() +{ + if(m_DelayedOperations == 0) + return; + + if(m_DelayedOperations & DELAYED_RESURRECT_PLAYER) + { + ResurrectPlayer(0.0f, false); + + if(GetMaxHealth() > m_resurrectHealth) + SetHealth( m_resurrectHealth ); + else + SetHealth( GetMaxHealth() ); + + if(GetMaxPower(POWER_MANA) > m_resurrectMana) + SetPower(POWER_MANA, m_resurrectMana ); + else + SetPower(POWER_MANA, GetMaxPower(POWER_MANA) ); + + SetPower(POWER_RAGE, 0 ); + SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY) ); + + SpawnCorpseBones(); + } + + if(m_DelayedOperations & DELAYED_SAVE_PLAYER) + { + SaveToDB(); + } + + if(m_DelayedOperations & DELAYED_SPELL_CAST_DESERTER) + { + CastSpell(this, 26013, true); // Deserter + } + + //we have executed ALL delayed ops, so clear the flag + m_DelayedOperations = 0; +} + +void Player::ScheduleDelayedOperation(uint32 operation) +{ + if(operation >= DELAYED_END) + return; + + m_DelayedOperations |= operation; +} + void Player::AddToWorld() { ///- Do not add/remove the player from the object storage @@ -1863,33 +1975,6 @@ void Player::RemoveFromWorld() } } -void Player::RewardRage( uint32 damage, uint32 weaponSpeedHitFactor, bool attacker ) -{ - float addRage; - - float rageconversion = ((0.0091107836 * getLevel()*getLevel())+3.225598133*getLevel())+4.2652911; - - if(attacker) - { - addRage = ((damage/rageconversion*7.5 + weaponSpeedHitFactor)/2); - - // talent who gave more rage on attack - addRage *= 1.0f + GetTotalAuraModifier(SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT) / 100.0f; - } - else - { - addRage = damage/rageconversion*2.5; - - // Berserker Rage effect - if(HasAura(18499)) - addRage *= 1.3; - } - - addRage *= sWorld.getRate(RATE_POWER_RAGE_INCOME); - - ModifyPower(POWER_RAGE, uint32(addRage*10)); -} - void Player::RegenerateAll() { if (m_regenTimer != 0) @@ -2372,9 +2457,12 @@ void Player::GiveLevel(uint32 level) SetUInt32Value(PLAYER_NEXT_LEVEL_XP, objmgr.GetXPForLevel(level)); //update level, max level of skills - if(getLevel()!= level) - m_Played_time[1] = 0; // Level Played Time reset + m_Played_time[PLAYED_TIME_LEVEL] = 0; // Level Played Time reset + + _ApplyAllLevelScaleItemMods(false); + SetLevel(level); + UpdateSkillsForLevel (); // save base values (bonuses already included in stored stats @@ -2402,6 +2490,8 @@ void Player::GiveLevel(uint32 level) SetPower(POWER_FOCUS, 0); SetPower(POWER_HAPPINESS, 0); + _ApplyAllLevelScaleItemMods(true); + // update level to hunter/summon pet if (Pet* pet = GetPet()) pet->SynchronizeLevelWithOwner(); @@ -2603,7 +2693,7 @@ void Player::InitStatsForLevel(bool reapplyMods) void Player::SendInitialSpells() { time_t curTime = time(NULL); - time_t infTime = curTime + MONTH/2; + time_t infTime = curTime + infinityCooldownDelayCheck; uint16 spellCount = 0; @@ -2637,18 +2727,21 @@ void Player::SendInitialSpells() if(!sEntry) continue; - // not send infinity cooldown - if(itr->second.end > infTime) - continue; - data << uint32(itr->first); - time_t cooldown = 0; - if(itr->second.end > curTime) - cooldown = (itr->second.end-curTime)*IN_MILISECONDS; - data << uint16(itr->second.itemid); // cast item id data << uint16(sEntry->Category); // spell category + + // send infinity cooldown in special format + if(itr->second.end >= infTime) + { + data << uint32(1); // cooldown + data << uint32(0x80000000); // category cooldown + continue; + } + + time_t cooldown = itr->second.end > curTime ? (itr->second.end-curTime)*IN_MILISECONDS : 0; + if(sEntry->Category) // may be wrong, but anyway better than nothing... { data << uint32(0); // cooldown @@ -2897,7 +2990,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen if(!rankSpellId || rankSpellId==spell_id) continue; - removeSpell(rankSpellId); + removeSpell(rankSpellId,false,false); } } } @@ -3125,7 +3218,6 @@ bool Player::IsNeedCastPassiveSpellAtLearn(SpellEntry const* spellInfo) const switch(spellInfo->Id) { // some spells not have stance data expected cast at form change or present - case 5420: need_cast = (m_form == FORM_TREE); break; case 5419: need_cast = (m_form == FORM_TRAVEL); break; case 7376: need_cast = (m_form == FORM_DEFENSIVESTANCE); break; case 7381: need_cast = (m_form == FORM_BERSERKERSTANCE); break; @@ -3173,7 +3265,7 @@ void Player::learnSpell(uint32 spell_id, bool dependent) GetSession()->SendPacket(&data); } -void Player::removeSpell(uint32 spell_id, bool disabled, bool update_action_bar_for_low_rank) +void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank) { PlayerSpellMap::iterator itr = m_spells.find(spell_id); if (itr == m_spells.end()) @@ -3193,7 +3285,7 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool update_action_bar_ SpellsRequiringSpellMap const& reqMap = spellmgr.GetSpellsRequiringSpell(); SpellsRequiringSpellMap::const_iterator itr2 = reqMap.find(spell_id); for (uint32 i=reqMap.count(spell_id);i>0;i--,itr2++) - removeSpell(itr2->second,disabled); + removeSpell(itr2->second,disabled,false); // re-search, it can be corrupted in prev loop itr = m_spells.find(spell_id); @@ -3223,8 +3315,9 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool update_action_bar_ RemoveAurasDueToSpell(spell_id); // remove pet auras - if(PetAura const* petSpell = spellmgr.GetPetAura(spell_id)) - RemovePetAura(petSpell); + for(int i = 0; i < MAX_SPELL_EFFECTS; ++i) + if(PetAura const* petSpell = spellmgr.GetPetAura(spell_id, i)) + RemovePetAura(petSpell); // free talent points uint32 talentCosts = GetTalentSpellCost(spell_id); @@ -3321,15 +3414,17 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool update_action_bar_ { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id); - // if talent then lesser rank also talent and need learn + // if talent then lesser rank also talent and need learn if(talentCosts) { - //learnSpell (prev_id,false); + // I cannot see why mangos has these lines. + //if(learn_low_rank) + // learnSpell (prev_id,false); } - // if ranked non-stackable spell: need activate lesser rank and update dendence state + // if ranked non-stackable spell: need activate lesser rank and update dendence state else if(cur_active && !SpellMgr::canStackSpellRanks(spellInfo) && spellmgr.GetSpellRank(spellInfo->Id) != 0) { - // need manually update dependence state (learn spell ignore like attempts) + // need manually update dependence state (learn spell ignore like attempts) PlayerSpellMap::iterator prev_itr = m_spells.find(prev_id); if (prev_itr != m_spells.end()) { @@ -3341,19 +3436,16 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool update_action_bar_ } // now re-learn if need re-activate - if(cur_active && !prev_itr->second->active) + if(cur_active && !prev_itr->second->active && learn_low_rank) { if(addSpell(prev_id,true,false,prev_itr->second->dependent,prev_itr->second->disabled)) { - if(update_action_bar_for_low_rank) - { - // downgrade spell ranks in spellbook and action bar - WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4); - data << uint32(spell_id); - data << uint32(prev_id); - GetSession()->SendPacket( &data ); - prev_activate = true; - } + // downgrade spell ranks in spellbook and action bar + WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4); + data << uint32(spell_id); + data << uint32(prev_id); + GetSession()->SendPacket( &data ); + prev_activate = true; } } } @@ -3374,14 +3466,10 @@ void Player::RemoveSpellCooldown( uint32 spell_id, bool update /* = false */ ) m_spellCooldowns.erase(spell_id); if(update) - { - WorldPacket data(SMSG_CLEAR_COOLDOWN, 4+8); - data << uint32(spell_id); - data << uint64(GetGUID()); - SendDirectMessage(&data); - } + SendClearCooldown(spell_id, this); } +// I am not sure which one is more efficient void Player::RemoveCategoryCooldown( uint32 cat ) { SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(cat); @@ -3390,6 +3478,22 @@ void Player::RemoveCategoryCooldown( uint32 cat ) RemoveSpellCooldown(*i_scset, true); } +void Player::RemoveSpellCategoryCooldown(uint32 cat, bool update /* = false */) +{ + SpellCategoryStore::const_iterator ct = sSpellCategoryStore.find(cat); + if (ct == sSpellCategoryStore.end()) + return; + + const SpellCategorySet& ct_set = ct->second; + for (SpellCooldowns::const_iterator i = m_spellCooldowns.begin(); i != m_spellCooldowns.end();) + { + if (ct_set.find(i->first) != ct_set.end()) + RemoveSpellCooldown((i++)->first, update); + else + ++i; + } +} + void Player::RemoveArenaSpellCooldowns() { // remove cooldowns on spells that has < 15 min CD @@ -3405,13 +3509,8 @@ void Player::RemoveArenaSpellCooldowns() entry->RecoveryTime <= 15 * MINUTE * IN_MILISECONDS && entry->CategoryRecoveryTime <= 15 * MINUTE * IN_MILISECONDS ) { - // notify player - WorldPacket data(SMSG_CLEAR_COOLDOWN, 4+8); - data << uint32(itr->first); - data << uint64(GetGUID()); - GetSession()->SendPacket(&data); - // remove cooldown - m_spellCooldowns.erase(itr); + // remove & notify + RemoveSpellCooldown(itr->first, true); } } } @@ -3421,12 +3520,8 @@ void Player::RemoveAllSpellCooldown() if(!m_spellCooldowns.empty()) { for(SpellCooldowns::const_iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end(); ++itr) - { - WorldPacket data(SMSG_CLEAR_COOLDOWN, 4+8); - data << uint32(itr->first); - data << uint64(GetGUID()); - GetSession()->SendPacket(&data); - } + SendClearCooldown(itr->first, this); + m_spellCooldowns.clear(); } } @@ -3451,7 +3546,7 @@ void Player::_LoadSpellCooldowns(QueryResult *result) if(!sSpellStore.LookupEntry(spell_id)) { - sLog.outError("Player %u have unknown spell %u in `character_spell_cooldown`, skipping.",GetGUIDLow(),spell_id); + sLog.outError("Player %u has unknown spell %u in `character_spell_cooldown`, skipping.",GetGUIDLow(),spell_id); continue; } @@ -3474,7 +3569,7 @@ void Player::_SaveSpellCooldowns() CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'", GetGUIDLow()); time_t curTime = time(NULL); - time_t infTime = curTime + MONTH/2; + time_t infTime = curTime + infinityCooldownDelayCheck; // remove outdated and save active for(SpellCooldowns::iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end();) @@ -3528,10 +3623,7 @@ bool Player::resetTalents(bool no_cost) { // not need after this call if(HasAtLoginFlag(AT_LOGIN_RESET_TALENTS)) - { - m_atLoginFlags = m_atLoginFlags & ~AT_LOGIN_RESET_TALENTS; - CharacterDatabase.PExecute("UPDATE characters set at_login = at_login & ~ %u WHERE guid ='%u'", uint32(AT_LOGIN_RESET_TALENTS), GetGUIDLow()); - } + RemoveAtLoginFlag(AT_LOGIN_RESET_TALENTS,true); uint32 talentPointsForLevel = CalculateTalentsPoints(); @@ -3585,7 +3677,13 @@ bool Player::resetTalents(bool no_cost) uint32 itrFirstId = spellmgr.GetFirstSpellInChain(itr->first); // unlearn if first rank is talent or learned by talent - if (itrFirstId == talentInfo->RankID[j] || spellmgr.IsSpellLearnToSpell(talentInfo->RankID[j],itrFirstId)) + if (itrFirstId == talentInfo->RankID[j]) + { + removeSpell(itr->first,!IsPassiveSpell(itr->first),false); + itr = GetSpellMap().begin(); + continue; + } + else if (spellmgr.IsSpellLearnToSpell(talentInfo->RankID[j],itrFirstId)) { removeSpell(itr->first,!IsPassiveSpell(itr->first)); itr = GetSpellMap().begin(); @@ -3791,9 +3889,9 @@ void Player::BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) Unit::BuildCreateUpdateBlockForPlayer( data, target ); } -void Player::DestroyForPlayer( Player *target ) const +void Player::DestroyForPlayer( Player *target, bool anim ) const { - Unit::DestroyForPlayer( target ); + Unit::DestroyForPlayer( target, anim ); for(uint8 i = 0; i < INVENTORY_SLOT_BAG_END; i++) { @@ -4036,7 +4134,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC CharacterDatabase.PExecute("DELETE FROM character_equipmentsets WHERE guid = '%u'",guid); CharacterDatabase.CommitTransaction(); - //LoginDatabase.PExecute("UPDATE realmcharacters SET numchars = numchars - 1 WHERE acctid = %d AND realmid = %d", accountId, realmID); + //loginDatabase.PExecute("UPDATE realmcharacters SET numchars = numchars - 1 WHERE acctid = %d AND realmid = %d", accountId, realmID); if(updateRealmChars) sWorld.UpdateRealmCharCount(accountId); } @@ -4458,7 +4556,7 @@ uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool g uint32 dmultiplier = dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class,ditemProto->SubClass)]; uint32 costs = uint32(LostDurability*dmultiplier*double(dQualitymodEntry->quality_mod)); - costs = uint32(costs * discountMod); + costs = uint32(costs * discountMod) * sWorld.getRate(RATE_REPAIRCOST); if (costs==0) //fix for ITEM_QUALITY_ARTIFACT costs = 1; @@ -5214,6 +5312,9 @@ void Player::UpdateWeaponSkill (WeaponAttackType attType) if(m_form == FORM_TREE) return; // use weapon but not skill up + if(pVictim && pVictim->GetTypeId() == TYPEID_UNIT && (((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_SKILLGAIN)) + return; + uint32 weapon_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_WEAPON); switch(attType) @@ -5557,65 +5658,69 @@ void Player::SendInitialActionButtons() const sLog.outDetail( "Initializing Action Buttons for '%u'", GetGUIDLow() ); WorldPacket data(SMSG_ACTION_BUTTONS, 1+(MAX_ACTION_BUTTONS*4)); - data << uint8(0); // can be 0, 1, 2 + data << uint8(0); // can be 0, 1, 2 (talent spec) for(int button = 0; button < MAX_ACTION_BUTTONS; ++button) { ActionButtonList::const_iterator itr = m_actionButtons.find(button); if(itr != m_actionButtons.end() && itr->second.uState != ACTIONBUTTON_DELETED) - { - data << uint16(itr->second.action); - data << uint8(itr->second.misc); - data << uint8(itr->second.type); - } + data << uint32(itr->second.packedData); else - { data << uint32(0); - } } GetSession()->SendPacket( &data ); sLog.outDetail( "Action Buttons for '%u' Initialized", GetGUIDLow() ); } -bool Player::addActionButton(const uint8 button, const uint16 action, const uint8 type, const uint8 misc) +ActionButton* Player::addActionButton(uint8 button, uint32 action, uint8 type) { if(button >= MAX_ACTION_BUTTONS) { sLog.outError( "Action %u not added into button %u for player %s: button must be < 132", action, button, GetName() ); - return false; + return NULL; } - // check cheating with adding non-known spells to action bar - if(type==ACTION_BUTTON_SPELL) + if(action >= MAX_ACTION_BUTTON_ACTION_VALUE) { - if(!sSpellStore.LookupEntry(action)) - { - sLog.outError( "Action %u not added into button %u for player %s: spell not exist", action, button, GetName() ); - return false; - } - - if(!HasSpell(action)) - { - sLog.outError( "Action %u not added into button %u for player %s: player don't known this spell", action, button, GetName() ); - return false; - } + sLog.outError( "Action %u not added into button %u for player %s: action must be < %u", action, button, GetName(), MAX_ACTION_BUTTON_ACTION_VALUE ); + return NULL; } - ActionButtonList::iterator buttonItr = m_actionButtons.find(button); + switch(type) + { + case ACTION_BUTTON_SPELL: + if(!sSpellStore.LookupEntry(action)) + { + sLog.outError( "Action %u not added into button %u for player %s: spell not exist", action, button, GetName() ); + return NULL; + } - if (buttonItr==m_actionButtons.end()) - { // just add new button - m_actionButtons[button] = ActionButton(action,type,misc); + if(!HasSpell(action)) + { + sLog.outError( "Action %u not added into button %u for player %s: player don't known this spell", action, button, GetName() ); + return NULL; + } + break; + case ACTION_BUTTON_ITEM: + if(!objmgr.GetItemPrototype(action)) + { + sLog.outError( "Action %u not added into button %u for player %s: item not exist", action, button, GetName() ); + return NULL; + } + break; + default: + break; // pther cases not checked at this moment } - else - { // change state of current button - ActionButtonUpdateState uState = buttonItr->second.uState; - buttonItr->second = ActionButton(action,type,misc); - if (uState != ACTIONBUTTON_NEW) buttonItr->second.uState = ACTIONBUTTON_CHANGED; - }; - sLog.outDetail( "Player '%u' Added Action '%u' to Button '%u'", GetGUIDLow(), action, button ); - return true; + + // it create new button (NEW state) if need or return existed + ActionButton& ab = m_actionButtons[button]; + + // set data and update to CHANGED if not NEW + ab.SetActionAndType(action,ActionButtonType(type)); + + sLog.outDetail( "Player '%u' Added Action '%u' (type %u) to Button '%u'", GetGUIDLow(), action, uint32(type), button ); + return &ab; } void Player::removeActionButton(uint8 button) @@ -5883,7 +5988,7 @@ int32 Player::CalculateReputationGain(uint32 creatureOrQuestLevel, int32 rep, in if (percent <= 0.0f) return 0; - return int32(rep*percent*0.01f); + return int32(rep*percent)/100; } //Calculates how many reputation points player gains in victim's enemy factions @@ -6010,7 +6115,7 @@ bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor, bool pvpt } // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens - if(GetDummyAura(SPELL_AURA_PLAYER_INACTIVE)) + if(HasAura(SPELL_AURA_PLAYER_INACTIVE)) return false; uint64 victim_guid = 0; @@ -6259,6 +6364,19 @@ uint32 Player::GetZoneIdFromDB(uint64 guid) return zone; } +uint32 Player::GetLevelFromDB(uint64 guid) +{ + QueryResult *result = CharacterDatabase.PQuery( "SELECT level FROM characters WHERE guid='%u'", GUID_LOPART(guid) ); + if (!result) + return 0; + + Field* fields = result->Fetch(); + uint32 level = fields[0].GetUInt32(); + delete result; + + return level; +} + void Player::UpdateArea(uint32 newArea) { // FFA_PVP flags are area and not zone id dependent @@ -6328,10 +6446,11 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) } pvpInfo.inNoPvPArea = false; - if((zone->flags & AREA_FLAG_SANCTUARY) || zone->mapid == 609) // in sanctuary + if(zone->IsSanctuary()) // in sanctuary { SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); pvpInfo.inNoPvPArea = true; + CombatStopWithPets(); } else { @@ -6456,22 +6575,6 @@ void Player::DuelComplete(DuelCompleteType type) duel->opponent->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL, 1); } - // cool-down duel spell - /*data.Initialize(SMSG_SPELL_COOLDOWN, 17); - - data<<GetGUID(); - data<<uint8(0x0); - - data<<(uint32)7266; - data<<uint32(0x0); - GetSession()->SendPacket(&data); - data.Initialize(SMSG_SPELL_COOLDOWN, 17); - data<<duel->opponent->GetGUID(); - data<<uint8(0x0); - data<<(uint32)7266; - data<<uint32(0x0); - duel->opponent->GetSession()->SendPacket(&data);*/ - //Remove Duel Flag object GameObject* obj = GetMap()->GetGameObject(GetUInt64Value(PLAYER_DUEL_ARBITER)); if(obj) @@ -6568,13 +6671,16 @@ 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, bool only_level_scale /*= false*/) { if(slot >= INVENTORY_SLOT_BAG_END || !proto) return; - ScalingStatDistributionEntry const *ssd = proto->ScalingStatDistribution ? sScalingStatDistributionStore.LookupEntry(proto->ScalingStatDistribution) : 0; - ScalingStatValuesEntry const *ssv = proto->ScalingStatValue ? sScalingStatValuesStore.LookupEntry(getLevel()) : 0; + ScalingStatDistributionEntry const *ssd = proto->ScalingStatDistribution ? sScalingStatDistributionStore.LookupEntry(proto->ScalingStatDistribution) : NULL; + ScalingStatValuesEntry const *ssv = proto->ScalingStatValue ? sScalingStatValuesStore.LookupEntry(getLevel()) : NULL; + + if(only_level_scale && !(ssd && ssv)) + return; for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i) { @@ -6755,7 +6861,8 @@ void Player::_ApplyItemBonuses(ItemPrototype const *proto, uint8 slot, bool appl } // Add armor bonus from ArmorDamageModifier if > 0 if (proto->ArmorDamageModifier > 0) - armor+=proto->ArmorDamageModifier; + armor += uint32(proto->ArmorDamageModifier); + if (armor) HandleStatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(armor), apply); @@ -7015,17 +7122,48 @@ void Player::UpdateEquipSpellsAtFormChange() } } } - -void Player::CastItemCombatSpell(Item *item, CalcDamageInfo *damageInfo, ItemPrototype const * proto) +void Player::CastItemCombatSpell(Unit *target, WeaponAttackType attType, uint32 procVictim, uint32 procEx) { - Unit * Target = damageInfo->target; - WeaponAttackType attType = damageInfo->attackType; - - if (!Target || Target == this ) + if(!target || !target->isAlive() || target == this) return; + for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++) + { + // If usable, try to cast item spell + if (Item * item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0,i)) + if(!item->IsBroken()) + if (ItemPrototype const *proto = item->GetProto()) + { + // Additional check for weapons + if (proto->Class==ITEM_CLASS_WEAPON) + { + // offhand item cannot proc from main hand hit etc + EquipmentSlots slot; + switch (attType) + { + case BASE_ATTACK: slot = EQUIPMENT_SLOT_MAINHAND; break; + case OFF_ATTACK: slot = EQUIPMENT_SLOT_OFFHAND; break; + case RANGED_ATTACK: slot = EQUIPMENT_SLOT_RANGED; break; + default: slot = EQUIPMENT_SLOT_END; break; + } + if (slot != i) + continue; + // Check if item is useable (forms or disarm) + if (attType == BASE_ATTACK) + { + if (!((Player*)this)->IsUseEquipedWeapon(true)) + continue; + } + } + ((Player*)this)->CastItemCombatSpell(target, attType, procVictim, procEx, item, proto); + } + } +} + +void Player::CastItemCombatSpell(Unit *target, WeaponAttackType attType, uint32 procVictim, uint32 procEx, Item *item, ItemPrototype const * proto) +{ // Can do effect if any damage done to target - if (damageInfo->damage) + if (procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE) //if (damageInfo->procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE) { for (int i = 0; i < 5; i++) @@ -7048,7 +7186,7 @@ void Player::CastItemCombatSpell(Item *item, CalcDamageInfo *damageInfo, ItemPro } // not allow proc extra attack spell at extra attack - if( m_extraAttacks && IsSpellHaveEffect(spellInfo,SPELL_EFFECT_ADD_EXTRA_ATTACKS) ) + if( m_extraAttacks && IsSpellHaveEffect(spellInfo, SPELL_EFFECT_ADD_EXTRA_ATTACKS) ) return; float chance = spellInfo->procChance; @@ -7064,7 +7202,7 @@ void Player::CastItemCombatSpell(Item *item, CalcDamageInfo *damageInfo, ItemPro } if (roll_chance_f(chance)) - CastSpell(Target, spellInfo->Id, true, item); + CastSpell(target, spellInfo->Id, true, item); } } @@ -7084,13 +7222,13 @@ void Player::CastItemCombatSpell(Item *item, CalcDamageInfo *damageInfo, ItemPro if (entry && entry->procEx) { // Check hit/crit/dodge/parry requirement - if((entry->procEx & damageInfo->procEx) == 0) + if((entry->procEx & procEx) == 0) continue; } else { // Can do effect if any damage done to target - if (!(damageInfo->damage)) + if (!(procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE)) //if (!(damageInfo->procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE)) continue; } @@ -7105,10 +7243,7 @@ void Player::CastItemCombatSpell(Item *item, CalcDamageInfo *damageInfo, ItemPro float chance = pEnchant->amount[s] != 0 ? float(pEnchant->amount[s]) : GetWeaponProcChance(); if (entry && entry->PPMChance) - { - uint32 WeaponSpeed = GetAttackTime(attType); - chance = GetPPMProcChance(WeaponSpeed, entry->PPMChance, spellInfo); - } + chance = GetPPMProcChance(proto->Delay, entry->PPMChance, spellInfo); else if (entry && entry->customChance) chance = entry->customChance; @@ -7120,7 +7255,7 @@ void Player::CastItemCombatSpell(Item *item, CalcDamageInfo *damageInfo, ItemPro if(IsPositiveSpell(pEnchant->spellid[s])) CastSpell(this, pEnchant->spellid[s], true, item); else - CastSpell(Target, pEnchant->spellid[s], true, item); + CastSpell(target, pEnchant->spellid[s], true, item); } } } @@ -7309,6 +7444,24 @@ void Player::_ApplyAllItemMods() sLog.outDebug("_ApplyAllItemMods complete."); } +void Player::_ApplyAllLevelScaleItemMods(bool apply) +{ + for (int i = 0; i < INVENTORY_SLOT_BAG_END; ++i) + { + if(m_items[i]) + { + if(m_items[i]->IsBroken()) + continue; + + ItemPrototype const *proto = m_items[i]->GetProto(); + if(!proto) + continue; + + _ApplyItemBonuses(proto,i, apply, true); + } + } +} + void Player::_ApplyAmmoBonuses() { // check ammo @@ -7434,7 +7587,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type) if (go->getLootState() == GO_READY) { - uint32 lootid = go->GetLootId(); + uint32 lootid = go->GetGOInfo()->GetLootId(); //TODO: fix this big hack if((go->GetEntry() == BG_AV_OBJECTID_MINE_N || go->GetEntry() == BG_AV_OBJECTID_MINE_S)) @@ -9040,39 +9193,42 @@ uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountV Item* pItem2 = GetItemByPos( bag, slot ); // ignore move item (this slot will be empty at move) - if(pItem2==pSrcItem) + if (pItem2==pSrcItem) pItem2 = NULL; uint32 need_space; // empty specific slot - check item fit to slot - if( !pItem2 || swap ) + if (!pItem2 || swap) { - if( bag == INVENTORY_SLOT_BAG_0 ) + if (bag == INVENTORY_SLOT_BAG_0) { // keyring case - if(slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_START+GetMaxKeyringSize() && !(pProto->BagFamily & BAG_FAMILY_MASK_KEYS)) + if (slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_START+GetMaxKeyringSize() && !(pProto->BagFamily & BAG_FAMILY_MASK_KEYS)) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; // currencytoken case - if(slot >= CURRENCYTOKEN_SLOT_START && slot < CURRENCYTOKEN_SLOT_END && !(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS)) + if (slot >= CURRENCYTOKEN_SLOT_START && slot < CURRENCYTOKEN_SLOT_END && !(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS)) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; // prevent cheating - if(slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END || slot >= PLAYER_SLOT_END) + if (slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END || slot >= PLAYER_SLOT_END) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; } else { Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag ); - if( !pBag ) + if (!pBag) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; ItemPrototype const* pBagProto = pBag->GetProto(); - if( !pBagProto ) + if (!pBagProto) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; - if( !ItemCanGoIntoBag(pProto,pBagProto) ) + if (slot >= pBagProto->ContainerSlots) + return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; + + if (!ItemCanGoIntoBag(pProto,pBagProto)) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; } @@ -9083,22 +9239,22 @@ uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountV else { // check item type - if(pItem2->GetEntry() != pProto->ItemId) + if (pItem2->GetEntry() != pProto->ItemId) return EQUIP_ERR_ITEM_CANT_STACK; // check free space - if(pItem2->GetCount() >= pProto->GetMaxStackSize()) + if (pItem2->GetCount() >= pProto->GetMaxStackSize()) return EQUIP_ERR_ITEM_CANT_STACK; // free stack space or infinity need_space = pProto->GetMaxStackSize() - pItem2->GetCount(); } - if(need_space > count) + if (need_space > count) need_space = count; ItemPosCount newPosition = ItemPosCount((bag << 8) | slot, need_space); - if(!newPosition.isContainedIn(dest)) + if (!newPosition.isContainedIn(dest)) { dest.push_back(newPosition); count -= need_space; @@ -9109,55 +9265,55 @@ uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountV uint8 Player::_CanStoreItem_InBag( uint8 bag, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool merge, bool non_specialized, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot ) const { // skip specific bag already processed in first called _CanStoreItem_InBag - if(bag==skip_bag) + if (bag==skip_bag) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag ); - if( !pBag ) + if (!pBag) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; ItemPrototype const* pBagProto = pBag->GetProto(); - if( !pBagProto ) + if (!pBagProto) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; // specialized bag mode or non-specilized - if( non_specialized != (pBagProto->Class == ITEM_CLASS_CONTAINER && pBagProto->SubClass == ITEM_SUBCLASS_CONTAINER) ) + if (non_specialized != (pBagProto->Class == ITEM_CLASS_CONTAINER && pBagProto->SubClass == ITEM_SUBCLASS_CONTAINER)) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; - if( !ItemCanGoIntoBag(pProto,pBagProto) ) + if (!ItemCanGoIntoBag(pProto,pBagProto)) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; for(uint32 j = 0; j < pBag->GetBagSize(); j++) { // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot - if(j==skip_slot) + if (j==skip_slot) continue; Item* pItem2 = GetItemByPos( bag, j ); // ignore move item (this slot will be empty at move) - if(pItem2==pSrcItem) + if (pItem2==pSrcItem) pItem2 = NULL; // if merge skip empty, if !merge skip non-empty - if((pItem2!=NULL)!=merge) + if ((pItem2!=NULL)!=merge) continue; - if( pItem2 ) + if (pItem2) { - if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->GetMaxStackSize()) + if (pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->GetMaxStackSize()) { uint32 need_space = pProto->GetMaxStackSize() - pItem2->GetCount(); if(need_space > count) need_space = count; ItemPosCount newPosition = ItemPosCount((bag << 8) | j, need_space); - if(!newPosition.isContainedIn(dest)) + if (!newPosition.isContainedIn(dest)) { dest.push_back(newPosition); count -= need_space; - if(count==0) + if (count==0) return EQUIP_ERR_OK; } } @@ -9165,16 +9321,16 @@ uint8 Player::_CanStoreItem_InBag( uint8 bag, ItemPosCountVec &dest, ItemPrototy else { uint32 need_space = pProto->GetMaxStackSize(); - if(need_space > count) + if (need_space > count) need_space = count; ItemPosCount newPosition = ItemPosCount((bag << 8) | j, need_space); - if(!newPosition.isContainedIn(dest)) + if (!newPosition.isContainedIn(dest)) { dest.push_back(newPosition); count -= need_space; - if(count==0) + if (count==0) return EQUIP_ERR_OK; } } @@ -9187,33 +9343,33 @@ uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, for(uint32 j = slot_begin; j < slot_end; j++) { // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot - if(INVENTORY_SLOT_BAG_0==skip_bag && j==skip_slot) + if (INVENTORY_SLOT_BAG_0==skip_bag && j==skip_slot) continue; Item* pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, j ); // ignore move item (this slot will be empty at move) - if(pItem2==pSrcItem) + if (pItem2==pSrcItem) pItem2 = NULL; // if merge skip empty, if !merge skip non-empty - if((pItem2!=NULL)!=merge) + if ((pItem2!=NULL)!=merge) continue; - if( pItem2 ) + if (pItem2) { - if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->GetMaxStackSize()) + if (pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->GetMaxStackSize()) { uint32 need_space = pProto->GetMaxStackSize() - pItem2->GetCount(); - if(need_space > count) + if (need_space > count) need_space = count; ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space); - if(!newPosition.isContainedIn(dest)) + if (!newPosition.isContainedIn(dest)) { dest.push_back(newPosition); count -= need_space; - if(count==0) + if (count==0) return EQUIP_ERR_OK; } } @@ -9221,16 +9377,16 @@ uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, else { uint32 need_space = pProto->GetMaxStackSize(); - if(need_space > count) + if (need_space > count) need_space = count; ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space); - if(!newPosition.isContainedIn(dest)) + if (!newPosition.isContainedIn(dest)) { dest.push_back(newPosition); count -= need_space; - if(count==0) + if (count==0) return EQUIP_ERR_OK; } } @@ -9243,16 +9399,16 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 sLog.outDebug( "STORAGE: CanStoreItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, entry, count); ItemPrototype const *pProto = objmgr.GetItemPrototype(entry); - if( !pProto ) + if (!pProto) { - if(no_space_count) + if (no_space_count) *no_space_count = count; return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED :EQUIP_ERR_ITEM_NOT_FOUND; } - if(pItem && pItem->IsBindedNotWith(this)) + if (pItem && pItem->IsBindedNotWith(this)) { - if(no_space_count) + if (no_space_count) *no_space_count = count; return EQUIP_ERR_DONT_OWN_THAT_ITEM; } @@ -9260,11 +9416,11 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 // check count of items (skip for auto move for same player from bank) uint32 no_similar_count = 0; // can't store this amount similar items uint8 res = _CanTakeMoreSimilarItems(entry,count,pItem,&no_similar_count); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) { - if(count==no_similar_count) + if (count==no_similar_count) { - if(no_space_count) + if (no_space_count) *no_space_count = no_similar_count; return res; } @@ -9272,22 +9428,22 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 } // in specific slot - if( bag != NULL_BAG && slot != NULL_SLOT ) + if (bag != NULL_BAG && slot != NULL_SLOT) { res = _CanStoreItem_InSpecificSlot(bag,slot,dest,pProto,count,swap,pItem); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) { - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } @@ -9296,45 +9452,45 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 // not specific slot or have space for partly store only in specific slot // in specific bag - if( bag != NULL_BAG ) + 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 + if (bag == INVENTORY_SLOT_BAG_0) // inventory { res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,CURRENCYTOKEN_SLOT_END,dest,pProto,count,true,pItem,bag,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) { - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + 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,true,pItem,bag,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) { - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } @@ -9343,22 +9499,22 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 { // we need check 2 time (specialized/non_specialized), use NULL_BAG to prevent skipping bag res = _CanStoreItem_InBag(bag,dest,pProto,count,true,false,pItem,NULL_BAG,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) res = _CanStoreItem_InBag(bag,dest,pProto,count,true,true,pItem,NULL_BAG,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) { - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } @@ -9366,83 +9522,83 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 } // search free slot in bag for place to - if( bag == INVENTORY_SLOT_BAG_0 ) // inventory + if(bag == INVENTORY_SLOT_BAG_0) // inventory { // search free slot - keyring case - if(pProto->BagFamily & BAG_FAMILY_MASK_KEYS) + if (pProto->BagFamily & BAG_FAMILY_MASK_KEYS) { uint32 keyringSize = GetMaxKeyringSize(); res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_START+keyringSize,dest,pProto,count,false,pItem,bag,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) { - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } res = _CanStoreItem_InInventorySlots(CURRENCYTOKEN_SLOT_START,CURRENCYTOKEN_SLOT_END,dest,pProto,count,false,pItem,bag,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) { - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } } - else if(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) + 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 (res!=EQUIP_ERR_OK) { - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + 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) + if (res!=EQUIP_ERR_OK) { - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } @@ -9450,22 +9606,22 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 else // equipped bag { res = _CanStoreItem_InBag(bag,dest,pProto,count,false,false,pItem,NULL_BAG,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) res = _CanStoreItem_InBag(bag,dest,pProto,count,false,true,pItem,NULL_BAG,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) { - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } @@ -9475,58 +9631,58 @@ 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,CURRENCYTOKEN_SLOT_END,dest,pProto,count,true,pItem,bag,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) { - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + 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,true,pItem,bag,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) { - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } - if( pProto->BagFamily ) + if (pProto->BagFamily) { for(uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) { res = _CanStoreItem_InBag(i,dest,pProto,count,true,false,pItem,bag,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) continue; - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } @@ -9536,15 +9692,15 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 for(uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) { res = _CanStoreItem_InBag(i,dest,pProto,count,true,true,pItem,bag,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) continue; - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } @@ -9552,45 +9708,45 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 } // search free slot - special bag case - if( pProto->BagFamily ) + if (pProto->BagFamily) { - if(pProto->BagFamily & BAG_FAMILY_MASK_KEYS) + if (pProto->BagFamily & BAG_FAMILY_MASK_KEYS) { uint32 keyringSize = GetMaxKeyringSize(); res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_START+keyringSize,dest,pProto,count,false,pItem,bag,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) { - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } } - else if(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) + 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 (res!=EQUIP_ERR_OK) { - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } @@ -9599,15 +9755,15 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 for(uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) { res = _CanStoreItem_InBag(i,dest,pProto,count,false,false,pItem,bag,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) continue; - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } @@ -9616,19 +9772,19 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 // search free slot res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) { - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return res; } - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } @@ -9636,21 +9792,21 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 for(uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) { res = _CanStoreItem_InBag(i,dest,pProto,count,false,true,pItem,bag,slot); - if(res!=EQUIP_ERR_OK) + if (res!=EQUIP_ERR_OK) continue; - if(count==0) + if (count==0) { - if(no_similar_count==0) + if (no_similar_count==0) return EQUIP_ERR_OK; - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } } - if(no_space_count) + if (no_space_count) *no_space_count = count + no_similar_count; return EQUIP_ERR_INVENTORY_FULL; @@ -10378,7 +10534,7 @@ uint8 Player::CanUseAmmo( uint32 item ) const return EQUIP_ERR_CANT_EQUIP_LEVEL_I; // Requires No Ammo - if(GetDummyAura(46699)) + if(HasAura(46699)) return EQUIP_ERR_BAG_FULL6; return EQUIP_ERR_OK; @@ -10474,7 +10630,7 @@ Item* Player::_StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, boo uint8 bag = pos >> 8; uint8 slot = pos & 255; - sLog.outDebug( "STORAGE: StoreItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, pItem->GetEntry(), count); + sLog.outDebug( "STORAGE: StoreItem bag = %u, slot = %u, item = %u, count = %u, guid = %u", bag, slot, pItem->GetEntry(), count, pItem->GetGUIDLow()); Item *pItem2 = GetItemByPos( bag, slot ); @@ -10581,7 +10737,6 @@ Item* Player::EquipNewItem( uint16 pos, uint32 item, bool update ) Item* Player::EquipItem( uint16 pos, Item *pItem, bool update ) { - AddEnchantmentDurations(pItem); AddItemDurations(pItem); @@ -12311,7 +12466,11 @@ void Player::PrepareQuestMenu( uint64 guid ) } else { - GameObject *pGameObject = GetMap()->GetGameObject(guid); + //we should obtain map pointer from GetMap() in 99% of cases. Special case + //only for quests which cast teleport spells on player + Map * _map = IsInWorld() ? GetMap() : MapManager::Instance().FindMap(GetMapId(), GetInstanceId()); + ASSERT(_map); + GameObject *pGameObject = _map->GetGameObject(guid); if( pGameObject ) { pObject = (Object*)pGameObject; @@ -12333,8 +12492,8 @@ void Player::PrepareQuestMenu( uint64 guid ) qm.AddMenuItem(quest_id, DIALOG_STATUS_UNK2); else if ( status == QUEST_STATUS_INCOMPLETE ) qm.AddMenuItem(quest_id, DIALOG_STATUS_UNK2); - else if (status == QUEST_STATUS_AVAILABLE ) - qm.AddMenuItem(quest_id, DIALOG_STATUS_CHAT); + //else if (status == QUEST_STATUS_AVAILABLE ) + // qm.AddMenuItem(quest_id, DIALOG_STATUS_CHAT); } for(QuestRelations::const_iterator i = pObjectQR->lower_bound(pObject->GetEntry()); i != pObjectQR->upper_bound(pObject->GetEntry()); ++i) @@ -12464,7 +12623,11 @@ Quest const * Player::GetNextQuest( uint64 guid, Quest const *pQuest ) } else { - GameObject *pGameObject = GetMap()->GetGameObject(guid); + //we should obtain map pointer from GetMap() in 99% of cases. Special case + //only for quests which cast teleport spells on player + Map * _map = IsInWorld() ? GetMap() : MapManager::Instance().FindMap(GetMapId(), GetInstanceId()); + ASSERT(_map); + GameObject *pGameObject = _map->GetGameObject(guid); if( pGameObject ) { pObject = (Object*)pGameObject; @@ -12746,7 +12909,7 @@ void Player::AddQuest( Quest const *pQuest, Object *questGiver ) //starting initial quest script if(questGiver && pQuest->GetQuestStartScript()!=0) - sWorld.ScriptsStart(sQuestStartScripts, pQuest->GetQuestStartScript(), questGiver, this); + GetMap()->ScriptsStart(sQuestStartScripts, pQuest->GetQuestStartScript(), questGiver, this); // Some spells applied at quest activation SpellAreaForQuestMapBounds saBounds = spellmgr.GetSpellAreaForQuestMapBounds(quest_id,true); @@ -12798,11 +12961,15 @@ void Player::IncompleteQuest( uint32 quest_id ) void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver, bool announce ) { + //this THING should be here to protect code from quest, which cast on player far teleport as a reward + //should work fine, cause far teleport will be executed in Player::Update() + SetCanDelayTeleport(true); + uint32 quest_id = pQuest->GetQuestId(); for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; i++ ) { - if ( pQuest->ReqItemId[i] ) + if (pQuest->ReqItemId[i]) DestroyItemCount( pQuest->ReqItemId[i], pQuest->ReqItemCount[i], true); } @@ -12810,29 +12977,29 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver // SetTimedQuest( 0 ); m_timedquests.erase(pQuest->GetQuestId()); - if ( pQuest->GetRewChoiceItemsCount() > 0 ) + if (pQuest->GetRewChoiceItemsCount() > 0) { - if( pQuest->RewChoiceItemId[reward] ) + if (uint32 itemId = pQuest->RewChoiceItemId[reward]) { ItemPosCountVec dest; - if( CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewChoiceItemId[reward], pQuest->RewChoiceItemCount[reward] ) == EQUIP_ERR_OK ) + if (CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, itemId, pQuest->RewChoiceItemCount[reward] ) == EQUIP_ERR_OK) { - Item* item = StoreNewItem( dest, pQuest->RewChoiceItemId[reward], true); + Item* item = StoreNewItem( dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId)); SendNewItem(item, pQuest->RewChoiceItemCount[reward], true, false); } } } - if ( pQuest->GetRewItemsCount() > 0 ) + if (pQuest->GetRewItemsCount() > 0) { for (uint32 i=0; i < pQuest->GetRewItemsCount(); ++i) { - if( pQuest->RewItemId[i] ) + if (uint32 itemId = pQuest->RewItemId[i]) { ItemPosCountVec dest; - if( CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewItemId[i], pQuest->RewItemCount[i] ) == EQUIP_ERR_OK ) + if (CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, itemId, pQuest->RewItemCount[i] ) == EQUIP_ERR_OK) { - Item* item = StoreNewItem( dest, pQuest->RewItemId[i], true); + Item* item = StoreNewItem( dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId)); SendNewItem(item, pQuest->RewItemCount[i], true, false); } } @@ -12841,13 +13008,8 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver RewardReputation( pQuest ); - if( pQuest->GetRewSpellCast() > 0 ) - CastSpell( this, pQuest->GetRewSpellCast(), true); - else if( pQuest->GetRewSpell() > 0) - CastSpell( this, pQuest->GetRewSpell(), true); - uint16 log_slot = FindQuestSlot( quest_id ); - if( log_slot < MAX_QUEST_LOG_SIZE) + if (log_slot < MAX_QUEST_LOG_SIZE) SetQuestSlot(log_slot,0); QuestStatusData& q_status = mQuestStatus[quest_id]; @@ -12855,7 +13017,7 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver // Not give XP in case already completed once repeatable quest uint32 XP = q_status.m_rewarded ? 0 : uint32(pQuest->XPValue( this )*sWorld.getRate(RATE_XP_QUEST)); - if ( getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) ) + if (getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) GiveXP( XP , NULL ); else { @@ -12865,33 +13027,33 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver } // Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative - if(pQuest->GetRewOrReqMoney()) + if (pQuest->GetRewOrReqMoney()) { ModifyMoney( pQuest->GetRewOrReqMoney() ); - if(pQuest->GetRewOrReqMoney() > 0) + if (pQuest->GetRewOrReqMoney() > 0) GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, pQuest->GetRewOrReqMoney()); } // honor reward - if(pQuest->GetRewHonorableKills()) - RewardHonor(NULL, 0, Trinity::Honor::hk_honor_at_level(getLevel(), pQuest->GetRewHonorableKills())); + if (pQuest->GetRewHonorableKills()) + RewardHonor(NULL, 0, MaNGOS::Honor::hk_honor_at_level(getLevel(), pQuest->GetRewHonorableKills())); // title reward - if(pQuest->GetCharTitleId()) + if (pQuest->GetCharTitleId()) { - if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId())) + if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId())) SetTitle(titleEntry); } - if(pQuest->GetBonusTalents()) + if (pQuest->GetBonusTalents()) { m_questRewardTalentCount+=pQuest->GetBonusTalents(); InitTalentForLevel(); } // Send reward mail - if(pQuest->GetRewMailTemplateId()) + if (pQuest->GetRewMailTemplateId()) { MailMessageType mailType; uint32 senderGuidOrEntry; @@ -12929,9 +13091,9 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver uint32 max_slot = questMailLoot.GetMaxSlotInLootFor(this); for(uint32 i = 0; mi.size() < MAX_MAIL_ITEMS && i < max_slot; ++i) { - if(LootItem* lootitem = questMailLoot.LootItemInSlot(i,this)) + if (LootItem* lootitem = questMailLoot.LootItemInSlot(i,this)) { - if(Item* item = Item::CreateItem(lootitem->itemid,lootitem->count,this)) + if (Item* item = Item::CreateItem(lootitem->itemid,lootitem->count,this)) { item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item); @@ -12942,23 +13104,30 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver WorldSession::SendMailTo(this, mailType, MAIL_STATIONERY_NORMAL, senderGuidOrEntry, GetGUIDLow(), "", 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE,pQuest->GetRewMailDelaySecs(),pQuest->GetRewMailTemplateId()); } - if(pQuest->IsDaily()) + if (pQuest->IsDaily()) { SetDailyQuestStatus(quest_id); GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST, 1); } - if ( !pQuest->IsRepeatable() ) + if (!pQuest->IsRepeatable()) SetQuestStatus(quest_id, QUEST_STATUS_COMPLETE); else SetQuestStatus(quest_id, QUEST_STATUS_NONE); q_status.m_rewarded = true; + if (q_status.uState != QUEST_NEW) + q_status.uState = QUEST_CHANGED; - if(announce) + if (announce) SendQuestReward( pQuest, XP, questGiver ); - if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED; + // cast spells after mark quest complete (some spells have quest completed state reqyurements in spell_area data) + if (pQuest->GetRewSpellCast() > 0) + CastSpell( this, pQuest->GetRewSpellCast(), true); + else if ( pQuest->GetRewSpell() > 0) + CastSpell( this, pQuest->GetRewSpell(), true); + if (pQuest->GetZoneOrSort() > 0) GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE, pQuest->GetZoneOrSort()); GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT); @@ -12990,6 +13159,9 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver if( !HasAura(itr->second->spellId) ) CastSpell(this,itr->second->spellId,true); } + + //lets remove flag for delayed teleports + SetCanDelayTeleport(false); } void Player::FailQuest( uint32 quest_id ) @@ -13657,7 +13829,17 @@ void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count ) UpdateForQuestWorldObjects(); } -void Player::KilledMonster( uint32 entry, uint64 guid ) +void Player::KilledMonster( CreatureInfo const* cInfo, uint64 guid ) +{ + if(cInfo->Entry) + KilledMonsterCredit(cInfo->Entry,guid); + + for(int i = 0; i < MAX_KILL_CREDIT; ++i) + if(cInfo->KillCredit[i]) + KilledMonsterCredit(cInfo->KillCredit[i],guid); +} + +void Player::KilledMonsterCredit( uint32 entry, uint64 guid ) { uint32 addkillcount = 1; GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, entry, addkillcount); @@ -13987,7 +14169,7 @@ void Player::SendQuestReward( Quest const *pQuest, uint32 XP, Object * questGive GetSession()->SendPacket( &data ); if (pQuest->GetQuestCompleteScript() != 0) - sWorld.ScriptsStart(sQuestEndScripts, pQuest->GetQuestCompleteScript(), questGiver, this); + GetMap()->ScriptsStart(sQuestEndScripts, pQuest->GetQuestCompleteScript(), questGiver, this); } void Player::SendQuestFailed( uint32 quest_id ) @@ -14074,8 +14256,8 @@ bool Player::MinimalLoadFromDB( QueryResult *result, uint32 guid ) bool delete_result = true; if (!result) { - // 0 1 2 3 4 5 6 7 8 9 10 - result = CharacterDatabase.PQuery("SELECT guid, data, name, position_x, position_y, position_z, map, totaltime, leveltime, at_login, zone FROM characters WHERE guid = '%u'",guid); + // 0 1 2 3 4 5 6 7 8 9 10 11 + result = CharacterDatabase.PQuery("SELECT guid, data, name, position_x, position_y, position_z, map, totaltime, leveltime, at_login, zone, level FROM characters WHERE guid = '%u'",guid); if (!result) return false; } @@ -14098,11 +14280,17 @@ bool Player::MinimalLoadFromDB( QueryResult *result, uint32 guid ) m_name = fields[2].GetCppString(); Relocate(fields[3].GetFloat(),fields[4].GetFloat(),fields[5].GetFloat()); - SetMapId(fields[6].GetUInt32()); + Map *map = MapManager::Instance().CreateMap(fields[6].GetUInt32(), this, 0); + SetMap(map); + + // randomize first save time in range [CONFIG_INTERVAL_SAVE] around [CONFIG_INTERVAL_SAVE] + // this must help in case next save after mass player load after server startup + m_nextSave = urand(m_nextSave/2,m_nextSave*3/2); + // the instance id is not needed at character enum - m_Played_time[0] = fields[7].GetUInt32(); - m_Played_time[1] = fields[8].GetUInt32(); + m_Played_time[PLAYED_TIME_TOTAL] = fields[7].GetUInt32(); + m_Played_time[PLAYED_TIME_LEVEL] = fields[8].GetUInt32(); m_atLoginFlags = fields[9].GetUInt32(); @@ -14308,8 +14496,8 @@ float Player::GetFloatValueFromDB(uint16 index, uint64 guid) bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) { - //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [28] [29] 30 31 32 33 34 35 36 37 38 39 40 - //QueryResult *result = CharacterDatabase.PQuery("SELECT guid, account, data, name, race, class, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty, arena_pending_points,bgid,bgteam,bgmap,bgx,bgy,bgz,bgo FROM characters WHERE guid = '%u'", guid); + //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 + //QueryResult *result = CharacterDatabase.PQuery("SELECT guid, account, data, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty, arena_pending_points,bgid,bgteam,bgmap,bgx,bgy,bgz,bgo FROM characters WHERE guid = '%u'", guid); QueryResult *result = holder->GetResult(PLAYER_LOGIN_QUERY_LOADFROM); if(!result) @@ -14336,7 +14524,8 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) m_name = fields[3].GetCppString(); // check name limitations - if(!ObjectMgr::IsValidName(m_name) || (GetSession()->GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(m_name))) + if (ObjectMgr::CheckPlayerName(m_name) != CHAR_NAME_SUCCESS || + GetSession()->GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(m_name)) { delete result; CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid ='%u'", uint32(AT_LOGIN_RENAME),guid); @@ -14353,6 +14542,23 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) // overwrite possible wrong/corrupted guid SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER)); + // overwrite some data fields + uint32 bytes0 = GetUInt32Value(UNIT_FIELD_BYTES_0) & 0xFF000000; + bytes0 |= fields[4].GetUInt8(); // race + bytes0 |= fields[5].GetUInt8() << 8; // class + bytes0 |= fields[6].GetUInt8() << 16; // gender + SetUInt32Value(UNIT_FIELD_BYTES_0, bytes0); + + SetUInt32Value(UNIT_FIELD_LEVEL, fields[7].GetUInt8()); + SetUInt32Value(PLAYER_XP, fields[8].GetUInt32()); + SetUInt32Value(PLAYER_FIELD_COINAGE, fields[9].GetUInt32()); + SetUInt32Value(PLAYER_BYTES, fields[10].GetUInt32()); + SetUInt32Value(PLAYER_BYTES_2, fields[11].GetUInt32()); + SetUInt32Value(PLAYER_BYTES_3, (GetUInt32Value(PLAYER_BYTES_3) & ~1) | fields[6].GetUInt8()); + SetUInt32Value(PLAYER_FLAGS, fields[12].GetUInt32()); + + InitDisplayIds(); + // cleanup inventory related item value fields (its will be filled correctly in _LoadInventory) for(uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) { @@ -14373,12 +14579,9 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) sLog.outDebug("Load Basic value of player %s is: ", m_name.c_str()); outDebugValues(); - m_race = fields[4].GetUInt8(); - //Need to call it to initialize m_team (m_team can be calculated from m_race) + //Need to call it to initialize m_team (m_team can be calculated from race) //Other way is to saves m_team into characters table. - setFactionForRace(m_race); - - m_class = fields[5].GetUInt8(); + setFactionForRace(getRace()); // load home bind and check in same time class/race pair, it used later for restore broken positions if(!_LoadHomeBind(holder->GetResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND))) @@ -14389,18 +14592,13 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) InitPrimaryProfessions(); // to max set before any spell loaded - // init saved position, and fix it later if problematic - uint32 transGUID = fields[24].GetUInt32(); - Relocate(fields[6].GetFloat(),fields[7].GetFloat(),fields[8].GetFloat(),fields[10].GetFloat()); - SetMapId(fields[9].GetUInt32()); - SetInstanceId(fields[41].GetFloat()); - SetDifficulty(fields[32].GetUInt32()); // may be changed in _LoadGroup + SetDifficulty(fields[39].GetUInt32()); // may be changed in _LoadGroup _LoadGroup(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGROUP)); _LoadArenaTeamInfo(holder->GetResult(PLAYER_LOGIN_QUERY_LOADARENAINFO)); - uint32 arena_currency = GetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY) + fields[33].GetUInt32(); + uint32 arena_currency = GetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY) + fields[40].GetUInt32(); if (arena_currency > sWorld.getConfig(CONFIG_MAX_ARENA_POINTS)) arena_currency = sWorld.getConfig(CONFIG_MAX_ARENA_POINTS); @@ -14424,35 +14622,42 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) _LoadBoundInstances(holder->GetResult(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES)); + // load player map related values + uint32 transGUID = fields[31].GetUInt32(); + Relocate(fields[13].GetFloat(),fields[14].GetFloat(),fields[15].GetFloat(),fields[17].GetFloat()); + uint32 mapId = fields[16].GetUInt32(); + uint32 instanceId = fields[41].GetFloat(); + std::string taxi_nodes = fields[38].GetCppString(); + + MapEntry const * mapEntry = sMapStore.LookupEntry(mapId); if(!IsPositionValid()) { sLog.outError("Player (guidlow %d) have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",guid,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); - RelocateToHomebind(); + RelocateToHomebind(mapId); transGUID = 0; + instanceId = 0; m_movementInfo.t_x = 0.0f; m_movementInfo.t_y = 0.0f; m_movementInfo.t_z = 0.0f; m_movementInfo.t_o = 0.0f; } - - uint32 bgid = fields[34].GetUInt32(); - uint32 bgteam = fields[35].GetUInt32(); - - if(bgid) //saved in BattleGround + // Player was saved in Arena or Bg + else if (mapEntry && mapEntry->IsBattleGroundOrArena()) { - SetBattleGroundEntryPoint(fields[36].GetUInt32(),fields[37].GetFloat(),fields[38].GetFloat(),fields[39].GetFloat(),fields[40].GetFloat()); + // Get Entry Point(bg master) or Homebind + SetBattleGroundEntryPoint(fields[43].GetUInt32(),fields[44].GetFloat(),fields[45].GetFloat(),fields[46].GetFloat(),fields[47].GetFloat()); - // check entry point and fix to homebind if need - MapEntry const* mapEntry = sMapStore.LookupEntry(m_bgEntryPoint.mapid); - if(!mapEntry || mapEntry->Instanceable() || !MapManager::IsValidMapCoord(m_bgEntryPoint)) + MapEntry const* bgEntry = sMapStore.LookupEntry(m_bgEntryPoint.mapid); + if(!bgEntry || bgEntry->Instanceable() || !MapManager::IsValidMapCoord(m_bgEntryPoint)) SetBattleGroundEntryPoint(m_homebindMapId,m_homebindX,m_homebindY,m_homebindZ,0.0f); - BattleGround *currentBg = sBattleGroundMgr.GetBattleGround(bgid, BATTLEGROUND_TYPE_NONE); - + // Bg still exists - join it! + BattleGround *currentBg = sBattleGroundMgr.GetBattleGround(instanceId, BATTLEGROUND_TYPE_NONE); if(currentBg && currentBg->IsPlayerInBattleGround(GetGUID())) { + uint32 bgteam = fields[42].GetUInt32(); BattleGroundQueueTypeId bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(currentBg->GetTypeID(), currentBg->GetArenaType()); AddBattleGroundQueueId(bgQueueTypeId); @@ -14465,38 +14670,25 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) SetInviteForBattleGroundQueueType(bgQueueTypeId,currentBg->GetInstanceID()); } + // Bg was not found - go to Entry Point else { + // Do not look for instance if bg not found + instanceId = 0; const WorldLocation& _loc = GetBattleGroundEntryPoint(); - SetMapId(_loc.mapid); + mapId = _loc.mapid; Relocate(_loc.coord_x, _loc.coord_y, _loc.coord_z, _loc.orientation); - //RemoveArenaAuras(true); } } - else + else if (transGUID != 0) { - MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId()); - // if server restart after player save in BG or area - // player can have current coordinates in to BG/Arean map, fix this - if(!mapEntry || mapEntry->IsBattleGroundOrArena()) - { - // return to BG master - SetMapId(fields[36].GetUInt32()); - Relocate(fields[37].GetFloat(),fields[38].GetFloat(),fields[39].GetFloat(),fields[40].GetFloat()); - - // check entry point and fix to homebind if need - mapEntry = sMapStore.LookupEntry(GetMapId()); - if(!mapEntry || mapEntry->IsBattleGroundOrArena() || !IsPositionValid()) - RelocateToHomebind(); - } - } + // There are no transports on instances + instanceId = 0; - if (transGUID != 0) - { - m_movementInfo.t_x = fields[20].GetFloat(); - m_movementInfo.t_y = fields[21].GetFloat(); - m_movementInfo.t_z = fields[22].GetFloat(); - m_movementInfo.t_o = fields[23].GetFloat(); + m_movementInfo.t_x = fields[27].GetFloat(); + m_movementInfo.t_y = fields[28].GetFloat(); + m_movementInfo.t_z = fields[29].GetFloat(); + m_movementInfo.t_o = fields[30].GetFloat(); if( !Trinity::IsValidMapCoord( GetPositionX()+m_movementInfo.t_x,GetPositionY()+m_movementInfo.t_y, @@ -14508,7 +14700,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) guid,GetPositionX()+m_movementInfo.t_x,GetPositionY()+m_movementInfo.t_y, GetPositionZ()+m_movementInfo.t_z,GetOrientation()+m_movementInfo.t_o); - RelocateToHomebind(); + RelocateToHomebind(mapId); m_movementInfo.t_x = 0.0f; m_movementInfo.t_y = 0.0f; @@ -14517,112 +14709,146 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) transGUID = 0; } - } - - if (transGUID != 0) - { - for (MapManager::TransportSet::iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().m_Transports.end(); ++iter) + else { - if( (*iter)->GetGUIDLow() == transGUID) + for (MapManager::TransportSet::iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().m_Transports.end(); ++iter) { - MapEntry const* transMapEntry = sMapStore.LookupEntry((*iter)->GetMapId()); - // client without expansion support - if(GetSession()->Expansion() < transMapEntry->Expansion()) + if( (*iter)->GetGUIDLow() == transGUID) { - sLog.outDebug("Player %s using client without required expansion tried login at transport at non accessible map %u", GetName(), (*iter)->GetMapId()); + m_transport = *iter; + m_transport->AddPassenger(this); + mapId = (m_transport->GetMapId()); break; } - - m_transport = *iter; - m_transport->AddPassenger(this); - SetMapId(m_transport->GetMapId()); - break; } - } - - if(!m_transport) - { - sLog.outError("Player (guidlow %d) have problems with transport guid (%u). Teleport to default race/class locations.", - guid,transGUID); + if(!m_transport) + { + sLog.outError("Player (guidlow %d) have problems with transport guid (%u). Teleport to default race/class locations.", + guid,transGUID); - RelocateToHomebind(); + RelocateToHomebind(mapId); - m_movementInfo.t_x = 0.0f; - m_movementInfo.t_y = 0.0f; - m_movementInfo.t_z = 0.0f; - m_movementInfo.t_o = 0.0f; + m_movementInfo.t_x = 0.0f; + m_movementInfo.t_y = 0.0f; + m_movementInfo.t_z = 0.0f; + m_movementInfo.t_o = 0.0f; - transGUID = 0; + transGUID = 0; + } } } - else // not transport case + else if (!taxi_nodes.empty()) // Taxi Flight path loaded from db { - MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId()); - // client without expansion support - if(GetSession()->Expansion() < mapEntry->Expansion()) + // There are no flightpaths in instances + instanceId = 0; + + if(!m_taxi.LoadTaxiDestinationsFromString(taxi_nodes,GetTeam())) + { + // problems with taxi path loading + TaxiNodesEntry const* nodeEntry = NULL; + if(uint32 node_id = m_taxi.GetTaxiSource()) + nodeEntry = sTaxiNodesStore.LookupEntry(node_id); + + if(!nodeEntry) // don't know taxi start node, to homebind + { + sLog.outError("Character %u have wrong data in taxi destination list, teleport to homebind.",GetGUIDLow()); + RelocateToHomebind(mapId); + } + else // have start node, to it + { + sLog.outError("Character %u have too short taxi destination list, teleport to original node.",GetGUIDLow()); + mapId = nodeEntry->map_id; + Relocate(nodeEntry->x, nodeEntry->y, nodeEntry->z,0.0f); + } + } + // Taxi path loading succesfull + else if(uint32 node_id = m_taxi.GetTaxiSource()) { - sLog.outDebug("Player %s using client without required expansion tried login at non accessible map %u", GetName(), GetMapId()); - RelocateToHomebind(); + // save source node as recall coord to prevent recall and fall from sky + TaxiNodesEntry const* nodeEntry = sTaxiNodesStore.LookupEntry(node_id); + assert(nodeEntry); // checked in m_taxi.LoadTaxiDestinationsFromString + Relocate(nodeEntry->x,nodeEntry->y,nodeEntry->z,0); + mapId = nodeEntry->map_id; + // flight will started later } } + // Map could be changed before + mapEntry = sMapStore.LookupEntry(mapId); + // client without expansion support + if(GetSession()->Expansion() < mapEntry->Expansion()) + { + sLog.outDebug("Player %s using client without required expansion tried login at non accessible map %u", GetName(), mapId); + RelocateToHomebind(mapId); + instanceId = 0; + } + + // fix crash (because of if(Map *map = _FindMap(instanceId)) in MapInstanced::CreateInstance) + if(instanceId) + if(InstanceSave * save = GetInstanceSave(mapId)) + if(save->GetInstanceId() != instanceId) + instanceId = 0; // NOW player must have valid map // load the player's map here if it's not already loaded - Map *map = GetMap(); + Map *map = MapManager::Instance().CreateMap(mapId, this, instanceId); if (!map) { - AreaTrigger const* at = objmgr.GetGoBackTrigger(GetMapId()); + instanceId = 0; + AreaTrigger const* at = objmgr.GetGoBackTrigger(mapId); if(at) { - SetMapId(at->target_mapId); + sLog.outError("Player (guidlow %d) is teleported to gobacktrigger (Map: %u X: %f Y: %f Z: %f O: %f).",guid,mapId,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); Relocate(at->target_X, at->target_Y, at->target_Z, GetOrientation()); - sLog.outError("Player (guidlow %d) is teleported to gobacktrigger (Map: %u X: %f Y: %f Z: %f O: %f).",guid,GetMapId(),GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); + mapId = at->target_mapId; } else { - RelocateToHomebind(); - sLog.outError("Player (guidlow %d) is teleported to home (Map: %u X: %f Y: %f Z: %f O: %f).",guid,GetMapId(),GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); + sLog.outError("Player (guidlow %d) is teleported to home (Map: %u X: %f Y: %f Z: %f O: %f).",guid,mapId,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); + RelocateToHomebind(mapId); } - map = GetMap(); + map = MapManager::Instance().CreateMap(mapId, this, 0); if(!map) { - sLog.outError("ERROR: Player (guidlow %d) have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",guid,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); - delete result; - return false; - - /*SetMapId(info->mapId); + PlayerInfo const *info = objmgr.GetPlayerInfo(getRace(), getClass()); + mapId = info->mapId; Relocate(info->positionX,info->positionY,info->positionZ,0.0f); - - map = GetMap(); - if(!map) + sLog.outError("ERROR: Player (guidlow %d) have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",guid,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); + map = MapManager::Instance().CreateMap(mapId, this, 0); + if (!map) { - sLog.outError("ERROR: Player (guidlow %d) have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",guid,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); - sLog.outError("CRASH."); - assert(false); - }*/ + sLog.outError("ERROR: Player (guidlow %d) has invalid default map coordinates (X: %f Y: %f Z: %f O: %f). or instance couldn't be created",guid,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); + delete result; + return false; + } } } - // since the player may not be bound to the map yet, make sure subsequent - // 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())) + if(instanceId && !sInstanceSaveManager.GetInstanceSave(instanceId)) { - AreaTrigger const* at = objmgr.GetMapEntranceTrigger(GetMapId()); + AreaTrigger const* at = objmgr.GetMapEntranceTrigger(mapId); 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 area-trigger leading to this map. Thus he can't be ported back to the entrance. This _might_ be an exploit attempt.", GetName(), GetGUIDLow(), GetMapId()); + { + sLog.outError("Player %s(GUID: %u) logged in to a reset instance (map: %u) and there is no area-trigger leading to this map. Thus he can't be ported back to the entrance. This _might_ be an exploit attempt.", GetName(), GetGUIDLow(), mapId); + RelocateToHomebind(mapId); + instanceId = 0; + } } + SetMap(map); + + // randomize first save time in range [CONFIG_INTERVAL_SAVE] around [CONFIG_INTERVAL_SAVE] + // this must help in case next save after mass player load after server startup + m_nextSave = urand(m_nextSave/2,m_nextSave*3/2); + SaveRecallPosition(); time_t now = time(NULL); - time_t logoutTime = time_t(fields[16].GetUInt64()); + time_t logoutTime = time_t(fields[23].GetUInt64()); // since last logout (in seconds) uint64 time_diff = uint64(now - logoutTime); @@ -14637,27 +14863,27 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) uint16 newDrunkenValue = uint16(soberFactor*(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFE)); SetDrunkValue(newDrunkenValue); - m_rest_bonus = fields[15].GetFloat(); + m_rest_bonus = fields[22].GetFloat(); //speed collect rest bonus in offline, in logout, far from tavern, city (section/in hour) float bubble0 = 0.031; //speed collect rest bonus in offline, in logout, in tavern, city (section/in hour) float bubble1 = 0.125; - if((int32)fields[16].GetUInt32() > 0) + if(time_diff > 0) { - float bubble = fields[17].GetUInt32() > 0 + float bubble = fields[24].GetUInt32() > 0 ? bubble1*sWorld.getRate(RATE_REST_OFFLINE_IN_TAVERN_OR_CITY) : bubble0*sWorld.getRate(RATE_REST_OFFLINE_IN_WILDERNESS); SetRestBonus(GetRestBonus()+ time_diff*((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)/72000)*bubble); } - m_cinematic = fields[12].GetUInt32(); - m_Played_time[0]= fields[13].GetUInt32(); - m_Played_time[1]= fields[14].GetUInt32(); + m_cinematic = fields[19].GetUInt32(); + m_Played_time[PLAYED_TIME_TOTAL]= fields[20].GetUInt32(); + m_Played_time[PLAYED_TIME_LEVEL]= fields[21].GetUInt32(); - m_resetTalentsCost = fields[18].GetUInt32(); - m_resetTalentsTime = time_t(fields[19].GetUInt64()); + m_resetTalentsCost = fields[25].GetUInt32(); + m_resetTalentsTime = time_t(fields[26].GetUInt64()); // reserve some flags uint32 old_safe_flags = GetUInt32Value(PLAYER_FLAGS) & ( PLAYER_FLAGS_HIDE_CLOAK | PLAYER_FLAGS_HIDE_HELM ); @@ -14665,30 +14891,28 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM) ) SetUInt32Value(PLAYER_FLAGS, 0 | old_safe_flags); - m_taxi.LoadTaxiMask( fields[11].GetString() ); // must be before InitTaxiNodesForLevel + m_taxi.LoadTaxiMask( fields[18].GetString() ); // must be before InitTaxiNodesForLevel - uint32 extraflags = fields[25].GetUInt32(); + uint32 extraflags = fields[32].GetUInt32(); - m_stableSlots = fields[26].GetUInt32(); + m_stableSlots = fields[33].GetUInt32(); if(m_stableSlots > MAX_PET_STABLES) { sLog.outError("Player can have not more %u stable slots, but have in DB %u",MAX_PET_STABLES,uint32(m_stableSlots)); m_stableSlots = MAX_PET_STABLES; } - m_atLoginFlags = fields[27].GetUInt32(); + m_atLoginFlags = fields[34].GetUInt32(); // Honor system // Update Honor kills data m_lastHonorUpdateTime = logoutTime; UpdateHonorFields(); - m_deathExpireTime = (time_t)fields[30].GetUInt64(); + m_deathExpireTime = (time_t)fields[37].GetUInt64(); if(m_deathExpireTime > now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP) m_deathExpireTime = now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP-1; - std::string taxi_nodes = fields[31].GetCppString(); - delete result; // clear channel spell data (if saved at channel spell casting) @@ -14774,42 +14998,6 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) SetUInt32Value(PLAYER_CHOSEN_TITLE, 0); } - // Not finish taxi flight path - if(!m_taxi.LoadTaxiDestinationsFromString(taxi_nodes,GetTeam())) - { - // problems with taxi path loading - TaxiNodesEntry const* nodeEntry = NULL; - if(uint32 node_id = m_taxi.GetTaxiSource()) - nodeEntry = sTaxiNodesStore.LookupEntry(node_id); - - if(!nodeEntry) // don't know taxi start node, to homebind - { - sLog.outError("Character %u have wrong data in taxi destination list, teleport to homebind.",GetGUIDLow()); - RelocateToHomebind(); - SaveRecallPosition(); // save as recall also to prevent recall and fall from sky - } - else // have start node, to it - { - sLog.outError("Character %u have too short taxi destination list, teleport to original node.",GetGUIDLow()); - SetMapId(nodeEntry->map_id); - Relocate(nodeEntry->x, nodeEntry->y, nodeEntry->z,0.0f); - SaveRecallPosition(); // save as recall also to prevent recall and fall from sky - } - m_taxi.ClearTaxiDestinations(); - } - else if(uint32 node_id = m_taxi.GetTaxiSource()) - { - // save source node as recall coord to prevent recall and fall from sky - TaxiNodesEntry const* nodeEntry = sTaxiNodesStore.LookupEntry(node_id); - assert(nodeEntry); // checked in m_taxi.LoadTaxiDestinationsFromString - m_recallMap = nodeEntry->map_id; - m_recallX = nodeEntry->x; - m_recallY = nodeEntry->y; - m_recallZ = nodeEntry->z; - - // flight will started later - } - // has to be called after last Relocate() in Player::LoadFromDB SetFallInformation(0, GetPositionZ()); @@ -14928,7 +15116,7 @@ void Player::_LoadActions(QueryResult *result) { m_actionButtons.clear(); - //QueryResult *result = CharacterDatabase.PQuery("SELECT button,action,type,misc FROM character_action WHERE guid = '%u' ORDER BY button",GetGUIDLow()); + //QueryResult *result = CharacterDatabase.PQuery("SELECT button,action,type FROM character_action WHERE guid = '%u' ORDER BY button",GetGUIDLow()); if(result) { @@ -14937,9 +15125,11 @@ void Player::_LoadActions(QueryResult *result) Field *fields = result->Fetch(); uint8 button = fields[0].GetUInt8(); + uint32 action = fields[1].GetUInt32(); + uint8 type = fields[2].GetUInt8(); - if(addActionButton(button, fields[1].GetUInt16(), fields[2].GetUInt8(), fields[3].GetUInt8())) - m_actionButtons[button].uState = ACTIONBUTTON_UNCHANGED; + if(ActionButton* ab = addActionButton(button, action, type)) + ab->uState = ACTIONBUTTON_UNCHANGED; else { sLog.outError( " ...at loading, and will deleted in DB also"); @@ -15012,7 +15202,7 @@ void Player::_LoadAuras(QueryResult *result, uint32 timediff) delete result; } - if(m_class == CLASS_WARRIOR) + if(getClass() == CLASS_WARRIOR && !HasAuraType(SPELL_AURA_MOD_SHAPESHIFT)) CastSpell(this,SPELL_ID_PASSIVE_BATTLE_STANCE,true); } @@ -15248,7 +15438,7 @@ void Player::_LoadMailedItems(Mail *mail) if(!proto) { - sLog.outError( "Player %u have unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.", GetGUIDLow(), item_guid_low, item_template,mail->messageID); + sLog.outError( "Player %u has unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.", GetGUIDLow(), item_guid_low, item_template,mail->messageID); CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low); CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guid_low); continue; @@ -15508,7 +15698,7 @@ void Player::_LoadSpells(QueryResult *result) { Field *fields = result->Fetch(); - addSpell(fields[0].GetUInt16(), fields[1].GetBool(), false, false, fields[2].GetBool()); + addSpell(fields[0].GetUInt32(), fields[1].GetBool(), false, false, fields[2].GetBool()); } while( result->NextRow() ); @@ -15595,6 +15785,19 @@ InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, uint8 difficulty) return NULL; } +InstanceSave * Player::GetInstanceSave(uint32 mapid) +{ + InstancePlayerBind *pBind = GetBoundInstance(mapid, GetDifficulty()); + InstanceSave *pSave = pBind ? pBind->save : NULL; + if(!pBind || !pBind->perm) + { + if(Group *group = GetGroup()) + if(InstanceGroupBind *groupBind = group->GetBoundInstance(mapid, GetDifficulty())) + pSave = groupBind->save; + } + return pSave; +} + void Player::UnbindInstance(uint32 mapid, uint8 difficulty, bool unload) { BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid); @@ -15764,8 +15967,7 @@ bool Player::Satisfy(AccessRequirement const *ar, uint32 target_map, bool report { if(ar->levelMin && getLevel() < ar->levelMin) LevelMin = ar->levelMin; - else if(ar->heroicLevelMin && GetDifficulty() == DIFFICULTY_HEROIC - && getLevel() < ar->heroicLevelMin) + if(ar->heroicLevelMin && GetDifficulty() == DIFFICULTY_HEROIC && getLevel() < ar->heroicLevelMin) LevelMin = ar->heroicLevelMin; if(ar->levelMax && getLevel() > ar->levelMax) LevelMax = ar->levelMax; @@ -15807,7 +16009,7 @@ bool Player::Satisfy(AccessRequirement const *ar, uint32 target_map, bool report if(report) { if(missingItem) - GetSession()->SendAreaTriggerMessage(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED_AND_ITEM), ar->levelMin, objmgr.GetItemPrototype(missingItem)->Name1); + GetSession()->SendAreaTriggerMessage(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED_AND_ITEM), LevelMin, objmgr.GetItemPrototype(missingItem)->Name1); else if(missingKey) SendTransferAborted(target_map, TRANSFER_ABORT_DIFFICULTY); else if(missingHeroicQuest) @@ -15882,30 +16084,19 @@ void Player::SaveToDB() // delay auto save at any saves (manual, in code, or autosave) m_nextSave = sWorld.getConfig(CONFIG_INTERVAL_SAVE); + //lets allow only players in world to be saved + if(IsBeingTeleportedFar()) + { + ScheduleDelayedOperation(DELAYED_SAVE_PLAYER); + return; + } + // first save/honor gain after midnight will also update the player's honor fields UpdateHonorFields(); - uint32 is_save_resting = HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0; - //save, far from tavern/city - //save, but in tavern/city sLog.outDebug("The value of player %s at save: ", m_name.c_str()); outDebugValues(); - // save state (after auras removing), if aura remove some flags then it must set it back by self) - uint32 tmp_bytes = GetUInt32Value(UNIT_FIELD_BYTES_1); - uint32 tmp_bytes2 = GetUInt32Value(UNIT_FIELD_BYTES_2); - uint32 tmp_flags = GetUInt32Value(UNIT_FIELD_FLAGS); - uint32 tmp_pflags = GetUInt32Value(PLAYER_FLAGS); - uint32 tmp_displayid = GetDisplayId(); - - // Set player sit state to standing on save, also stealth and shifted form - SetByteValue(UNIT_FIELD_BYTES_1, 0, UNIT_STAND_STATE_STAND); - SetByteValue(UNIT_FIELD_BYTES_2, 3, 0); // shapeshift - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); - SetDisplayId(GetNativeDisplayId()); - - bool inworld = IsInWorld(); - CharacterDatabase.BeginTransaction(); CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'",GetGUIDLow()); @@ -15914,7 +16105,7 @@ void Player::SaveToDB() CharacterDatabase.escape_string(sql_name); std::ostringstream ss; - ss << "INSERT INTO characters (guid,account,name,race,class," + ss << "INSERT INTO characters (guid,account,name,race,class,gender,level,xp,money,playerBytes,playerBytes2,playerFlags," "map, instance_id, dungeon_difficulty, position_x, position_y, position_z, orientation, data, " "taximask, online, cinematic, " "totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, " @@ -15923,8 +16114,15 @@ void Player::SaveToDB() << GetGUIDLow() << ", " << GetSession()->GetAccountId() << ", '" << sql_name << "', " - << m_race << ", " - << m_class << ", "; + << (uint32)getRace() << ", " + << (uint32)getClass() << ", " + << (uint32)getGender() << ", " + << getLevel() << ", " + << GetUInt32Value(PLAYER_XP) << ", " + << GetMoney() << ", " + << GetUInt32Value(PLAYER_BYTES) << ", " + << GetUInt32Value(PLAYER_BYTES_2) << ", " + << GetUInt32Value(PLAYER_FLAGS) << ", "; if(!IsBeingTeleported()) { @@ -15955,69 +16153,48 @@ void Player::SaveToDB() ss << "', "; - ss << m_taxi; // string with TaxiMaskSize numbers + ss << m_taxi << ", "; // string with TaxiMaskSize numbers - ss << ", "; - ss << (inworld ? 1 : 0); + ss << (IsInWorld() ? 1 : 0) << ", "; - ss << ", "; - ss << m_cinematic; + ss << m_cinematic << ", "; - ss << ", "; - ss << m_Played_time[0]; - ss << ", "; - ss << m_Played_time[1]; + ss << m_Played_time[PLAYED_TIME_TOTAL] << ", "; + ss << m_Played_time[PLAYED_TIME_LEVEL] << ", "; - ss << ", "; - ss << finiteAlways(m_rest_bonus); - ss << ", "; - ss << (uint64)time(NULL); - ss << ", "; - ss << is_save_resting; - ss << ", "; - ss << m_resetTalentsCost; - ss << ", "; - ss << (uint64)m_resetTalentsTime; + ss << finiteAlways(m_rest_bonus) << ", "; + ss << (uint64)time(NULL) << ", "; + ss << (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0) << ", "; + //save, far from tavern/city + //save, but in tavern/city + ss << m_resetTalentsCost << ", "; + ss << (uint64)m_resetTalentsTime << ", "; - ss << ", "; - ss << finiteAlways(m_movementInfo.t_x); - ss << ", "; - ss << finiteAlways(m_movementInfo.t_y); - ss << ", "; - ss << finiteAlways(m_movementInfo.t_z); - ss << ", "; - ss << finiteAlways(m_movementInfo.t_o); - ss << ", "; + ss << finiteAlways(m_movementInfo.t_x) << ", "; + ss << finiteAlways(m_movementInfo.t_y) << ", "; + ss << finiteAlways(m_movementInfo.t_z) << ", "; + ss << finiteAlways(m_movementInfo.t_o) << ", "; if (m_transport) ss << m_transport->GetGUIDLow(); else ss << "0"; - ss << ", "; - ss << m_ExtraFlags; - ss << ", "; - ss << uint32(m_stableSlots); // to prevent save uint8 as char + ss << m_ExtraFlags << ", "; - ss << ", "; - ss << uint32(m_atLoginFlags & ((1<<AT_LOAD_PET_FLAGS) -1)); + ss << uint32(m_stableSlots) << ", "; // to prevent save uint8 as char - ss << ", "; - ss << GetZoneId(); + ss << uint32(m_atLoginFlags) << ", "; - ss << ", "; - ss << (uint64)m_deathExpireTime; + ss << GetZoneId() << ", "; - ss << ", '"; - ss << m_taxi.SaveTaxiDestinationsToString(); + ss << (uint64)m_deathExpireTime << ", '"; - ss << "', '0', "; - ss << GetSession()->GetLatency(); - ss << ", "; - ss << GetBattleGroundId(); - ss << ", "; - ss << GetBGTeam(); - ss << ", "; + ss << m_taxi.SaveTaxiDestinationsToString() << "', "; + ss << "'0', "; // arena_pending_points + ss << GetSession()->GetLatency() << ", "; + ss << GetBattleGroundId() << ", "; + ss << GetBGTeam() << ", "; ss << m_bgEntryPoint.mapid << ", " << finiteAlways(m_bgEntryPoint.coord_x) << ", " << finiteAlways(m_bgEntryPoint.coord_y) << ", " @@ -16044,13 +16221,6 @@ void Player::SaveToDB() CharacterDatabase.CommitTransaction(); - // restore state (before aura apply, if aura remove flag then aura must set it ack by self) - SetDisplayId(tmp_displayid); - SetUInt32Value(UNIT_FIELD_BYTES_1, tmp_bytes); - SetUInt32Value(UNIT_FIELD_BYTES_2, tmp_bytes2); - SetUInt32Value(UNIT_FIELD_FLAGS, tmp_flags); - SetUInt32Value(PLAYER_FLAGS, tmp_pflags); - // save pet (hunter pet level and experience and all type pets health/mana). if(Pet* pet = GetPet()) pet->SavePetToDB(PET_SAVE_AS_CURRENT); @@ -16060,8 +16230,12 @@ void Player::SaveToDB() void Player::SaveInventoryAndGoldToDB() { _SaveInventory(); - //money is in data field - SaveDataFieldToDB(); + SaveGoldToDB(); +} + +void Player::SaveGoldToDB() +{ + CharacterDatabase.PExecute("UPDATE characters SET money = '%u' WHERE guid = '%u'", GetMoney(), GetGUIDLow()); } void Player::_SaveActions() @@ -16071,14 +16245,14 @@ void Player::_SaveActions() switch (itr->second.uState) { case ACTIONBUTTON_NEW: - CharacterDatabase.PExecute("INSERT INTO character_action (guid,button,action,type,misc) VALUES ('%u', '%u', '%u', '%u', '%u')", - GetGUIDLow(), (uint32)itr->first, (uint32)itr->second.action, (uint32)itr->second.type, (uint32)itr->second.misc ); + CharacterDatabase.PExecute("INSERT INTO character_action (guid,button,action,type) VALUES ('%u', '%u', '%u', '%u')", + GetGUIDLow(), (uint32)itr->first, (uint32)itr->second.GetAction(), (uint32)itr->second.GetType() ); itr->second.uState = ACTIONBUTTON_UNCHANGED; ++itr; break; case ACTIONBUTTON_CHANGED: - CharacterDatabase.PExecute("UPDATE character_action SET action = '%u', type = '%u', misc= '%u' WHERE guid= '%u' AND button= '%u' ", - (uint32)itr->second.action, (uint32)itr->second.type, (uint32)itr->second.misc, GetGUIDLow(), (uint32)itr->first ); + CharacterDatabase.PExecute("UPDATE character_action SET action = '%u', type = '%u' WHERE guid= '%u' AND button= '%u' ", + (uint32)itr->second.GetAction(), (uint32)itr->second.GetType(), GetGUIDLow(), (uint32)itr->first ); itr->second.uState = ACTIONBUTTON_UNCHANGED; ++itr; break; @@ -16089,7 +16263,7 @@ void Player::_SaveActions() default: ++itr; break; - }; + } } } @@ -16103,13 +16277,11 @@ void Player::_SaveAuras() // skip: // area auras or single cast auras casted by other unit // passive auras and stances - if (itr->second->IsPassive() - || itr->second->IsAuraType(SPELL_AURA_MOD_SHAPESHIFT) - || itr->second->IsRemovedOnShapeLost()) + if (itr->second->IsPassive()) continue; - bool isCaster = itr->second->GetCasterGUID() == GetGUID(); - if (!isCaster) - if (itr->second->IsSingleTarget() + + if (itr->second->GetCasterGUID() != GetGUID()) + if (IsSingleTargetSpell(itr->second->GetSpellProto()) || itr->second->IsAreaAura()) continue; @@ -16330,7 +16502,7 @@ void Player::outDebugValues() const sLog.outDebug("HP is: \t\t\t%u\t\tMP is: \t\t\t%u",GetMaxHealth(), GetMaxPower(POWER_MANA)); sLog.outDebug("AGILITY is: \t\t%f\t\tSTRENGTH is: \t\t%f",GetStat(STAT_AGILITY), GetStat(STAT_STRENGTH)); sLog.outDebug("INTELLECT is: \t\t%f\t\tSPIRIT is: \t\t%f",GetStat(STAT_INTELLECT), GetStat(STAT_SPIRIT)); - sLog.outDebug("STAMINA is: \t\t%f\t\tSPIRIT is: \t\t%f",GetStat(STAT_STAMINA), GetStat(STAT_SPIRIT)); + sLog.outDebug("STAMINA is: \t\t%f",GetStat(STAT_STAMINA)); sLog.outDebug("Armor is: \t\t%u\t\tBlock is: \t\t%f",GetArmor(), GetFloatValue(PLAYER_BLOCK_PERCENTAGE)); sLog.outDebug("HolyRes is: \t\t%u\t\tFireRes is: \t\t%u",GetResistance(SPELL_SCHOOL_HOLY), GetResistance(SPELL_SCHOOL_FIRE)); sLog.outDebug("NatureRes is: \t\t%u\t\tFrostRes is: \t\t%u",GetResistance(SPELL_SCHOOL_NATURE), GetResistance(SPELL_SCHOOL_FROST)); @@ -16465,38 +16637,20 @@ void Player::SetFloatValueInDB(uint16 index, float value, uint64 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) + // 0 + QueryResult* result = CharacterDatabase.PQuery("SELECT playerBytes2 FROM characters WHERE guid = '%u'", GUID_LOPART(guid)); + if(!result) 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))); + Field* fields = result->Fetch(); - uint32 player_bytes2 = GetUInt32ValueFromArray(tokens, PLAYER_BYTES_2); + uint32 player_bytes2 = fields[0].GetUInt32(); 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); + CharacterDatabase.PExecute("UPDATE characters SET gender = '%u', playerBytes = '%u', playerBytes2 = '%u' WHERE guid = '%u'", gender, skin | (face << 8) | (hairStyle << 16) | (hairColor << 24), player_bytes2, GUID_LOPART(guid)); - SaveValuesArrayInDB(tokens, guid); + delete result; } void Player::SendAttackSwingDeadTarget() @@ -16523,10 +16677,10 @@ void Player::SendAttackSwingBadFacingAttack() GetSession()->SendPacket( &data ); } -void Player::SendAutoRepeatCancel() +void Player::SendAutoRepeatCancel(Unit *target) { - WorldPacket data(SMSG_CANCEL_AUTO_REPEAT, GetPackGUID().size()); - data.append(GetPackGUID()); // may be it's target guid + WorldPacket data(SMSG_CANCEL_AUTO_REPEAT, target->GetPackGUID().size()); + data.append(target->GetPackGUID()); // may be it's target guid GetSession()->SendPacket( &data ); } @@ -16751,7 +16905,6 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent) SetMinion(pet, false); - pet->CleanupsBeforeDelete(); pet->AddObjectToRemoveList(); pet->m_removed = true; @@ -16784,8 +16937,14 @@ void Player::StopCastingCharm() if(GetCharmGUID()) { - sLog.outCrash("Player %s is not able to uncharm unit (Entry: %u, Type: %u)", GetName(), charm->GetEntry(), charm->GetTypeId()); - assert(false); + sLog.outCrash("Player %s (GUID: " UI64FMTD " is not able to uncharm unit (GUID: " UI64FMTD " Entry: %u, Type: %u)", GetName(), GetGUID(), GetCharmGUID(), charm->GetEntry(), charm->GetTypeId()); + if(charm->GetCharmerGUID()) + { + sLog.outCrash("Charmed unit has charmer guid " UI64FMTD, charm->GetCharmerGUID()); + assert(false); + } + else + SetCharm(charm, false); } } @@ -16911,8 +17070,7 @@ void Player::PetSpellInitialize() if(itr->second.state == PETSPELL_REMOVED) continue; - data << uint16(itr->first); - data << uint16(itr->second.active); // pet spell active state isn't boolean + data << uint32(MAKE_UNIT_ACTION_BUTTON(itr->first,itr->second.active)); ++addlist; } } @@ -17037,7 +17195,7 @@ void Player::CharmSpellInitialize() { for(uint32 i = 0; i < MAX_SPELL_CHARM; ++i) { - if(charmInfo->GetCharmSpell(i)->spellId) + if(charmInfo->GetCharmSpell(i)->GetAction()) ++addlist; } } @@ -17062,11 +17220,8 @@ void Player::CharmSpellInitialize() for(uint32 i = 0; i < MAX_SPELL_CHARM; ++i) { CharmSpellEntry *cspell = charmInfo->GetCharmSpell(i); - if(cspell->spellId) - { - data << uint16(cspell->spellId); - data << uint16(cspell->active); - } + if(cspell->GetAction()) + data << uint32(cspell->packedData); } } @@ -17162,6 +17317,10 @@ void Player::RestoreSpellMods(Spell * spell) else mod->charges++; + // Do not set more spellmods than avalible + if (mod->ownerAura->GetAuraCharges() < mod->charges) + mod->charges = mod->ownerAura->GetAuraCharges(); + // Skip this check for now - aura charges may change due to various reason // TODO: trac these changes correctly //assert (mod->ownerAura->GetAuraCharges() <= mod->charges); @@ -17189,11 +17348,7 @@ void Player::RemoveSpellMods(Spell * spell) checkedSpells.find(aur->GetParentAura()) != checkedSpells.end()) continue; - flag96 const * mask = spellmgr.GetSpellAffect(aur->GetId(), aur->GetEffIndex()); - if (!mask) - mask = &spellInfo->EffectSpellClassMask[aur->GetEffIndex()]; - - if (spell->m_spellInfo->SpellFamilyFlags & *mask) + if (spell->m_spellInfo->SpellFamilyFlags & spellInfo->EffectSpellClassMask[aur->GetEffIndex()]) { checkedSpells.insert(aur->GetParentAura()); spell->m_appliedMods.erase(aur->GetParentAura()); @@ -17248,6 +17403,9 @@ void Player::SetSpellModTakingSpell(Spell * spell, bool apply) if (!spell || (m_spellModTakingSpell && m_spellModTakingSpell != spell)) return; + if (apply && spell->getState() == SPELL_STATE_FINISHED) + return; + if (apply) m_spellModTakingSpell = spell; else @@ -17360,19 +17518,38 @@ void Player::HandleStealthedUnitsDetection() Trinity::UnitListSearcher<Trinity::AnyStealthedCheck > searcher(this, stealthedUnits, u_check); VisitNearbyObject(World::GetMaxVisibleDistance(), searcher); - for (std::list<Unit*>::iterator i = stealthedUnits.begin(); i != stealthedUnits.end(); ++i) + for (std::list<Unit*>::const_iterator i = stealthedUnits.begin(); i != stealthedUnits.end(); ++i) { - if (!HaveAtClient(*i) && canSeeOrDetect(*i, true)) + if((*i)==this) + continue; + + bool hasAtClient = HaveAtClient((*i)); + bool hasDetected = canSeeOrDetect(*i, true); + + if (hasDetected) { - (*i)->SendUpdateToPlayer(this); - m_clientGUIDs.insert((*i)->GetGUID()); + if(!hasAtClient) + { + (*i)->SendUpdateToPlayer(this); + m_clientGUIDs.insert((*i)->GetGUID()); - #ifdef TRINITY_DEBUG - if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0) - sLog.outDebug("Object %u (Type: %u) is detected in stealth by player %u. Distance = %f",(*i)->GetGUIDLow(),(*i)->GetTypeId(),GetGUIDLow(),GetDistance(*i)); - #endif + #ifdef MANGOS_DEBUG + if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0) + sLog.outDebug("Object %u (Type: %u) is detected in stealth by player %u. Distance = %f",(*i)->GetGUIDLow(),(*i)->GetTypeId(),GetGUIDLow(),GetDistance(*i)); + #endif - SendInitialVisiblePackets(*i); + // target aura duration for caster show only if target exist at caster client + // send data at target visibility change (adding to client) + SendInitialVisiblePackets(*i); + } + } + else + { + if(hasAtClient) + { + (*i)->DestroyForPlayer(this); + m_clientGUIDs.erase((*i)->GetGUID()); + } } } } @@ -17382,8 +17559,8 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc if(nodes.size() < 2) return false; - // not let cheating with start flight in time of logout process || if casting not finished || while in combat || if not use Spell's with EffectSendTaxi - if(GetSession()->isLogingOut() || isInCombat()) + // not let cheating with start flight in time of logout process || while in combat || has type state: stunned || has type state: root + if(GetSession()->isLogingOut() || isInCombat() || hasUnitState(UNIT_STAT_STUNNED) || hasUnitState(UNIT_STAT_ROOT)) { WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4); data << uint32(ERR_TAXIPLAYERBUSY); @@ -17585,6 +17762,14 @@ bool Player::ActivateTaxiPathTo( uint32 taxi_path_id, uint32 spellid /*= 0*/ ) return ActivateTaxiPathTo(nodes,NULL,spellid); } +void Player::CleanupAfterTaxiFlight() +{ + m_taxi.ClearTaxiDestinations(); // not destinations, clear source node + Unmount(); + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); + getHostilRefManager().setOnlineOfflineState(true); +} + void Player::ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs ) { // last check 2.0.10 @@ -17666,17 +17851,47 @@ void Player::InitDataForForm(bool reapplyMods) UpdateAttackPowerAndDamage(true); } +void Player::InitDisplayIds() +{ + PlayerInfo const *info = objmgr.GetPlayerInfo(getRace(), getClass()); + if(!info) + { + sLog.outError("Player %u has incorrect race/class pair. Can't init display ids.", GetGUIDLow()); + return; + } + + uint8 gender = getGender(); + switch(gender) + { + case GENDER_FEMALE: + SetDisplayId(info->displayId_f ); + SetNativeDisplayId(info->displayId_f ); + break; + case GENDER_MALE: + SetDisplayId(info->displayId_m ); + SetNativeDisplayId(info->displayId_m ); + break; + default: + sLog.outError("Invalid gender %u for player",gender); + return; + } +} + // Return true is the bought item has a max count to force refresh of window by caller -bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint64 bagguid, uint8 slot) +bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint8 bag, uint8 slot) { // cheating attempt - if(count < 1) count = 1; + if (count < 1) count = 1; - if(!isAlive()) + // cheating attempt + if(slot > MAX_BAG_SIZE && slot !=NULL_SLOT) + return false; + + if (!isAlive()) return false; ItemPrototype const *pProto = objmgr.GetItemPrototype( item ); - if( !pProto ) + if (!pProto) { SendBuyError( BUY_ERR_CANT_FIND_ITEM, NULL, item, 0); return false; @@ -17698,7 +17913,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint } size_t vendor_slot = vItems->FindItemSlot(item); - if(vendor_slot >= vItems->GetItemCount()) + if (vendor_slot >= vItems->GetItemCount()) { SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0); return false; @@ -17707,39 +17922,39 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint VendorItem const* crItem = vItems->m_items[vendor_slot]; // check current item amount if it limited - if( crItem->maxcount != 0 ) + if (crItem->maxcount != 0) { - if(pCreature->GetVendorItemCurrentCount(crItem) < pProto->BuyCount * count ) + if (pCreature->GetVendorItemCurrentCount(crItem) < pProto->BuyCount * count ) { SendBuyError( BUY_ERR_ITEM_ALREADY_SOLD, pCreature, item, 0); return false; } } - if( uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank) + if (uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank) { SendBuyError( BUY_ERR_REPUTATION_REQUIRE, pCreature, item, 0); return false; } - if(crItem->ExtendedCost) + if (crItem->ExtendedCost) { ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost); - if(!iece) + if (!iece) { sLog.outError("Item %u have wrong ExtendedCost field value %u", pProto->ItemId, crItem->ExtendedCost); return false; } // honor points price - if(GetHonorPoints() < (iece->reqhonorpoints * count)) + if (GetHonorPoints() < (iece->reqhonorpoints * count)) { SendEquipError(EQUIP_ERR_NOT_ENOUGH_HONOR_POINTS, NULL, NULL); return false; } // arena points price - if(GetArenaPoints() < (iece->reqarenapoints * count)) + if (GetArenaPoints() < (iece->reqarenapoints * count)) { SendEquipError(EQUIP_ERR_NOT_ENOUGH_ARENA_POINTS, NULL, NULL); return false; @@ -17769,63 +17984,38 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint // reputation discount price = uint32(floor(price * GetReputationPriceDiscount(pCreature))); - if( GetMoney() < price ) + if (GetMoney() < price) { SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, item, 0); return false; } - uint8 bag = 0; // init for case invalid bagGUID - - if (bagguid != NULL_BAG && slot != NULL_SLOT) - { - if( bagguid == GetGUID() ) - { - bag = INVENTORY_SLOT_BAG_0; - } - else - { - for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END;i++) - { - if( Bag *pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0,i) ) - { - if( bagguid == pBag->GetGUID() ) - { - if(slot < pBag->GetBagSlot() && !pBag->GetItemByPos(slot)) - bag = i; - break; - } - } - } - } - } - - if( IsInventoryPos( bag, slot ) || (bagguid == NULL_BAG && slot == NULL_SLOT) ) + if ((bag == NULL_BAG && slot == NULL_SLOT) || IsInventoryPos(bag, slot)) { ItemPosCountVec dest; uint8 msg = CanStoreNewItem( bag, slot, dest, item, pProto->BuyCount * count ); - if( msg != EQUIP_ERR_OK ) + if (msg != EQUIP_ERR_OK) { SendEquipError( msg, NULL, NULL ); return false; } ModifyMoney( -(int32)price ); - if(crItem->ExtendedCost) // case for new honor system + if (crItem->ExtendedCost) // case for new honor system { ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost); - if(iece->reqhonorpoints) + if (iece->reqhonorpoints) ModifyHonorPoints( - int32(iece->reqhonorpoints * count)); - if(iece->reqarenapoints) + if (iece->reqarenapoints) ModifyArenaPoints( - int32(iece->reqarenapoints * count)); for (uint8 i = 0; i < 5; ++i) { - if(iece->reqitem[i]) + if (iece->reqitem[i]) DestroyItemCount(iece->reqitem[i], (iece->reqitemcount[i] * count), true); } } - if(Item *it = StoreNewItem( dest, item, true )) + if (Item *it = StoreNewItem( dest, item, true )) { uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,pProto->BuyCount * count); @@ -17839,9 +18029,9 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint SendNewItem(it, pProto->BuyCount*count, true, false, false); } } - else if( IsEquipmentPos( bag, slot ) ) + else if (IsEquipmentPos(bag, slot)) { - if(pProto->BuyCount * count != 1) + if (pProto->BuyCount * count != 1) { SendEquipError( EQUIP_ERR_ITEM_CANT_BE_EQUIPPED, NULL, NULL ); return false; @@ -17849,19 +18039,19 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint uint16 dest; uint8 msg = CanEquipNewItem( slot, dest, item, false ); - if( msg != EQUIP_ERR_OK ) + if (msg != EQUIP_ERR_OK) { SendEquipError( msg, NULL, NULL ); return false; } ModifyMoney( -(int32)price ); - if(crItem->ExtendedCost) // case for new honor system + if (crItem->ExtendedCost) // case for new honor system { ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost); - if(iece->reqhonorpoints) + if (iece->reqhonorpoints) ModifyHonorPoints( - int32(iece->reqhonorpoints)); - if(iece->reqarenapoints) + if (iece->reqarenapoints) ModifyArenaPoints( - int32(iece->reqarenapoints)); for (uint8 i = 0; i < 5; ++i) { @@ -17870,7 +18060,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint } } - if(Item *it = EquipNewItem( dest, item, true )) + if (Item *it = EquipNewItem( dest, item, true )) { uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,pProto->BuyCount * count); @@ -17892,7 +18082,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint return false; } - return crItem->maxcount!=0; + return crItem->maxcount != 0; } uint32 Player::GetMaxPersonalArenaRatingRequirement() @@ -18051,8 +18241,8 @@ void Player::AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 it { // use +MONTH as infinity mark for spell cooldown (will checked as MONTH/2 at save ans skipped) // but not allow ignore until reset or re-login - catrecTime = catrec > 0 ? curTime+MONTH : 0; - recTime = rec > 0 ? curTime+MONTH : catrecTime; + catrecTime = catrec > 0 ? curTime+infinityCooldownDelay : 0; + recTime = rec > 0 ? curTime+infinityCooldownDelay : catrecTime; } else { @@ -18314,7 +18504,16 @@ void Player::LeaveBattleground(bool teleportToEntryPoint) if( bg->isBattleGround() && !isGameMaster() && sWorld.getConfig(CONFIG_BATTLEGROUND_CAST_DESERTER) ) { if( bg->GetStatus() == STATUS_IN_PROGRESS || bg->GetStatus() == STATUS_WAIT_JOIN ) + { + //lets check if player was teleported from BG and schedule delayed Deserter spell cast + if(IsBeingTeleportedFar()) + { + ScheduleDelayedOperation(DELAYED_SPELL_CAST_DESERTER); + return; + } + CastSpell(this, 26013, true); // Deserter + } } } } @@ -18322,7 +18521,7 @@ void Player::LeaveBattleground(bool teleportToEntryPoint) bool Player::CanJoinToBattleground() const { // check Deserter debuff - if(GetDummyAura(26013)) + if(HasAura(26013)) return false; return true; @@ -18558,7 +18757,7 @@ void Player::UpdateVisibilityOf(WorldObject* target) { if(HaveAtClient(target)) { - if(!target->isVisibleForInState(this,true)) + if(!target->isVisibleForInState(this, true)) { target->DestroyForPlayer(this); m_clientGUIDs.erase(target->GetGUID()); @@ -18656,17 +18855,19 @@ void Player::SendComboPoints() } } -void Player::AddComboPoints(Unit* target, int8 count) +void Player::AddComboPoints(Unit* target, int8 count, Spell * spell) { if(!count) return; + int8 * comboPoints = spell ? &spell->m_comboPointGain : &m_comboPoints; + // without combo points lost (duration checked in aura) RemoveAurasByType(SPELL_AURA_RETAIN_COMBO_POINTS); if(target->GetGUID() == m_comboTarget) { - m_comboPoints += count; + *comboPoints += count; } else { @@ -18675,13 +18876,26 @@ void Player::AddComboPoints(Unit* target, int8 count) target2->RemoveComboPointHolder(GetGUIDLow()); m_comboTarget = target->GetGUID(); - m_comboPoints = count; + *comboPoints = count; target->AddComboPointHolder(GetGUIDLow()); } + if (*comboPoints > 5) *comboPoints = 5; + else if (*comboPoints < 0) *comboPoints = 0; + + if (!spell) + SendComboPoints(); +} + +void Player::GainSpellComboPoints(int8 count) +{ + if(!count) + return; + + m_comboPoints += count; if (m_comboPoints > 5) m_comboPoints = 5; - if (m_comboPoints < 0) m_comboPoints = 0; + else if (m_comboPoints < 0) m_comboPoints = 0; SendComboPoints(); } @@ -18746,11 +18960,6 @@ void Player::SendInitialPacketsBeforeAddToMap() m_reputationMgr.SendInitialReputations(); m_achievementMgr.SendAllAchievementData(); - // update zone - uint32 newzone, newarea; - GetZoneAndAreaId(newzone,newarea); - UpdateZone(newzone,newarea); // also call SendInitWorldStates(); - SendEquipmentSetList(); data.Initialize(SMSG_LOGIN_SETTIMESPEED, 4 + 4 + 4); @@ -18762,6 +18971,11 @@ void Player::SendInitialPacketsBeforeAddToMap() void Player::SendInitialPacketsAfterAddToMap() { + // update zone + uint32 newzone, newarea; + GetZoneAndAreaId(newzone,newarea); + UpdateZone(newzone,newarea); // also call SendInitWorldStates(); + WorldPacket data(SMSG_TIME_SYNC_REQ, 4); // new 2.0.x, enable movement data << uint32(0x00000000); // on blizz it increments periodically GetSession()->SendPacket(&data); @@ -18883,17 +19097,14 @@ void Player::resetSpells() { // not need after this call if(HasAtLoginFlag(AT_LOGIN_RESET_SPELLS)) - { - m_atLoginFlags = m_atLoginFlags & ~AT_LOGIN_RESET_SPELLS; - CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login & ~ %u WHERE guid ='%u'", uint32(AT_LOGIN_RESET_SPELLS), GetGUIDLow()); - } + RemoveAtLoginFlag(AT_LOGIN_RESET_SPELLS,true); // make full copy of map (spells removed and marked as deleted at another spell remove // and we can't use original map for safe iterative with visit each spell at loop end PlayerSpellMap smap = GetSpellMap(); for(PlayerSpellMap::const_iterator iter = smap.begin();iter != smap.end(); ++iter) - removeSpell(iter->first); // only iter->first can be accessed, object by iter->second can be deleted already + removeSpell(iter->first,false,false); // only iter->first can be accessed, object by iter->second can be deleted already learnDefaultSpells(); learnQuestRewardedSpells(); @@ -18954,8 +19165,10 @@ void Player::learnQuestRewardedSpells(Quest const* quest) if(!learnedInfo) return; + uint32 profSpell = spellmgr.GetSpellRequired(learned_0); + // specialization - if(learnedInfo->Effect[0]==SPELL_EFFECT_TRADE_SKILL && learnedInfo->Effect[1]==0) + if(learnedInfo->Effect[0]==SPELL_EFFECT_TRADE_SKILL && learnedInfo->Effect[1]==0 && profSpell) { // search other specialization for same prof for(PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) @@ -18972,11 +19185,7 @@ void Player::learnQuestRewardedSpells(Quest const* quest) continue; // compare same chain spells - if(spellmgr.GetFirstSpellInChain(itr->first) != first_spell) - continue; - - // now we have 2 specialization, learn possible only if found is lesser specialization rank - if(!spellmgr.IsHighRankOfSpell(learned_0,itr->first)) + if (spellmgr.GetSpellRequired(itr->first) == profSpell) return; } } @@ -19273,7 +19482,7 @@ void Player::SummonIfPossible(bool agree) if(isInFlight()) { GetMotionMaster()->MovementExpired(); - m_taxi.ClearTaxiDestinations(); + CleanupAfterTaxiFlight(); } // drop flag at summon @@ -19572,7 +19781,7 @@ bool Player::RewardPlayerAndGroupAtKill(Unit* pVictim) { // normal creature (not pet/etc) can be only in !PvP case if(pVictim->GetTypeId()==TYPEID_UNIT) - pGroupGuy->KilledMonster(pVictim->GetEntry(), pVictim->GetGUID()); + pGroupGuy->KilledMonster(((Creature*)pVictim)->GetCreatureInfo(), pVictim->GetGUID()); } } } @@ -19597,7 +19806,7 @@ bool Player::RewardPlayerAndGroupAtKill(Unit* pVictim) // normal creature (not pet/etc) can be only in !PvP case if(pVictim->GetTypeId()==TYPEID_UNIT) - KilledMonster(pVictim->GetEntry(),pVictim->GetGUID()); + KilledMonster(((Creature*)pVictim)->GetCreatureInfo(), pVictim->GetGUID()); } } return xp || honored_kill; @@ -19621,26 +19830,23 @@ void Player::RewardPlayerAndGroupAtEvent(uint32 creature_id, WorldObject* pRewar // quest objectives updated only for alive group member or dead but with not released body if(pGroupGuy->isAlive()|| !pGroupGuy->GetCorpse()) - pGroupGuy->KilledMonster(creature_id, creature_guid); + pGroupGuy->KilledMonsterCredit(creature_id, creature_guid); } } else // if (!pGroup) - KilledMonster(creature_id, creature_guid); + KilledMonsterCredit(creature_id, creature_guid); } bool Player::IsAtGroupRewardDistance(WorldObject const* pRewardSource) const { - if (pRewardSource->IsWithinDistInMap(this,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))) - return true; + const WorldObject* player = GetCorpse(); + if(!player || isAlive()) + player = this; - if (isAlive()) + if(player->GetMapId() != pRewardSource->GetMapId() || player->GetInstanceId() != pRewardSource->GetInstanceId()) return false; - Corpse* corpse = GetCorpse(); - if (!corpse) - return false; - - return pRewardSource->IsWithinDistInMap(corpse,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE)); + return pRewardSource->GetDistance(player) <= sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE); } uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const @@ -19662,6 +19868,14 @@ void Player::ResurectUsingRequestData() if(IS_PLAYER_GUID(m_resurrectGUID)) TeleportTo(m_resurrectMap, m_resurrectX, m_resurrectY, m_resurrectZ, GetOrientation()); + //we cannot resurrect player when we triggered far teleport + //player will be resurrected upon teleportation + if(IsBeingTeleportedFar()) + { + ScheduleDelayedOperation(DELAYED_RESURRECT_PLAYER); + return; + } + ResurrectPlayer(0.0f,false); if(GetMaxHealth() > m_resurrectHealth) @@ -19693,13 +19907,6 @@ void Player::SetClientControl(Unit* target, uint8 allowMove) void Player::UpdateZoneDependentAuras( uint32 newZone ) { - // remove new continent flight forms - if( !IsAllowUseFlyMountsHere() ) - { - RemoveAurasByType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED); - RemoveAurasByType(SPELL_AURA_FLY); - } - // Some spells applied at enter into zone (with subzones), aura removed in UpdateAreaDependentAuras that called always at zone->area update SpellAreaForAreaMapBounds saBounds = spellmgr.GetSpellAreaForAreaMapBounds(newZone); for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) @@ -20122,7 +20329,7 @@ bool Player::isTotalImmune() bool Player::HasTitle(uint32 bitIndex) { - if (bitIndex > 192) + if (bitIndex > MAX_TITLE_INDEX) return false; uint32 fieldIndexOffset = bitIndex / 32; @@ -20130,11 +20337,30 @@ bool Player::HasTitle(uint32 bitIndex) return HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag); } -void Player::SetTitle(CharTitlesEntry const* title) +void Player::SetTitle(CharTitlesEntry const* title, bool lost) { uint32 fieldIndexOffset = title->bit_index / 32; uint32 flag = 1 << (title->bit_index % 32); - SetFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag); + + if(lost) + { + if(!HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag)) + return; + + RemoveFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag); + } + else + { + if(HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag)) + return; + + SetFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag); + } + + WorldPacket data(SMSG_TITLE_EARNED, 4 + 4); + data << uint32(title->bit_index); + data << uint32(lost ? 0 : 1); // 1 - earned, 0 - lost + GetSession()->SendPacket(&data); } /*-----------------------TRINITY--------------------------*/ @@ -20230,6 +20456,7 @@ void Player::InitRunes() m_runes = new Runes; m_runes->runeState = 0; + m_runes->lastUsedRune = RUNE_BLOOD; for(uint32 i = 0; i < MAX_RUNES; ++i) { @@ -20272,29 +20499,25 @@ void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore cons uint32 Player::CalculateTalentsPoints() const { - uint32 base_talent = getLevel() < 10 ? 0 : uint32((getLevel()-9)*sWorld.getRate(RATE_TALENT)); + uint32 base_talent = getLevel() < 10 ? 0 : getLevel()-9; - if(getClass() != CLASS_DEATH_KNIGHT) - return base_talent; + if(getClass() != CLASS_DEATH_KNIGHT || GetMapId() != 609) + return uint32(base_talent * sWorld.getRate(RATE_TALENT)); - uint32 talentPointsForLevel = - (getLevel() < 56 ? 0 : uint32((getLevel()-55)*sWorld.getRate(RATE_TALENT))) - + m_questRewardTalentCount; + uint32 talentPointsForLevel = getLevel() < 56 ? 0 : getLevel() - 55; + talentPointsForLevel += m_questRewardTalentCount; if(talentPointsForLevel > base_talent) talentPointsForLevel = base_talent; - return talentPointsForLevel; + return uint32(talentPointsForLevel * sWorld.getRate(RATE_TALENT)); } -bool Player::IsAllowUseFlyMountsHere() const +bool Player::IsKnowHowFlyIn(uint32 mapid, uint32 zone) const { - if (isGameMaster()) - return true; - - uint32 zoneId = GetZoneId(); - uint32 v_map = GetVirtualMapForMapAndZone(GetMapId(), zoneId); - return v_map == 530 || v_map == 571 && HasSpell(54197) && zoneId != 4197; + // continent checked in SpellMgr::GetSpellAllowedInLocationError at cast and area update + uint32 v_map = GetVirtualMapForMapAndZone(mapid, zone); + return v_map != 571 || HasSpell(54197) && zone != 4197; // Cold Weather Flying } void Player::learnSpellHighRank(uint32 spellid) @@ -20484,7 +20707,7 @@ void Player::HandleFall(MovementInfo const& movementInfo) damage = GetMaxHealth(); // Gust of Wind - if (GetDummyAura(43621)) + if (HasAura(43621)) damage = GetMaxHealth()/2; EnvironmentalDamage(DAMAGE_FALL, damage); @@ -20502,7 +20725,12 @@ void Player::HandleFall(MovementInfo const& movementInfo) void Player::UpdateAchievementCriteria( AchievementCriteriaTypes type, uint32 miscvalue1/*=0*/, uint32 miscvalue2/*=0*/, Unit *unit/*=NULL*/, uint32 time/*=0*/ ) { - GetAchievementMgr().UpdateAchievementCriteria(type, miscvalue1,miscvalue2,unit,time); + GetAchievementMgr().UpdateAchievementCriteria(type, miscvalue1, miscvalue2, unit, time); +} + +void Player::CompletedAchievement(AchievementEntry const* entry) +{ + GetAchievementMgr().CompletedAchievement(entry); } void Player::LearnTalent(uint32 talentId, uint32 talentRank) @@ -21130,3 +21358,37 @@ void Player::ActivateSpec(uint32 specNum) resetTalents(true); } + +void Player::RemoveAtLoginFlag( AtLoginFlags f, bool in_db_also /*= false*/ ) +{ + m_atLoginFlags &= ~f; + + if(in_db_also) + CharacterDatabase.PExecute("UPDATE characters set at_login = at_login & ~ %u WHERE guid ='%u'", uint32(f), GetGUIDLow()); +} + +void Player::SendClearCooldown( uint32 spell_id, Unit* target ) +{ + WorldPacket data(SMSG_CLEAR_COOLDOWN, 4+8); + data << uint32(spell_id); + data << uint64(target->GetGUID()); + SendDirectMessage(&data); +} + +void Player::ResetMap() +{ + // this may be called during Map::Update + // after decrement+unlink, ++m_mapRefIter will continue correctly + // when the first element of the list is being removed + // nocheck_prev will return the padding element of the RefManager + // instead of NULL in the case of prev + GetMap()->UpdateIteratorBack(this); + Unit::ResetMap(); + GetMapRef().unlink(); +} + +void Player::SetMap(Map * map) +{ + Unit::SetMap(map); + m_mapRef.link(map, this); +} |