Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- diff --git a/sql/patches/ext_mail.sql b/sql/patches/ext_mail.sql
- new file mode 100644
- index 0000000..779aa02
- --- /dev/null
- +++ b/sql/patches/ext_mail.sql
- @@ -0,0 +1,18 @@
- +DROP TABLE IF EXISTS `mail_external`;
- +CREATE TABLE `mail_external` (
- + `id` bigint(20) unsigned NOT NULL auto_increment,
- + `receiver` bigint(20) unsigned NOT NULL,
- + `subject` varchar(200) default 'Support Message',
- + `message` varchar(500) default 'Support Message',
- + `money` bigint(20) unsigned NOT NULL default '0',
- + PRIMARY KEY (`id`)
- +) ENGINE=MyISAM DEFAULT CHARSET=utf8;
- +
- +DROP TABLE IF EXISTS `mail_external_items`;
- +CREATE TABLE `mail_external_items` (
- + `id` int(10) unsigned NOT NULL auto_increment,
- + `mail_id` int(10) unsigned NOT NULL,
- + `item` int(11) NOT NULL,
- + `count` int(11) NOT NULL,
- + PRIMARY KEY (`id`)
- +) ENGINE=MyISAM DEFAULT CHARSET=latin1;
- diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp
- index d3b54a4..0f041e8 100644
- --- a/src/game/CharacterHandler.cpp
- +++ b/src/game/CharacterHandler.cpp
- @@ -72,7 +72,7 @@ bool LoginQueryHolder::Initialize()
- "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,"
- "arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk,"
- - "health, power1, power2, power3, power4, power5, power6, power7 FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));
- + "health, power1, power2, power3, power4, power5, power6, power7, speccount, activespec FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT leaderGuid FROM group_member WHERE memberGuid ='%u'", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS, "SELECT caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid));
- @@ -81,7 +81,7 @@ bool LoginQueryHolder::Initialize()
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS,"SELECT quest,time FROM character_queststatus_daily WHERE guid = '%u'", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADREPUTATION, "SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADINVENTORY, "SELECT data,bag,slot,item,item_template FROM character_inventory JOIN item_instance ON character_inventory.item = item_instance.guid WHERE character_inventory.guid = '%u' ORDER BY bag,slot", GUID_LOPART(m_guid));
- - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACTIONS, "SELECT button,action,type FROM character_action WHERE guid = '%u' ORDER BY button", GUID_LOPART(m_guid));
- + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACTIONS, "SELECT a.button,a.action,a.type FROM character_action as a, characters as c WHERE a.guid = c.guid AND a.spec = c.activespec AND a.guid = '%u' ORDER BY button", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILCOUNT, "SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" UI64FMTD "'", GUID_LOPART(m_guid), (uint64)time(NULL));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILDATE, "SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSOCIALLIST, "SELECT friend,flags,note FROM character_social WHERE guid = '%u' LIMIT 255", GUID_LOPART(m_guid));
- @@ -96,6 +96,8 @@ bool LoginQueryHolder::Initialize()
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS,"SELECT criteria, counter, date FROM character_achievement_progress WHERE guid = '%u'", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS, "SELECT setguid, setindex, name, iconname, item0, item1, item2, item3, item4, item5, item6, item7, item8, item9, item10, item11, item12, item13, item14, item15, item16, item17, item18 FROM character_equipmentsets WHERE guid = '%u' ORDER BY setindex", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADBGDATA, "SELECT instance_id, team, join_x, join_y, join_z, join_o, join_map, taxi_start, taxi_end, mount_spell FROM character_battleground_data WHERE guid = '%u'", GUID_LOPART(m_guid));
- + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGLYPHS, "SELECT spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6 FROM character_glyphs WHERE guid='%u'", GUID_LOPART(m_guid));
- + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTS, "SELECT spell, spec FROM character_talent WHERE guid='%u'", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACCOUNTDATA, "SELECT type, time, data FROM character_account_data WHERE guid='%u'", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = '%u'", GUID_LOPART(m_guid));
- diff --git a/src/game/GossipDef.h b/src/game/GossipDef.h
- index 68744db..65cc47c 100644
- --- a/src/game/GossipDef.h
- +++ b/src/game/GossipDef.h
- @@ -48,6 +48,7 @@ enum Gossip_Option
- GOSSIP_OPTION_ARMORER = 15, //UNIT_NPC_FLAG_ARMORER (4096)
- GOSSIP_OPTION_UNLEARNTALENTS = 16, //UNIT_NPC_FLAG_TRAINER (16) (bonus option for GOSSIP_OPTION_TRAINER)
- GOSSIP_OPTION_UNLEARNPETSKILLS = 17, //UNIT_NPC_FLAG_TRAINER (16) (bonus option for GOSSIP_OPTION_TRAINER)
- + GOSSIP_OPTION_LEARNDUALSPEC = 19, //UNIT_NPC_FLAG_TRAINER (bonus option for GOSSIP_OPTION_TRAINER)
- GOSSIP_OPTION_MAX
- };
- diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp
- index f319cf0..d64ef55 100644
- --- a/src/game/Level3.cpp
- +++ b/src/game/Level3.cpp
- @@ -4449,10 +4449,10 @@ bool ChatHandler::HandleResetSpellsCommand(const char * args)
- if(target)
- {
- - target->resetSpells();
- + target->resetSpells(/* bool myClassOnly */);
- ChatHandler(target).SendSysMessage(LANG_RESET_SPELLS);
- - if(!m_session || m_session->GetPlayer()!=target)
- + if(!m_session || m_session->GetPlayer() != target)
- PSendSysMessage(LANG_RESET_SPELLS_ONLINE,GetNameLink(target).c_str());
- }
- else
- diff --git a/src/game/Mail.cpp b/src/game/Mail.cpp
- index 0509240..14c7a33 100644
- --- a/src/game/Mail.cpp
- +++ b/src/game/Mail.cpp
- @@ -984,3 +984,86 @@ void MailDraft::SendMailTo(MailReceiver const& receiver, MailSender const& sende
- else if (!m_items.empty())
- deleteIncludedItems();
- }
- +
- +void WorldSession::SendExternalMails()
- +{
- + sLog.outString("EXTERNAL MAIL> Sending mails in queue...");
- + QueryResult *result = CharacterDatabase.Query("SELECT e.id, e.receiver, e.subject, e.message, e.money, i.item, i.count FROM mail_external e LEFT JOIN mail_external_items i ON e.id = i.mail_id ORDER BY e.id;");
- + if(!result)
- + {
- + sLog.outString("EXTERNAL MAIL> No mails in queue...");
- + delete result;
- + return;
- + }
- + else
- + {
- + uint32 last_id = 0;
- + MailDraft* mail = NULL;
- + uint32 last_receiver_guid;
- +
- + do
- + {
- + Field *fields = result->Fetch();
- + uint32 id = fields[0].GetUInt32();
- + uint64 receiver_guid = fields[1].GetUInt64();
- + std::string subject = fields[2].GetString();
- + std::string message = fields[3].GetString();
- + uint32 money = fields[4].GetUInt32();
- + uint32 itemId = fields[5].GetUInt32();
- + uint32 itemCount = fields[6].GetUInt32();
- +
- + Player *receiver = sObjectMgr.GetPlayer( receiver_guid );
- +
- + if (id != last_id)
- + {
- + // send mail
- + if (last_id != 0)
- + {
- + sLog.outString("EXTERNAL MAIL> Sending mail to character with guid %d", last_receiver_guid);
- + mail->SendMailTo( MailReceiver(last_receiver_guid), MailSender(MAIL_NORMAL, 0, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_RETURNED);
- + delete mail;
- + CharacterDatabase.PExecute("DELETE mail_external AS e, mail_external_items AS i FROM mail_external AS e, mail_external_items AS i WHERE i.mail_id = e.id AND e.id = %u;", last_id);
- + sLog.outString("EXTERNAL MAIL> Mail sent");
- + }
- +
- + // create new mail
- + uint32 itemTextId = !message.empty() ? sObjectMgr.CreateItemText( message ) : 0;
- + mail = new MailDraft( subject, itemTextId );
- +
- + if(money)
- + {
- + sLog.outString("EXTERNAL MAIL> Adding money");
- + mail->AddMoney(money);
- + }
- + }
- +
- + if (itemId)
- + {
- + sLog.outString("EXTERNAL MAIL> Adding %u of item with id %u", itemCount, itemId);
- + Item* mailItem = Item::CreateItem( itemId, itemCount, receiver );
- + mailItem->SaveToDB();
- + mail->AddItem(mailItem);
- + }
- +
- + last_id = id;
- + last_receiver_guid = receiver_guid;
- +
- + }
- + while( result->NextRow() );
- +
- + // we only send a mail when mail_id!=last_mail_id, so we need to send the very last mail here:
- + if (last_id != 0)
- + {
- + // send last mail
- + sLog.outString("EXTERNAL MAIL> Sending mail to character with guid %d", last_receiver_guid);
- +
- + mail->SendMailTo( MailReceiver(last_receiver_guid), MailSender(MAIL_NORMAL, 0, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_RETURNED);
- + delete mail;
- + CharacterDatabase.PExecute("DELETE mail_external AS e, mail_external_items AS i FROM mail_external AS e, mail_external_items AS i WHERE i.mail_id = e.id AND e.id = %u;", last_id);
- + sLog.outString("EXTERNAL MAIL> Mail sent");
- + }
- + }
- +
- + delete result;
- + sLog.outString("EXTERNAL MAIL> All Mails Sent...");
- +}
- diff --git a/src/game/Player.cpp b/src/game/Player.cpp
- index 71276f5..945c1b5 100644
- --- a/src/game/Player.cpp
- +++ b/src/game/Player.cpp
- @@ -464,6 +464,14 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa
- m_activeSpec = 0;
- m_specsCount = 1;
- + for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i)
- + {
- + for (int g = 0; g < MAX_GLYPH_SLOT_INDEX; ++g)
- + m_Glyphs[i][g] = 0;
- +
- + m_talents[i] = new PlayerTalentMap();
- + }
- +
- for (int i = 0; i < BASEMOD_END; ++i)
- {
- m_auraBaseMod[i][FLAT_MOD] = 0.0f;
- @@ -518,6 +526,13 @@ Player::~Player ()
- for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
- delete itr->second;
- + for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i)
- + {
- + for (PlayerTalentMap::const_iterator itr = m_talents[i]->begin(); itr != m_talents[i]->end(); ++itr)
- + delete itr->second;
- + delete m_talents[i];
- + }
- +
- //all mailed items should be deleted, also all mail should be deallocated
- for (PlayerMails::const_iterator itr = m_mail.begin(); itr != m_mail.end();++itr)
- delete *itr;
- @@ -2522,6 +2537,12 @@ void Player::InitTalentForLevel()
- }
- else
- {
- + if (level < sWorld.getConfig(CONFIG_MIN_DUALSPEC_LEVEL) || m_specsCount == 0)
- + {
- + m_specsCount = 1;
- + m_activeSpec = 0;
- + }
- +
- uint32 talentPointsForLevel = CalculateTalentsPoints();
- // if used more that have then reset
- @@ -2840,7 +2861,7 @@ void Player::AddNewMailDeliverTime(time_t deliver_time)
- }
- }
- -bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependent, bool disabled)
- +bool Player::AddTalent(uint32 spell_id, uint8 spec, bool learning)
- {
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
- if (!spellInfo)
- @@ -2848,6 +2869,69 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- // do character spell book cleanup (all characters)
- if(!IsInWorld() && !learning) // spell load case
- {
- + sLog.outError("Player::AddTalent: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.",spell_id);
- + CharacterDatabase.PExecute("DELETE FROM character_talent WHERE spell = '%u'",spell_id);
- + }
- + else
- + sLog.outError("Player::AddTalent: Non-existed in SpellStore spell #%u request.",spell_id);
- +
- + return false;
- + }
- +
- + if(!SpellMgr::IsSpellValid(spellInfo,this,false))
- + {
- + // do character spell book cleanup (all characters)
- + if(!IsInWorld() && !learning) // spell load case
- + {
- + sLog.outError("Player::addTalent: Broken spell #%u learning not allowed, deleting for all characters in `character_talent`.",spell_id);
- + CharacterDatabase.PExecute("DELETE FROM character_talent WHERE spell = '%u'",spell_id);
- + }
- + else
- + sLog.outError("Player::addTalent: Broken spell #%u learning not allowed.",spell_id);
- +
- + return false;
- + }
- +
- + PlayerTalentMap::iterator itr = m_talents[spec]->find(spell_id);
- + if (itr != m_talents[spec]->end())
- + itr->second->state = PLAYERSPELL_UNCHANGED;
- + else if (TalentSpellPos const *talentPos = GetTalentSpellPos(spell_id))
- + {
- + if (TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentPos->talent_id ))
- + {
- + for(uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank)
- + {
- + // skip learning spell and no rank spell case
- + uint32 rankSpellId = talentInfo->RankID[rank];
- + if(!rankSpellId || rankSpellId == spell_id)
- + continue;
- +
- + PlayerTalentMap::iterator itr = m_talents[spec]->find(rankSpellId);
- + if (itr != m_talents[spec]->end())
- + itr->second->state = PLAYERSPELL_REMOVED;
- + }
- + }
- +
- + PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
- + PlayerTalent *newtalent = new PlayerTalent();
- +
- + newtalent->state = state;
- + newtalent->spec = spec;
- +
- + (*m_talents[spec])[spell_id] = newtalent;
- + return true;
- + }
- + return false;
- +}
- +
- +bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependent, bool disabled)
- +{
- + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
- + if (!spellInfo)
- + {
- + // do character spell book cleanup (all characters)
- + if (!IsInWorld() && !learning) // spell load case
- + {
- sLog.outError("Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.",spell_id);
- CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'",spell_id);
- }
- @@ -2857,7 +2941,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- return false;
- }
- - if(!SpellMgr::IsSpellValid(spellInfo,this,false))
- + if (!SpellMgr::IsSpellValid(spellInfo,this,false))
- {
- // do character spell book cleanup (all characters)
- if(!IsInWorld() && !learning) // spell load case
- @@ -2898,10 +2982,10 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- }
- // not do anything if already known in expected state
- - if(itr->second->state != PLAYERSPELL_REMOVED && itr->second->active == active &&
- + if (itr->second->state != PLAYERSPELL_REMOVED && itr->second->active == active &&
- itr->second->dependent == dependent && itr->second->disabled == disabled)
- {
- - if(!IsInWorld() && !learning) // explicitly load from DB and then exist in it already and set correctly
- + if (!IsInWorld() && !learning) // explicitly load from DB and then exist in it already and set correctly
- itr->second->state = PLAYERSPELL_UNCHANGED;
- return false;
- @@ -2917,21 +3001,21 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- }
- // update active state for known spell
- - if(itr->second->active != active && itr->second->state != PLAYERSPELL_REMOVED && !itr->second->disabled)
- + if (itr->second->active != active && itr->second->state != PLAYERSPELL_REMOVED && !itr->second->disabled)
- {
- itr->second->active = active;
- - if(!IsInWorld() && !learning && !dependent_set) // explicitly load from DB and then exist in it already and set correctly
- + if (!IsInWorld() && !learning && !dependent_set) // explicitly load from DB and then exist in it already and set correctly
- itr->second->state = PLAYERSPELL_UNCHANGED;
- - else if(itr->second->state != PLAYERSPELL_NEW)
- + else if (itr->second->state != PLAYERSPELL_NEW)
- itr->second->state = PLAYERSPELL_CHANGED;
- - if(active)
- + if (active)
- {
- if (IsPassiveSpell(spell_id) && IsNeedCastPassiveSpellAtLearn(spellInfo))
- - CastSpell (this,spell_id,true);
- + CastSpell(this, spell_id, true);
- }
- - else if(IsInWorld())
- + else if (IsInWorld())
- {
- if(next_active_spell_id)
- {
- @@ -2939,7 +3023,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4);
- data << uint32(spell_id);
- data << uint32(next_active_spell_id);
- - GetSession()->SendPacket( &data );
- + GetSession()->SendPacket(&data);
- }
- else
- {
- @@ -2952,18 +3036,18 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- return active; // learn (show in spell book if active now)
- }
- - if(itr->second->disabled != disabled && itr->second->state != PLAYERSPELL_REMOVED)
- + if (itr->second->disabled != disabled && itr->second->state != PLAYERSPELL_REMOVED)
- {
- - if(itr->second->state != PLAYERSPELL_NEW)
- + if (itr->second->state != PLAYERSPELL_NEW)
- itr->second->state = PLAYERSPELL_CHANGED;
- itr->second->disabled = disabled;
- - if(disabled)
- + if (disabled)
- return false;
- disabled_case = true;
- }
- - else switch(itr->second->state)
- + else switch (itr->second->state)
- {
- case PLAYERSPELL_UNCHANGED: // known saved spell
- return false;
- @@ -2977,7 +3061,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- default: // known not saved yet spell (new or modified)
- {
- // can be in case spell loading but learned at some previous spell loading
- - if(!IsInWorld() && !learning && !dependent_set)
- + if (!IsInWorld() && !learning && !dependent_set)
- itr->second->state = PLAYERSPELL_UNCHANGED;
- return false;
- @@ -2985,18 +3069,19 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- }
- }
- - if(!disabled_case) // skip new spell adding if spell already known (disabled spells case)
- + // skip new spell adding if spell already known (disabled spells case)
- + if (!disabled_case)
- {
- // talent: unlearn all other talent ranks (high and low)
- - if(TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id))
- + if (TalentSpellPos const *talentPos = GetTalentSpellPos(spell_id))
- {
- - if(TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentPos->talent_id ))
- + if (TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentPos->talent_id ))
- {
- - for(int i=0; i < MAX_TALENT_RANK; ++i)
- + for(uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank)
- {
- // skip learning spell and no rank spell case
- - uint32 rankSpellId = talentInfo->RankID[i];
- - if(!rankSpellId || rankSpellId==spell_id)
- + uint32 rankSpellId = talentInfo->RankID[rank];
- + if (!rankSpellId || rankSpellId == spell_id)
- continue;
- removeSpell(rankSpellId,false,false);
- @@ -3004,9 +3089,9 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- }
- }
- // non talent spell: learn low ranks (recursive call)
- - else if(uint32 prev_spell = sSpellMgr.GetPrevSpellInChain(spell_id))
- + else if (uint32 prev_spell = sSpellMgr.GetPrevSpellInChain(spell_id))
- {
- - if(!IsInWorld() || disabled) // at spells loading, no output, but allow save
- + if (!IsInWorld() || disabled) // at spells loading, no output, but allow save
- addSpell(prev_spell,active,true,true,disabled);
- else // at normal learning
- learnSpell(prev_spell,true);
- @@ -3019,21 +3104,21 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- newspell->disabled = disabled;
- // replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible
- - if(newspell->active && !newspell->disabled && !SpellMgr::canStackSpellRanks(spellInfo) && sSpellMgr.GetSpellRank(spellInfo->Id) != 0)
- + if (newspell->active && !newspell->disabled && !SpellMgr::canStackSpellRanks(spellInfo) && sSpellMgr.GetSpellRank(spellInfo->Id) != 0)
- {
- - for( PlayerSpellMap::iterator itr2 = m_spells.begin(); itr2 != m_spells.end(); ++itr2 )
- + for (PlayerSpellMap::iterator itr2 = m_spells.begin(); itr2 != m_spells.end(); ++itr2 )
- {
- - if(itr2->second->state == PLAYERSPELL_REMOVED) continue;
- + if (itr2->second->state == PLAYERSPELL_REMOVED) continue;
- SpellEntry const *i_spellInfo = sSpellStore.LookupEntry(itr2->first);
- if(!i_spellInfo) continue;
- - if( sSpellMgr.IsRankSpellDueToSpell(spellInfo,itr2->first) )
- + if (sSpellMgr.IsRankSpellDueToSpell(spellInfo,itr2->first) )
- {
- - if(itr2->second->active)
- + if (itr2->second->active)
- {
- - if(sSpellMgr.IsHighRankOfSpell(spell_id,itr2->first))
- + if (sSpellMgr.IsHighRankOfSpell(spell_id,itr2->first))
- {
- - if(IsInWorld()) // not send spell (re-/over-)learn packets at loading
- + if (IsInWorld()) // not send spell (re-/over-)learn packets at loading
- {
- WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4);
- data << uint32(itr2->first);
- @@ -3043,13 +3128,13 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- // mark old spell as disable (SMSG_SUPERCEDED_SPELL replace it in client by new)
- itr2->second->active = false;
- - if(itr2->second->state != PLAYERSPELL_NEW)
- + if (itr2->second->state != PLAYERSPELL_NEW)
- itr2->second->state = PLAYERSPELL_CHANGED;
- superceded_old = true; // new spell replace old in action bars and spell book.
- }
- - else if(sSpellMgr.IsHighRankOfSpell(itr2->first,spell_id))
- + else if (sSpellMgr.IsHighRankOfSpell(itr2->first,spell_id))
- {
- - if(IsInWorld()) // not send spell (re-/over-)learn packets at loading
- + if (IsInWorld()) // not send spell (re-/over-)learn packets at loading
- {
- WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4);
- data << uint32(spell_id);
- @@ -3059,7 +3144,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- // mark new spell as disable (not learned yet for client and will not learned)
- newspell->active = false;
- - if(newspell->state != PLAYERSPELL_NEW)
- + if (newspell->state != PLAYERSPELL_NEW)
- newspell->state = PLAYERSPELL_CHANGED;
- }
- }
- @@ -3101,7 +3186,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- // update free primary prof.points (if any, can be none in case GM .learn prof. learning)
- if (uint32 freeProfs = GetFreePrimaryProfessionPoints())
- {
- - if(sSpellMgr.IsPrimaryProfessionFirstRankSpell(spell_id))
- + if (sSpellMgr.IsPrimaryProfessionFirstRankSpell(spell_id))
- SetFreePrimaryProfessions(freeProfs-1);
- }
- @@ -3125,7 +3210,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- if (skill_max_value < new_skill_max_value)
- skill_max_value = new_skill_max_value;
- - SetSkill(spellLearnSkill->skill,skill_value,skill_max_value);
- + SetSkill(spellLearnSkill->skill, skill_value, skill_max_value);
- }
- else
- {
- @@ -3143,16 +3228,16 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
- ((pSkill->id==SKILL_LOCKPICKING || pSkill->id==SKILL_RUNEFORGING) && _spell_idx->second->max_value==0))
- {
- - switch(GetSkillRangeType(pSkill,_spell_idx->second->racemask!=0))
- + switch (GetSkillRangeType(pSkill,_spell_idx->second->racemask!=0))
- {
- case SKILL_RANGE_LANGUAGE:
- - SetSkill(pSkill->id, 300, 300 );
- + SetSkill(pSkill->id, 300, 300);
- break;
- case SKILL_RANGE_LEVEL:
- - SetSkill(pSkill->id, 1, GetMaxSkillValueForLevel() );
- + SetSkill(pSkill->id, 1, GetMaxSkillValueForLevel());
- break;
- case SKILL_RANGE_MONO:
- - SetSkill(pSkill->id, 1, 1 );
- + SetSkill(pSkill->id, 1, 1);
- break;
- default:
- break;
- @@ -3169,9 +3254,9 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
- if (!itr2->second.autoLearned)
- {
- if (!IsInWorld() || !itr2->second.active) // at spells loading, no output, but allow save
- - addSpell(itr2->second.spell,itr2->second.active,true,true,false);
- + addSpell(itr2->second.spell, itr2->second.active, true, true, false);
- else // at normal learning
- - learnSpell(itr2->second.spell,true);
- + learnSpell(itr2->second.spell, true);
- }
- }
- @@ -3280,7 +3365,7 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo
- // free talent points
- uint32 talentCosts = GetTalentSpellCost(spell_id);
- - if(talentCosts > 0)
- + if (talentCosts > 0)
- {
- if(talentCosts < m_usedTalentCount)
- m_usedTalentCount -= talentCosts;
- @@ -3297,7 +3382,7 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo
- }
- // remove dependent skill
- - SpellLearnSkillNode const* spellLearnSkill = sSpellMgr.GetSpellLearnSkill(spell_id);
- + SpellLearnSkillNode const *spellLearnSkill = sSpellMgr.GetSpellLearnSkill(spell_id);
- if(spellLearnSkill)
- {
- uint32 prev_spell = sSpellMgr.GetPrevSpellInChain(spell_id);
- @@ -3306,7 +3391,7 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo
- else
- {
- // search prev. skill setting by spell ranks chain
- - SpellLearnSkillNode const* prevSkill = sSpellMgr.GetSpellLearnSkill(prev_spell);
- + SpellLearnSkillNode const *prevSkill = sSpellMgr.GetSpellLearnSkill(prev_spell);
- while(!prevSkill && prev_spell)
- {
- prev_spell = sSpellMgr.GetPrevSpellInChain(prev_spell);
- @@ -3320,13 +3405,13 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo
- uint32 skill_value = GetPureSkillValue(prevSkill->skill);
- uint32 skill_max_value = GetPureMaxSkillValue(prevSkill->skill);
- - if (skill_value > prevSkill->value)
- + if (skill_value > prevSkill->value)
- skill_value = prevSkill->value;
- uint32 new_skill_max_value = prevSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : prevSkill->maxvalue;
- if (skill_max_value > new_skill_max_value)
- - skill_max_value = new_skill_max_value;
- + skill_max_value = new_skill_max_value;
- SetSkill(prevSkill->skill,skill_value,skill_max_value);
- }
- @@ -3350,7 +3435,7 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo
- ((pSkill->id==SKILL_LOCKPICKING || pSkill->id==SKILL_RUNEFORGING) && _spell_idx->second->max_value==0))
- {
- // not reset skills for professions and racial abilities
- - if ((pSkill->categoryId==SKILL_CATEGORY_SECONDARY || pSkill->categoryId==SKILL_CATEGORY_PROFESSION) &&
- + if ((pSkill->categoryId == SKILL_CATEGORY_SECONDARY || pSkill->categoryId == SKILL_CATEGORY_PROFESSION) &&
- (IsProfessionSkill(pSkill->id) || _spell_idx->second->racemask!=0))
- continue;
- @@ -3408,6 +3493,12 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo
- }
- }
- }
- +
- + if(spell_id == 46917 && m_canTitanGrip)
- + SetCanTitanGrip(false);
- +
- + if(sWorld.getConfig(CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN))
- + AutoUnequipOffhandIfNeed();
- // remove from spell book if not replaced by lesser rank
- if (!prev_activate && sendUpdate)
- @@ -3586,7 +3677,7 @@ uint32 Player::resetTalentsCost() const
- bool Player::resetTalents(bool no_cost)
- {
- // not need after this call
- - if(HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
- + if (HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
- RemoveAtLoginFlag(AT_LOGIN_RESET_TALENTS,true);
- uint32 talentPointsForLevel = CalculateTalentsPoints();
- @@ -3605,63 +3696,51 @@ bool Player::resetTalents(bool no_cost)
- if (GetMoney() < cost)
- {
- - SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
- + SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
- return false;
- }
- }
- - for (unsigned int i = 0; i < sTalentStore.GetNumRows(); ++i)
- + for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i)
- {
- TalentEntry const *talentInfo = sTalentStore.LookupEntry(i);
- - if (!talentInfo) continue;
- + if (!talentInfo)
- + continue;
- - TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
- + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
- - if(!talentTabInfo)
- + if (!talentTabInfo)
- continue;
- // unlearn only talents for character class
- // some spell learned by one class as normal spells or know at creation but another class learn it as talent,
- // to prevent unexpected lost normal learned spell skip another class talents
- - if( (getClassMask() & talentTabInfo->ClassMask) == 0 )
- + if ((getClassMask() & talentTabInfo->ClassMask) == 0)
- continue;
- - for (int j = 0; j < MAX_TALENT_RANK; ++j)
- + for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
- {
- - for(PlayerSpellMap::iterator itr = GetSpellMap().begin(); itr != GetSpellMap().end();)
- - {
- - if(itr->second->state == PLAYERSPELL_REMOVED || itr->second->disabled)
- - {
- - ++itr;
- - continue;
- - }
- -
- - // remove learned spells (all ranks)
- - uint32 itrFirstId = sSpellMgr.GetFirstSpellInChain(itr->first);
- -
- - // unlearn if first rank is talent or learned by talent
- - if (itrFirstId == talentInfo->RankID[j])
- - {
- - removeSpell(itr->first,!IsPassiveSpell(itr->first),false);
- - itr = GetSpellMap().begin();
- - continue;
- - }
- - else if (sSpellMgr.IsSpellLearnToSpell(talentInfo->RankID[j],itrFirstId))
- - {
- - removeSpell(itr->first,!IsPassiveSpell(itr->first));
- - itr = GetSpellMap().begin();
- - continue;
- - }
- - else
- - ++itr;
- - }
- + // skip non-existant talent ranks
- + if (talentInfo->RankID[rank] == 0)
- + continue;
- + removeSpell(talentInfo->RankID[rank], true);
- + if (const SpellEntry *_spellEntry = sSpellStore.LookupEntry(talentInfo->RankID[rank]))
- + for (uint8 i = 0; i < 3; ++i) // search through the SpellEntry for valid trigger spells
- + if (_spellEntry->EffectTriggerSpell[i] > 0 && _spellEntry->Effect[i] == SPELL_EFFECT_LEARN_SPELL)
- + removeSpell(_spellEntry->EffectTriggerSpell[i], true); // and remove any spells that the talent teaches
- + // if this talent rank can be found in the PlayerTalentMap, mark the talent as removed so it gets deleted
- + PlayerTalentMap::iterator plrTalent = m_talents[m_activeSpec]->find(talentInfo->RankID[rank]);
- + if (plrTalent != m_talents[m_activeSpec]->end())
- + plrTalent->second->state = PLAYERSPELL_REMOVED;
- }
- }
- + _SaveSpells();
- +
- SetFreeTalentPoints(talentPointsForLevel);
- - if(!no_cost)
- + if (!no_cost)
- {
- ModifyMoney(-(int32)cost);
- GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS, cost);
- @@ -3681,14 +3760,6 @@ bool Player::resetTalents(bool no_cost)
- }
- */
- -
- - if(m_canTitanGrip)
- - {
- - m_canTitanGrip = false;
- - if(sWorld.getConfig(CONFIG_OFFHAND_CHECK_AT_TALENTS_RESET))
- - AutoUnequipOffhandIfNeed();
- - }
- -
- return true;
- }
- @@ -3891,6 +3962,12 @@ bool Player::HasSpell(uint32 spell) const
- !itr->second->disabled);
- }
- +bool Player::HasTalent(uint32 spell, uint8 spec) const
- +{
- + PlayerTalentMap::const_iterator itr = m_talents[spec]->find(spell);
- + return (itr != m_talents[spec]->end() && itr->second->state != PLAYERSPELL_REMOVED);
- +}
- +
- bool Player::HasActiveSpell(uint32 spell) const
- {
- PlayerSpellMap::const_iterator itr = m_spells.find(spell);
- @@ -5624,12 +5701,12 @@ int16 Player::GetSkillTempBonusValue(uint32 skill) const
- return SKILL_TEMP_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos)));
- }
- -void Player::SendInitialActionButtons() const
- +void Player::SendActionButtons(uint32 state) const
- {
- - sLog.outDetail( "Initializing Action Buttons for '%u'", GetGUIDLow() );
- + sLog.outDetail( "Sending Action Buttons for '%u' spec '%u'", GetGUIDLow(), m_activeSpec);
- WorldPacket data(SMSG_ACTION_BUTTONS, 1+(MAX_ACTION_BUTTONS*4));
- - data << uint8(0); // can be 0, 1, 2 (talent spec)
- + data << uint8(state); // can be 0, 1, 2
- for(int button = 0; button < MAX_ACTION_BUTTONS; ++button)
- {
- ActionButtonList::const_iterator itr = m_actionButtons.find(button);
- @@ -5640,7 +5717,7 @@ void Player::SendInitialActionButtons() const
- }
- GetSession()->SendPacket( &data );
- - sLog.outDetail( "Action Buttons for '%u' Initialized", GetGUIDLow() );
- + sLog.outDetail( "Action Buttons for '%u' spec '%u' Sent", GetGUIDLow(), m_activeSpec );
- }
- bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type, Player* player)
- @@ -5720,6 +5797,11 @@ void Player::removeActionButton(uint8 button)
- if (buttonItr==m_actionButtons.end())
- return;
- + if (!buttonItr->second.canRemoveByClient)
- + {
- + buttonItr->second.canRemoveByClient = true;
- + return;
- + }
- if(buttonItr->second.uState==ACTIONBUTTON_NEW)
- m_actionButtons.erase(buttonItr); // new and not saved
- else
- @@ -12306,6 +12388,10 @@ void Player::PrepareGossipMenu(WorldObject *pSource, uint32 menuId)
- if (!pCreature->isCanTrainingOf(this, false))
- bCanTalk = false;
- break;
- + case GOSSIP_OPTION_LEARNDUALSPEC:
- + if(!(GetSpecsCount() == 1 && pCreature->isCanTrainingAndResetTalentsOf(this) && !(getLevel() < sWorld.getConfig(CONFIG_MIN_DUALSPEC_LEVEL))))
- + bCanTalk = false;
- + break;
- case GOSSIP_OPTION_UNLEARNTALENTS:
- if (!pCreature->isCanTrainingAndResetTalentsOf(this))
- bCanTalk = false;
- @@ -12505,6 +12591,29 @@ void Player::OnGossipSelect(WorldObject* pSource, uint32 gossipListId, uint32 me
- case GOSSIP_OPTION_TRAINER:
- GetSession()->SendTrainerList(guid);
- break;
- + case GOSSIP_OPTION_LEARNDUALSPEC:
- + if(GetSpecsCount() == 1 && !(getLevel() < sWorld.getConfig(CONFIG_MIN_DUALSPEC_LEVEL)))
- + {
- + if (GetMoney() < 10000000)
- + {
- + SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
- + PlayerTalkClass->CloseGossip();
- + break;
- + }
- + else
- + {
- + ModifyMoney(-10000000);
- +
- + // Cast spells that teach dual spec
- + // Both are also ImplicitTarget self and must be cast by player
- + this->CastSpell(this,63680,true,NULL,NULL,this->GetGUID());
- + this->CastSpell(this,63624,true,NULL,NULL,this->GetGUID());
- +
- + // Should show another Gossip text with "Congratulations..."
- + PlayerTalkClass->CloseGossip();
- + }
- + }
- + break;
- case GOSSIP_OPTION_UNLEARNTALENTS:
- PlayerTalkClass->CloseGossip();
- SendTalentWipeConfirm(guid);
- @@ -14558,8 +14667,8 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
- //"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,"
- // 40 41 42 43 44 45 46 47 48 49 50
- //"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk,"
- - // 51 52 53 54 55 56 57 58
- - //"health, power1, power2, power3, power4, power5, power6, power7 FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));
- + // 51 52 53 54 55 56 57 58 59 60
- + //"health, power1, power2, power3, power4, power5, power6, power7, speccount, activespec FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));
- QueryResult *result = holder->GetResult(PLAYER_LOGIN_QUERY_LOADFROM);
- if(!result)
- @@ -14973,7 +15082,22 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
- // Mail
- _LoadMail();
- -
- +
- + m_specsCount = fields[59].GetUInt32();
- + m_activeSpec = fields[60].GetUInt32();
- +
- + // sanity check
- + if (m_specsCount > MAX_TALENT_SPECS || m_activeSpec > MAX_TALENT_SPEC ||
- + m_specsCount < MIN_TALENT_SPECS || m_activeSpec < MIN_TALENT_SPEC)
- + {
- + m_activeSpec = 0;
- + sLog.outError("Player %s(GUID: %u) has SpecCount = %u and ActiveSpec = %u.", GetName(), GetGUIDLow(), m_specsCount, m_activeSpec);
- + }
- +
- + _LoadTalents(holder->GetResult(PLAYER_LOGIN_QUERY_LOADTALENTS));
- +
- + _LoadGlyphs(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGLYPHS));
- +
- _LoadAuras(holder->GetResult(PLAYER_LOGIN_QUERY_LOADAURAS), time_diff);
- _LoadGlyphAuras();
- @@ -14981,7 +15105,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
- if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST) )
- m_deathState = DEAD;
- - _LoadSpells(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLS));
- + _LoadSpells(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLS));
- // after spell load, learn rewarded spell if need also
- _LoadQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS));
- @@ -14999,7 +15123,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
- // update items with duration and realtime
- UpdateItemDuration(time_diff, true);
- - _LoadActions(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACTIONS));
- + _LoadActions(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACTIONS), true);
- // unread mails and next delivery time, actual mails not loaded
- _LoadMailInit(holder->GetResult(PLAYER_LOGIN_QUERY_LOADMAILCOUNT), holder->GetResult(PLAYER_LOGIN_QUERY_LOADMAILDATE));
- @@ -15179,12 +15303,8 @@ bool Player::isAllowedToLoot(Creature* creature)
- return !creature->hasLootRecipient();
- }
- -void Player::_LoadActions(QueryResult *result)
- +void Player::_LoadActions(QueryResult *result, bool startup)
- {
- - m_actionButtons.clear();
- -
- - //QueryResult *result = CharacterDatabase.PQuery("SELECT button,action,type FROM character_action WHERE guid = '%u' ORDER BY button",GetGUIDLow());
- -
- if(result)
- {
- do
- @@ -15196,7 +15316,11 @@ void Player::_LoadActions(QueryResult *result)
- uint8 type = fields[2].GetUInt8();
- if(ActionButton* ab = addActionButton(button, action, type))
- + {
- ab->uState = ACTIONBUTTON_UNCHANGED;
- + if(!startup) // Switching specs
- + ab->canRemoveByClient = false;
- + }
- else
- {
- sLog.outError( " ...at loading, and will deleted in DB also");
- @@ -16132,7 +16256,7 @@ void Player::SaveToDB()
- "trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, "
- "death_expire_time, taxi_path, arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, "
- "todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, health, power1, power2, power3, "
- - "power4, power5, power6, power7) VALUES ("
- + "power4, power5, power6, power7, speccount, activespec) VALUES ("
- << GetGUIDLow() << ", "
- << GetSession()->GetAccountId() << ", '"
- << sql_name << "', "
- @@ -16239,6 +16363,10 @@ void Player::SaveToDB()
- for(uint32 i = 0; i < MAX_POWERS; ++i)
- ss << "," << GetPower(Powers(i));
- + ss << ", ";
- + ss << uint32(m_specsCount);
- + ss << ", ";
- + ss << uint32(m_activeSpec);
- ss << ")";
- @@ -16251,6 +16379,7 @@ void Player::SaveToDB()
- _SaveInventory();
- _SaveQuestStatus();
- _SaveDailyQuestStatus();
- + _SaveTalents();
- _SaveSpells();
- _SaveSpellCooldowns();
- _SaveActions();
- @@ -16260,6 +16389,7 @@ void Player::SaveToDB()
- m_reputationMgr.SaveToDB();
- _SaveEquipmentSets();
- GetSession()->SaveTutorialsData(); // changed only while character in game
- + _SaveGlyphs();
- CharacterDatabase.CommitTransaction();
- @@ -16287,19 +16417,19 @@ void Player::_SaveActions()
- switch (itr->second.uState)
- {
- case ACTIONBUTTON_NEW:
- - 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() );
- + CharacterDatabase.PExecute("INSERT INTO character_action (guid,spec,button,action,type) VALUES ('%u', '%u', '%u', '%u', '%u')",
- + GetGUIDLow(), (uint32)m_activeSpec, (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' WHERE guid= '%u' AND button= '%u' ",
- - (uint32)itr->second.GetAction(), (uint32)itr->second.GetType(), GetGUIDLow(), (uint32)itr->first );
- + CharacterDatabase.PExecute("UPDATE character_action SET action = '%u', type = '%u' WHERE guid = '%u' AND button = '%u' AND spec = '%u'",
- + (uint32)itr->second.GetAction(), (uint32)itr->second.GetType(), GetGUIDLow(), (uint32)itr->first, (uint32)m_activeSpec);
- itr->second.uState = ACTIONBUTTON_UNCHANGED;
- ++itr;
- break;
- case ACTIONBUTTON_DELETED:
- - CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u' and button = '%u'", GetGUIDLow(), (uint32)itr->first );
- + CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u' and button = '%u' and spec = '%u'", GetGUIDLow(), (uint32)itr->first, (uint32)m_activeSpec );
- m_actionButtons.erase(itr++);
- break;
- default:
- @@ -20681,13 +20811,13 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
- if( (getClassMask() & talentTabInfo->ClassMask) == 0 )
- return;
- - // find current max talent rank
- - int32 curtalent_maxrank = 0;
- - for(int32 k = MAX_TALENT_RANK-1; k > -1; --k)
- + // find current max talent rank (0~5)
- + uint8 curtalent_maxrank = 0; // 0 = not learned any rank
- + for(int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
- {
- - if(talentInfo->RankID[k] && HasSpell(talentInfo->RankID[k]))
- + if(talentInfo->RankID[rank] && HasSpell(talentInfo->RankID[rank]))
- {
- - curtalent_maxrank = k + 1;
- + curtalent_maxrank = (rank + 1);
- break;
- }
- }
- @@ -20706,10 +20836,10 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
- if(TalentEntry const *depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn))
- {
- bool hasEnoughRank = false;
- - for (int i = talentInfo->DependsOnRank; i < MAX_TALENT_RANK; ++i)
- + for (uint8 rank = talentInfo->DependsOnRank; rank < MAX_TALENT_RANK; rank++)
- {
- - if (depTalentInfo->RankID[i] != 0)
- - if (HasSpell(depTalentInfo->RankID[i]))
- + if (depTalentInfo->RankID[rank] != 0)
- + if (HasSpell(depTalentInfo->RankID[rank]))
- hasEnoughRank = true;
- }
- if (!hasEnoughRank)
- @@ -20765,7 +20895,9 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
- // learn! (other talent ranks will unlearned at learning)
- learnSpell(spellid, false);
- - sLog.outDetail("TalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid);
- + AddTalent(spellid, m_activeSpec, true);
- +
- + sLog.outDetail("TalentID: %u Rank: %u Spell: %u Spec: %u\n", talentId, talentRank, spellid, m_activeSpec);
- // update free talent points
- SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1));
- @@ -20816,13 +20948,13 @@ void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank)
- if(!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask))
- return;
- - // find current max talent rank
- - int32 curtalent_maxrank = 0;
- - for(int32 k = MAX_TALENT_RANK-1; k > -1; --k)
- + // find current max talent rank (0~5)
- + uint8 curtalent_maxrank = 0; // 0 = not learned any rank
- + for(int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
- {
- - if(talentInfo->RankID[k] && pet->HasSpell(talentInfo->RankID[k]))
- + if(talentInfo->RankID[rank] && pet->HasSpell(talentInfo->RankID[rank]))
- {
- - curtalent_maxrank = k + 1;
- + curtalent_maxrank = (rank + 1);
- break;
- }
- }
- @@ -20841,10 +20973,10 @@ void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank)
- if(TalentEntry const *depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn))
- {
- bool hasEnoughRank = false;
- - for (int i = talentInfo->DependsOnRank; i < MAX_TALENT_RANK; ++i)
- + for (uint8 rank = talentInfo->DependsOnRank; rank < MAX_TALENT_RANK; rank++)
- {
- - if (depTalentInfo->RankID[i] != 0)
- - if (pet->HasSpell(depTalentInfo->RankID[i]))
- + if (depTalentInfo->RankID[rank] != 0)
- + if (pet->HasSpell(depTalentInfo->RankID[rank]))
- hasEnoughRank = true;
- }
- if (!hasEnoughRank)
- @@ -20867,13 +20999,13 @@ void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank)
- {
- if (tmpTalent->TalentTab == tTab)
- {
- - for (int j = 0; j < MAX_TALENT_RANK; ++j)
- + for (uint8 rank = 0; rank < MAX_TALENT_RANK; rank++)
- {
- - if (tmpTalent->RankID[j] != 0)
- + if (tmpTalent->RankID[rank] != 0)
- {
- - if (pet->HasSpell(tmpTalent->RankID[j]))
- + if (pet->HasSpell(tmpTalent->RankID[rank]))
- {
- - spentPoints += j + 1;
- + spentPoints += (rank + 1);
- }
- }
- }
- @@ -21002,13 +21134,13 @@ void Player::BuildPlayerTalentsInfoData(WorldPacket *data)
- if(talentInfo->TalentTab != talentTabId)
- continue;
- - // find max talent rank
- - int32 curtalent_maxrank = -1;
- - for(int32 k = MAX_TALENT_RANK-1; k > -1; --k)
- + // find max talent rank (0~4)
- + int8 curtalent_maxrank = -1;
- + for(int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
- {
- - if(talentInfo->RankID[k] && HasSpell(talentInfo->RankID[k]))
- + if(talentInfo->RankID[rank] && HasTalent(talentInfo->RankID[rank], specIdx))
- {
- - curtalent_maxrank = k;
- + curtalent_maxrank = rank;
- break;
- }
- }
- @@ -21029,7 +21161,7 @@ void Player::BuildPlayerTalentsInfoData(WorldPacket *data)
- *data << uint8(MAX_GLYPH_SLOT_INDEX); // glyphs count
- for(uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
- - *data << uint16(GetGlyph(i)); // GlyphProperties.dbc
- + *data << uint16(m_Glyphs[specIdx][i]); // GlyphProperties.dbc
- }
- }
- }
- @@ -21079,13 +21211,13 @@ void Player::BuildPetTalentsInfoData(WorldPacket *data)
- if(talentInfo->TalentTab != talentTabId)
- continue;
- - // find max talent rank
- - int32 curtalent_maxrank = -1;
- - for(int32 k = 4; k > -1; --k)
- + // find max talent rank (0~4)
- + int8 curtalent_maxrank = -1;
- + for(int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
- {
- - if(talentInfo->RankID[k] && pet->HasSpell(talentInfo->RankID[k]))
- + if(talentInfo->RankID[rank] && pet->HasSpell(talentInfo->RankID[rank]))
- {
- - curtalent_maxrank = k;
- + curtalent_maxrank = rank;
- break;
- }
- }
- @@ -21284,14 +21416,6 @@ void Player::DeleteEquipmentSet(uint64 setGuid)
- }
- }
- -void Player::ActivateSpec(uint32 specNum)
- -{
- - if(GetActiveSpec() == specNum)
- - return;
- -
- - resetTalents(true);
- -}
- -
- void Player::RemoveAtLoginFlag( AtLoginFlags f, bool in_db_also /*= false*/ )
- {
- m_atLoginFlags &= ~f;
- @@ -21386,6 +21510,245 @@ bool Player::IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) c
- return Unit::IsImmunedToSpellEffect(spellInfo, index);
- }
- +void Player::_LoadGlyphs(QueryResult *result)
- +{
- + // SetPQuery(PLAYER_LOGIN_QUERY_LOADGLYPHS, "SELECT spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6 from character_glyphs WHERE guid = '%u'", GUID_LOPART(m_guid));
- + if (!result)
- + return;
- +
- + do
- + {
- + Field *fields = result->Fetch();
- +
- + uint8 spec = fields[0].GetUInt8();
- + if (spec >= m_specsCount)
- + continue;
- +
- + m_Glyphs[spec][0] = fields[1].GetUInt32();
- + m_Glyphs[spec][1] = fields[2].GetUInt32();
- + m_Glyphs[spec][2] = fields[3].GetUInt32();
- + m_Glyphs[spec][3] = fields[4].GetUInt32();
- + m_Glyphs[spec][4] = fields[5].GetUInt32();
- + m_Glyphs[spec][5] = fields[6].GetUInt32();
- +
- + } while (result->NextRow());
- +
- + delete result;
- +}
- +
- +void Player::_SaveGlyphs()
- +{
- + CharacterDatabase.PExecute("DELETE FROM character_glyphs WHERE guid='%u'",GetGUIDLow());
- + for (uint8 spec = 0; spec < m_specsCount; ++spec)
- + {
- + CharacterDatabase.PExecute("INSERT INTO character_glyphs VALUES('%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')",
- + GetGUIDLow(), spec, m_Glyphs[spec][0], m_Glyphs[spec][1], m_Glyphs[spec][2], m_Glyphs[spec][3], m_Glyphs[spec][4], m_Glyphs[spec][5]);
- + }
- +}
- +
- +void Player::_LoadTalents(QueryResult *result)
- +{
- + // SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTS, "SELECT spell, spec FROM character_talent WHERE guid = '%u'", GUID_LOPART(m_guid));
- + if (result)
- + {
- + do
- + {
- + Field *fields = result->Fetch();
- + AddTalent(fields[0].GetUInt32(), fields[1].GetUInt32(), false);
- + }
- + while( result->NextRow() );
- +
- + delete result;
- + }
- +}
- +
- +void Player::_SaveTalents()
- +{
- + for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i)
- + {
- + for (PlayerTalentMap::iterator itr = m_talents[i]->begin(), next = m_talents[i]->begin(); itr != m_talents[i]->end();)
- + {
- + if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->state == PLAYERSPELL_CHANGED)
- + CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' and spell = '%u' and spec = '%u'", GetGUIDLow(), itr->first, itr->second->spec);
- +
- + if (itr->second->state == PLAYERSPELL_NEW || itr->second->state == PLAYERSPELL_CHANGED)
- + CharacterDatabase.PExecute("INSERT INTO character_talent (guid,spell,spec) VALUES ('%u', '%u', '%u')", GetGUIDLow(), itr->first, itr->second->spec);
- +
- + if (itr->second->state == PLAYERSPELL_REMOVED)
- + {
- + delete itr->second;
- + m_talents[i]->erase(itr++);
- + }
- + else
- + {
- + itr->second->state = PLAYERSPELL_UNCHANGED;
- + ++itr;
- + }
- + }
- + }
- +}
- +
- +void Player::UpdateSpecCount(uint8 count)
- +{
- + if(GetSpecsCount() == count)
- + return;
- +
- + if(count == MIN_TALENT_SPECS)
- + {
- + _SaveActions(); // make sure the button list is cleaned up
- + // active spec becomes only spec?
- + CharacterDatabase.PExecute("DELETE FROM character_action WHERE spec<>'%u' AND guid='%u'",m_activeSpec, GetGUIDLow());
- + m_activeSpec = 0;
- + }
- + else if (count == MAX_TALENT_SPECS)
- + {
- + _SaveActions(); // make sure the button list is cleaned up
- + for(ActionButtonList::iterator itr = m_actionButtons.begin(); itr != m_actionButtons.end(); ++itr)
- + {
- + CharacterDatabase.PExecute("INSERT INTO character_action (guid,button,action,type,spec) VALUES ('%u', '%u', '%u', '%u', '%u')",
- + GetGUIDLow(), (uint32)itr->first, (uint32)itr->second.GetAction(), (uint32)itr->second.GetType(), 1 );
- + }
- + }
- + else
- + {
- + return;
- + }
- +
- + SetSpecsCount(count);
- +
- + SendTalentsInfoData(false);
- +}
- +
- +void Player::ActivateSpec(uint8 spec)
- +{
- + if(GetActiveSpec() == spec)
- + return;
- +
- + if(GetSpecsCount() != MAX_TALENT_SPECS)
- + return;
- +
- + _SaveActions();
- +
- + UnsummonPetTemporaryIfAny();
- +
- + for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i)
- + {
- + TalentEntry const *talentInfo = sTalentStore.LookupEntry(i);
- +
- + if (!talentInfo)
- + continue;
- +
- + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
- +
- + if (!talentTabInfo)
- + continue;
- +
- + // unlearn only talents for character class
- + // some spell learned by one class as normal spells or know at creation but another class learn it as talent,
- + // to prevent unexpected lost normal learned spell skip another class talents
- + if ((getClassMask() & talentTabInfo->ClassMask) == 0)
- + continue;
- +
- + for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
- + {
- + // skip non-existant talent ranks
- + if (talentInfo->RankID[rank] == 0)
- + continue;
- + removeSpell(talentInfo->RankID[rank], true); // removes the talent, and all dependant, learned, and chained spells..
- + if (const SpellEntry *_spellEntry = sSpellStore.LookupEntry(talentInfo->RankID[rank]))
- + for (uint8 i = 0; i < 3; ++i) // search through the SpellEntry for valid trigger spells
- + if (_spellEntry->EffectTriggerSpell[i] > 0 && _spellEntry->Effect[i] == SPELL_EFFECT_LEARN_SPELL)
- + removeSpell(_spellEntry->EffectTriggerSpell[i], true); // and remove any spells that the talent teaches
- + // if this talent rank can be found in the PlayerTalentMap, mark the talent as removed so it gets deleted
- + //PlayerTalentMap::iterator plrTalent = m_talents[m_activeSpec]->find(talentInfo->RankID[rank]);
- + //if (plrTalent != m_talents[m_activeSpec]->end())
- + // plrTalent->second->state = PLAYERSPELL_REMOVED;
- + }
- + }
- + // set glyphs
- + for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
- + {
- + // remove secondary glyph
- + if(uint32 oldglyph = m_Glyphs[m_activeSpec][slot])
- + {
- + if(GlyphPropertiesEntry const *old_gp = sGlyphPropertiesStore.LookupEntry(oldglyph))
- + {
- + RemoveAurasDueToSpell(old_gp->SpellId);
- + }
- + }
- + }
- +
- + SetActiveSpec(spec);
- + uint32 spentTalents = 0;
- +
- + for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
- + {
- + TalentEntry const *talentInfo = sTalentStore.LookupEntry(talentId);
- +
- + if (!talentInfo)
- + continue;
- +
- + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
- +
- + if (!talentTabInfo)
- + continue;
- +
- + // learn only talents for character class
- + if ((getClassMask() & talentTabInfo->ClassMask) == 0)
- + continue;
- +
- + // learn highest talent rank that exists in newly activated spec
- + for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
- + {
- + // skip non-existant talent ranks
- + if (talentInfo->RankID[rank] == 0)
- + continue;
- + // if the talent can be found in the newly activated PlayerTalentMap
- + if (HasTalent(talentInfo->RankID[rank], m_activeSpec))
- + {
- + learnSpell(talentInfo->RankID[rank], false); // add the talent to the PlayerSpellMap
- + spentTalents += (rank + 1); // increment the spentTalents count
- + }
- + }
- + }
- +
- + // set glyphs
- + for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
- + {
- + uint32 glyph = m_Glyphs[m_activeSpec][slot];
- + // apply primary glyph
- + if (glyph)
- + {
- + if (GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyph))
- + {
- + CastSpell(this, gp->SpellId, true);
- + }
- + }
- + SetGlyph(slot, glyph);
- + }
- +
- + m_usedTalentCount = spentTalents;
- + InitTalentForLevel();
- +
- + _SaveSpells();
- +
- + m_actionButtons.clear();
- + QueryResult *result = CharacterDatabase.PQuery("SELECT button,action,type FROM character_action WHERE guid = '%u' AND spec = '%u' ORDER BY button", GetGUIDLow(), m_activeSpec);
- + if (result)
- + {
- + _LoadActions(result, false);
- + }
- +
- + ResummonPetTemporaryUnSummonedIfAny();
- + SendActionButtons(1);
- +
- + Powers pw = getPowerType();
- + if(pw != POWER_MANA)
- + SetPower(POWER_MANA, 0);
- +
- + SetPower(pw, 0);
- +}
- +
- void Player::SetHomebindToCurrentPos()
- {
- m_homebindMapId = GetMapId();
- diff --git a/src/game/Player.h b/src/game/Player.h
- index d32f682..ac678b0 100644
- --- a/src/game/Player.h
- +++ b/src/game/Player.h
- @@ -93,6 +93,12 @@ struct PlayerSpell
- bool disabled : 1; // first rank has been learned in result talent learn but currently talent unlearned, save max learned ranks
- };
- +struct PlayerTalent
- +{
- + PlayerSpellState state : 8;
- + uint8 spec : 8;
- +};
- +
- // Spell modifier (used for modify other spells)
- struct SpellModifier
- {
- @@ -118,6 +124,7 @@ struct SpellModifier
- Spell const* lastAffected;
- };
- +typedef UNORDERED_MAP<uint32, PlayerTalent*> PlayerTalentMap;
- typedef UNORDERED_MAP<uint32, PlayerSpell*> PlayerSpellMap;
- typedef std::list<SpellModifier*> SpellModList;
- @@ -161,10 +168,11 @@ enum ActionButtonType
- struct ActionButton
- {
- - ActionButton() : packedData(0), uState( ACTIONBUTTON_NEW ) {}
- + ActionButton() : packedData(0), uState( ACTIONBUTTON_NEW ), canRemoveByClient(true){}
- uint32 packedData;
- ActionButtonUpdateState uState;
- + bool canRemoveByClient;
- // helpers
- ActionButtonType GetType() const { return ActionButtonType(ACTION_BUTTON_TYPE(packedData)); }
- @@ -925,7 +933,9 @@ enum PlayerLoginQueryIndex
- PLAYER_LOGIN_QUERY_LOADBGDATA = 21,
- PLAYER_LOGIN_QUERY_LOADACCOUNTDATA = 22,
- PLAYER_LOGIN_QUERY_LOADSKILLS = 23,
- - MAX_PLAYER_LOGIN_QUERY = 24
- + PLAYER_LOGIN_QUERY_LOADGLYPHS = 24,
- + PLAYER_LOGIN_QUERY_LOADTALENTS = 25,
- + MAX_PLAYER_LOGIN_QUERY = 26
- };
- enum PlayerDelayedOperations
- @@ -1607,20 +1617,28 @@ class MANGOS_DLL_SPEC Player : public Unit
- void LearnTalent(uint32 talentId, uint32 talentRank);
- void LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank);
- + bool AddTalent(uint32 spell, uint8 spec, bool learning);
- + bool HasTalent(uint32 spell_id, uint8 spec) const;
- +
- uint32 CalculateTalentsPoints() const;
- // Dual Spec
- + void UpdateSpecCount(uint8 count);
- uint32 GetActiveSpec() { return m_activeSpec; }
- - void SetActiveSpec(uint32 spec) { m_activeSpec = spec; }
- - uint32 GetSpecsCount() { return m_specsCount; }
- - void SetSpecsCount(uint32 count) { m_specsCount = count; }
- - void ActivateSpec(uint32 specNum);
- + void SetActiveSpec(uint8 spec){ m_activeSpec = spec; }
- + uint8 GetSpecsCount() { return m_specsCount; }
- + void SetSpecsCount(uint8 count) { m_specsCount = count; }
- + void ActivateSpec(uint8 spec);
- void InitGlyphsForLevel();
- void SetGlyphSlot(uint8 slot, uint32 slottype) { SetUInt32Value(PLAYER_FIELD_GLYPH_SLOTS_1 + slot, slottype); }
- uint32 GetGlyphSlot(uint8 slot) { return GetUInt32Value(PLAYER_FIELD_GLYPH_SLOTS_1 + slot); }
- - void SetGlyph(uint8 slot, uint32 glyph) { SetUInt32Value(PLAYER_FIELD_GLYPHS_1 + slot, glyph); }
- - uint32 GetGlyph(uint8 slot) { return GetUInt32Value(PLAYER_FIELD_GLYPHS_1 + slot); }
- + void SetGlyph(uint8 slot, uint32 glyph)
- + {
- + m_Glyphs[m_activeSpec][slot] = glyph;
- + SetUInt32Value(PLAYER_FIELD_GLYPHS_1 + slot, glyph);
- + }
- + uint32 GetGlyph(uint8 slot) { return m_Glyphs[m_activeSpec][slot]; }
- uint32 GetFreePrimaryProfessionPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS2); }
- void SetFreePrimaryProfessions(uint16 profs) { SetUInt32Value(PLAYER_CHARACTER_POINTS2, profs); }
- @@ -1691,7 +1709,8 @@ class MANGOS_DLL_SPEC Player : public Unit
- static bool IsActionButtonDataValid(uint8 button, uint32 action, uint8 type, Player* player);
- ActionButton* addActionButton(uint8 button, uint32 action, uint8 type);
- void removeActionButton(uint8 button);
- - void SendInitialActionButtons() const;
- + void SendInitialActionButtons() const { SendActionButtons(0); }
- + void SendActionButtons(uint32 state) const;
- PvPInfo pvpInfo;
- void UpdatePvP(bool state, bool ovrride=false);
- @@ -2347,7 +2366,7 @@ class MANGOS_DLL_SPEC Player : public Unit
- /*** LOAD SYSTEM ***/
- /*********************************************************/
- - void _LoadActions(QueryResult *result);
- + void _LoadActions(QueryResult *result, bool startup);
- void _LoadAuras(QueryResult *result, uint32 timediff);
- void _LoadGlyphAuras();
- void _LoadBoundInstances(QueryResult *result);
- @@ -2366,6 +2385,8 @@ class MANGOS_DLL_SPEC Player : public Unit
- void _LoadArenaTeamInfo(QueryResult *result);
- void _LoadEquipmentSets(QueryResult *result);
- void _LoadBGData(QueryResult* result);
- + void _LoadGlyphs(QueryResult *result);
- + void _LoadTalents(QueryResult *result);
- /*********************************************************/
- /*** SAVE SYSTEM ***/
- @@ -2381,6 +2402,8 @@ class MANGOS_DLL_SPEC Player : public Unit
- void _SaveSpells();
- void _SaveEquipmentSets();
- void _SaveBGData();
- + void _SaveGlyphs();
- + void _SaveTalents();
- void _SetCreateBits(UpdateMask *updateMask, Player *target) const;
- void _SetUpdateBits(UpdateMask *updateMask, Player *target) const;
- @@ -2433,11 +2456,14 @@ class MANGOS_DLL_SPEC Player : public Unit
- PlayerMails m_mail;
- PlayerSpellMap m_spells;
- SpellCooldowns m_spellCooldowns;
- + PlayerTalentMap *m_talents[MAX_TALENT_SPECS];
- uint32 m_lastPotionId; // last used health/mana potion in combat, that block next potion use
- uint32 m_activeSpec;
- uint32 m_specsCount;
- + uint32 m_Glyphs[MAX_TALENT_SPECS][MAX_GLYPH_SLOT_INDEX];
- +
- ActionButtonList m_actionButtons;
- float m_auraBaseMod[BASEMOD_END][MOD_END];
- diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h
- index 9074a2a..325f957 100644
- --- a/src/game/SharedDefines.h
- +++ b/src/game/SharedDefines.h
- @@ -449,6 +449,10 @@ const uint32 ItemQualityColors[MAX_ITEM_QUALITY] = {
- #define SPELL_ATTR_EX6_UNK30 0x40000000 // 30 not set in 3.0.3
- #define SPELL_ATTR_EX6_UNK31 0x80000000 // 31 not set in 3.0.3
- +#define MIN_TALENT_SPEC 0
- +#define MAX_TALENT_SPEC 1
- +#define MIN_TALENT_SPECS 1
- +#define MAX_TALENT_SPECS 2
- #define MAX_GLYPH_SLOT_INDEX 6
- enum SheathTypes
- diff --git a/src/game/Spell.h b/src/game/Spell.h
- index 00878b4..83f7e52 100644
- --- a/src/game/Spell.h
- +++ b/src/game/Spell.h
- @@ -327,6 +327,8 @@ class Spell
- void EffectTitanGrip(uint32 i);
- void EffectEnchantItemPrismatic(uint32 i);
- void EffectPlayMusic(uint32 i);
- + void EffectSpecCount(uint32 i);
- + void EffectActivateSpec(uint32 i);
- Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 originalCasterGUID = 0, Spell** triggeringContainer = NULL );
- ~Spell();
- diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp
- index 5e18c1b..7f6ced2 100644
- --- a/src/game/SpellEffects.cpp
- +++ b/src/game/SpellEffects.cpp
- @@ -218,8 +218,8 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
- &Spell::EffectMilling, //158 SPELL_EFFECT_MILLING milling
- &Spell::EffectRenamePet, //159 SPELL_EFFECT_ALLOW_RENAME_PET allow rename pet once again
- &Spell::EffectNULL, //160 SPELL_EFFECT_160 unused
- - &Spell::EffectNULL, //161 SPELL_EFFECT_TALENT_SPEC_COUNT second talent spec (learn/revert)
- - &Spell::EffectNULL, //162 SPELL_EFFECT_TALENT_SPEC_SELECT activate primary/secondary spec
- + &Spell::EffectSpecCount, //161 SPELL_EFFECT_TALENT_SPEC_COUNT second talent spec (learn/revert)
- + &Spell::EffectActivateSpec, //162 SPELL_EFFECT_TALENT_SPEC_SELECT activate primary/secondary spec
- };
- void Spell::EffectNULL(uint32 /*i*/)
- @@ -6965,3 +6965,19 @@ void Spell::EffectPlayMusic(uint32 i)
- data << uint32(soundid);
- ((Player*)unitTarget)->GetSession()->SendPacket(&data);
- }
- +
- +void Spell::EffectSpecCount(uint32 /*eff_idx*/)
- +{
- + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- + return;
- +
- + ((Player*)unitTarget)->UpdateSpecCount(damage);
- +}
- +
- +void Spell::EffectActivateSpec(uint32 /*eff_idx*/)
- +{
- + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- + return;
- +
- + ((Player*)unitTarget)->ActivateSpec(damage-1); // damage is 1 or 2, spec is 0 or 1
- +}
- diff --git a/src/game/World.cpp b/src/game/World.cpp
- index a9e6f4b..9aee063 100644
- --- a/src/game/World.cpp
- +++ b/src/game/World.cpp
- @@ -701,6 +701,8 @@ void World::LoadConfigSettings(bool reload)
- m_configs[CONFIG_MAX_PLAYER_LEVEL] = MAX_LEVEL;
- }
- + m_configs[CONFIG_MIN_DUALSPEC_LEVEL] = sConfig.GetIntDefault("MinDualSpecLevel", 40);
- +
- m_configs[CONFIG_START_PLAYER_LEVEL] = sConfig.GetIntDefault("StartPlayerLevel", 1);
- if(m_configs[CONFIG_START_PLAYER_LEVEL] < 1)
- {
- @@ -973,7 +975,7 @@ void World::LoadConfigSettings(bool reload)
- m_configs[CONFIG_ARENA_SEASON_ID] = sConfig.GetIntDefault ("Arena.ArenaSeason.ID", 1);
- m_configs[CONFIG_ARENA_SEASON_IN_PROGRESS] = sConfig.GetBoolDefault("Arena.ArenaSeason.InProgress", true);
- - m_configs[CONFIG_OFFHAND_CHECK_AT_TALENTS_RESET] = sConfig.GetBoolDefault("OffhandCheckAtTalentsReset", false);
- + m_configs[CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN] = sConfig.GetBoolDefault("OffhandCheckAtSpellUnlearn", false);
- if(int clientCacheId = sConfig.GetIntDefault("ClientCacheVersion", 0))
- {
- @@ -1003,6 +1005,10 @@ void World::LoadConfigSettings(bool reload)
- m_configs[CONFIG_TIMERBAR_FIRE_GMLEVEL] = sConfig.GetIntDefault("TimerBar.Fire.GMLevel", SEC_CONSOLE);
- m_configs[CONFIG_TIMERBAR_FIRE_MAX] = sConfig.GetIntDefault("TimerBar.Fire.Max", 1);
- + // External Mail
- + m_configs[CONFIG_EXTERNAL_MAIL] = sConfig.GetIntDefault("ExternalMail.Enabled", 0);
- + m_configs[CONFIG_EXTERNAL_MAIL_INTERVAL] = sConfig.GetIntDefault("ExternalMail.Interval", 1);
- +
- m_VisibleUnitGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Unit", 1);
- if(m_VisibleUnitGreyDistance > MAX_VISIBILITY_DISTANCE)
- {
- @@ -1484,6 +1490,7 @@ void World::SetInitialWorldSettings()
- //Update "uptime" table based on configuration entry in minutes.
- m_timers[WUPDATE_CORPSES].SetInterval(20*MINUTE*IN_MILISECONDS);
- //erase corpses every 20 minutes
- + m_timers[WUPDATE_EXT_MAIL].SetInterval(m_configs[CONFIG_EXTERNAL_MAIL_INTERVAL] * MINUTE * IN_MILISECONDS);
- //to set mailtimer to return mails every day between 4 and 5 am
- //mailtimer is increased when updating auctions
- @@ -1678,6 +1685,13 @@ void World::Update(uint32 diff)
- m_timers[WUPDATE_EVENTS].Reset();
- }
- + // External Mail
- + if(m_configs[CONFIG_EXTERNAL_MAIL] != 0 && m_timers[WUPDATE_EXT_MAIL].Passed())
- + {
- + WorldSession::SendExternalMails();
- + m_timers[WUPDATE_EXT_MAIL].Reset();
- + }
- +
- /// </ul>
- ///- Move all creatures with "delayed move" and remove and delete all objects with "delayed remove"
- sMapMgr.DoDelayedMovesAndRemoves();
- diff --git a/src/game/World.h b/src/game/World.h
- index 170fc2a..d64e627 100644
- --- a/src/game/World.h
- +++ b/src/game/World.h
- @@ -77,7 +77,8 @@ enum WorldTimers
- WUPDATE_UPTIME = 4,
- WUPDATE_CORPSES = 5,
- WUPDATE_EVENTS = 6,
- - WUPDATE_COUNT = 7
- + WUPDATE_EXT_MAIL = 7,
- + WUPDATE_COUNT = 8
- };
- /// Configuration elements
- @@ -118,6 +119,7 @@ enum WorldConfigs
- CONFIG_MIN_LEVEL_FOR_HEROIC_CHARACTER_CREATING,
- CONFIG_SKIP_CINEMATICS,
- CONFIG_MAX_PLAYER_LEVEL,
- + CONFIG_MIN_DUALSPEC_LEVEL,
- CONFIG_START_PLAYER_LEVEL,
- CONFIG_START_HEROIC_PLAYER_LEVEL,
- CONFIG_START_PLAYER_MONEY,
- @@ -212,7 +214,7 @@ enum WorldConfigs
- CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE,
- CONFIG_ARENA_SEASON_ID,
- CONFIG_ARENA_SEASON_IN_PROGRESS,
- - CONFIG_OFFHAND_CHECK_AT_TALENTS_RESET,
- + CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN,
- CONFIG_CLIENTCACHE_VERSION,
- CONFIG_GUILD_EVENT_LOG_COUNT,
- CONFIG_GUILD_BANK_EVENT_LOG_COUNT,
- @@ -222,7 +224,12 @@ enum WorldConfigs
- CONFIG_TIMERBAR_BREATH_MAX,
- CONFIG_TIMERBAR_FIRE_GMLEVEL,
- CONFIG_TIMERBAR_FIRE_MAX,
- - CONFIG_VALUE_COUNT
- +
- + // External Mail
- + CONFIG_EXTERNAL_MAIL,
- + CONFIG_EXTERNAL_MAIL_INTERVAL,
- +
- + CONFIG_VALUE_COUNT,
- };
- /// Server rates
- diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h
- index a1aad7b..b9d2a68 100644
- --- a/src/game/WorldSession.h
- +++ b/src/game/WorldSession.h
- @@ -226,6 +226,9 @@ class MANGOS_DLL_SPEC WorldSession
- //used with item_page table
- bool SendItemInfo( uint32 itemid, WorldPacket data );
- + // External mail
- + static void SendExternalMails();
- +
- //auction
- void SendAuctionHello( uint64 guid, Creature * unit );
- void SendAuctionCommandResult( uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError = 0);
- diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in
- index 479a6ce..eb6ad69 100644
- --- a/src/mangosd/mangosd.conf.dist.in
- +++ b/src/mangosd/mangosd.conf.dist.in
- @@ -595,6 +595,15 @@ LogColors = ""
- # Mail delivery delay time for item sending
- # Default: 3600 sec (1 hour)
- #
- +# ExternalMail.Enabled
- +# Enable external mail delivery from mail_external table.
- +# Default: 0 (disabled)
- +# 1 (enabled)
- +#
- +# ExternalMail.Interval
- +# Mail delivery delay time for item sending from mail_external table, in minutes.
- +# Default: 1 minute
- +#
- # SkillChance.Prospecting
- # For prospecting skillup impossible by default, but can be allowed as custom setting
- # Default: 0 - no skilups
- @@ -646,6 +655,7 @@ HeroicCharactersPerRealm = 1
- MinLevelForHeroicCharacterCreating = 55
- SkipCinematics = 0
- MaxPlayerLevel = 80
- +MinDualSpecLevel = 40
- StartPlayerLevel = 1
- StartHeroicPlayerLevel = 55
- StartPlayerMoney = 0
- @@ -677,9 +687,11 @@ MaxPrimaryTradeSkill = 2
- MinPetitionSigns = 9
- MaxGroupXPDistance = 74
- MailDeliveryDelay = 3600
- +ExternalMail.Enabled = 0
- +ExternalMail.Interval = 1
- SkillChance.Prospecting = 0
- SkillChance.Milling = 0
- -OffhandCheckAtTalentsReset = 0
- +OffhandCheckAtSpellUnlearn = 0
- ClientCacheVersion = 0
- Event.Announce = 0
- BeepAtStart = 1
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement