diff options
Diffstat (limited to 'src/game')
148 files changed, 11124 insertions, 7836 deletions
diff --git a/src/game/AccountMgr.cpp b/src/game/AccountMgr.cpp index 66660766dae..74bc4cd2dcc 100644 --- a/src/game/AccountMgr.cpp +++ b/src/game/AccountMgr.cpp @@ -26,7 +26,7 @@ #include "Player.h" #include "Util.h" -extern DatabaseType LoginDatabase; +extern DatabaseType loginDatabase; INSTANTIATE_SINGLETON_1(AccountMgr); @@ -44,26 +44,26 @@ AccountOpResult AccountMgr::CreateAccount(std::string username, std::string pass normalizeString(username); normalizeString(password); - LoginDatabase.escape_string(username); - LoginDatabase.escape_string(password); + loginDatabase.escape_string(username); + loginDatabase.escape_string(password); - QueryResult *result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE username = '%s'", username.c_str()); + QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE username = '%s'", username.c_str()); if(result) { delete result; return AOR_NAME_ALREDY_EXIST; // username does already exist } - if(!LoginDatabase.PExecute("INSERT INTO account(username,sha_pass_hash,joindate) VALUES('%s',SHA1(CONCAT('%s',':','%s')),NOW())", username.c_str(), username.c_str(), password.c_str())) + if(!loginDatabase.PExecute("INSERT INTO account(username,sha_pass_hash,joindate) VALUES('%s',SHA1(CONCAT('%s',':','%s')),NOW())", username.c_str(), username.c_str(), password.c_str())) return AOR_DB_INTERNAL_ERROR; // unexpected error - LoginDatabase.Execute("INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM realmlist,account LEFT JOIN realmcharacters ON acctid=account.id WHERE acctid IS NULL"); + loginDatabase.Execute("INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM realmlist,account LEFT JOIN realmcharacters ON acctid=account.id WHERE acctid IS NULL"); return AOR_OK; // everything's fine } AccountOpResult AccountMgr::DeleteAccount(uint32 accid) { - QueryResult *result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); + QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); if(!result) return AOR_NAME_NOT_EXIST; // account doesn't exist delete result; @@ -78,7 +78,7 @@ AccountOpResult AccountMgr::DeleteAccount(uint32 accid) uint64 guid = MAKE_NEW_GUID(guidlo, 0, HIGHGUID_PLAYER); // kick if player currently - if(Player* p = ObjectAccessor::FindPlayer(guid)) + if(Player* p = ObjectAccessor::GetObjectInWorld(guid, (Player*)NULL)) { WorldSession* s = p->GetSession(); s->KickPlayer(); // mark session to remove at next session list update @@ -94,13 +94,13 @@ AccountOpResult AccountMgr::DeleteAccount(uint32 accid) // table realm specific but common for all characters of account for realm CharacterDatabase.PExecute("DELETE FROM character_tutorial WHERE account = '%u'",accid); - LoginDatabase.BeginTransaction(); + loginDatabase.BeginTransaction(); bool res = - LoginDatabase.PExecute("DELETE FROM account WHERE id='%d'", accid) && - LoginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid='%d'", accid); + loginDatabase.PExecute("DELETE FROM account WHERE id='%d'", accid) && + loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid='%d'", accid); - LoginDatabase.CommitTransaction(); + loginDatabase.CommitTransaction(); if(!res) return AOR_DB_INTERNAL_ERROR; // unexpected error; @@ -110,7 +110,7 @@ AccountOpResult AccountMgr::DeleteAccount(uint32 accid) AccountOpResult AccountMgr::ChangeUsername(uint32 accid, std::string new_uname, std::string new_passwd) { - QueryResult *result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); + QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); if(!result) return AOR_NAME_NOT_EXIST; // account doesn't exist delete result; @@ -124,9 +124,9 @@ AccountOpResult AccountMgr::ChangeUsername(uint32 accid, std::string new_uname, normalizeString(new_uname); normalizeString(new_passwd); - LoginDatabase.escape_string(new_uname); - LoginDatabase.escape_string(new_passwd); - if(!LoginDatabase.PExecute("UPDATE account SET username='%s',sha_pass_hash=SHA1(CONCAT('%s',':','%s')) WHERE id='%d'", new_uname.c_str(), new_uname.c_str(), new_passwd.c_str(), accid)) + loginDatabase.escape_string(new_uname); + loginDatabase.escape_string(new_passwd); + if(!loginDatabase.PExecute("UPDATE account SET username='%s',sha_pass_hash=SHA1(CONCAT('%s',':','%s')) WHERE id='%d'", new_uname.c_str(), new_uname.c_str(), new_passwd.c_str(), accid)) return AOR_DB_INTERNAL_ERROR; // unexpected error return AOR_OK; @@ -134,7 +134,7 @@ AccountOpResult AccountMgr::ChangeUsername(uint32 accid, std::string new_uname, AccountOpResult AccountMgr::ChangePassword(uint32 accid, std::string new_passwd) { - QueryResult *result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); + QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); if(!result) return AOR_NAME_NOT_EXIST; // account doesn't exist delete result; @@ -144,8 +144,8 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accid, std::string new_passwd) normalizeString(new_passwd); - LoginDatabase.escape_string(new_passwd); - if(!LoginDatabase.PExecute("UPDATE account SET sha_pass_hash=SHA1(CONCAT(username,':','%s')) WHERE id='%d'", new_passwd.c_str(), accid)) + loginDatabase.escape_string(new_passwd); + if(!loginDatabase.PExecute("UPDATE account SET sha_pass_hash=SHA1(CONCAT(username,':','%s')) WHERE id='%d'", new_passwd.c_str(), accid)) return AOR_DB_INTERNAL_ERROR; // unexpected error return AOR_OK; @@ -153,8 +153,8 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accid, std::string new_passwd) uint32 AccountMgr::GetId(std::string username) { - LoginDatabase.escape_string(username); - QueryResult *result = LoginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'", username.c_str()); + loginDatabase.escape_string(username); + QueryResult *result = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'", username.c_str()); if(!result) return 0; else @@ -167,7 +167,7 @@ uint32 AccountMgr::GetId(std::string username) uint32 AccountMgr::GetSecurity(uint32 acc_id) { - QueryResult *result = LoginDatabase.PQuery("SELECT gmlevel FROM account WHERE id = '%u'", acc_id); + QueryResult *result = loginDatabase.PQuery("SELECT gmlevel FROM account WHERE id = '%u'", acc_id); if(result) { uint32 sec = (*result)[0].GetUInt32(); @@ -180,7 +180,7 @@ uint32 AccountMgr::GetSecurity(uint32 acc_id) bool AccountMgr::GetName(uint32 acc_id, std::string &name) { - QueryResult *result = LoginDatabase.PQuery("SELECT username FROM account WHERE id = '%u'", acc_id); + QueryResult *result = loginDatabase.PQuery("SELECT username FROM account WHERE id = '%u'", acc_id); if(result) { name = (*result)[0].GetCppString(); @@ -194,9 +194,9 @@ bool AccountMgr::GetName(uint32 acc_id, std::string &name) bool AccountMgr::CheckPassword(uint32 accid, std::string passwd) { normalizeString(passwd); - LoginDatabase.escape_string(passwd); + loginDatabase.escape_string(passwd); - QueryResult *result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d' AND sha_pass_hash=SHA1(CONCAT(username,':','%s'))", accid, passwd.c_str()); + QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d' AND sha_pass_hash=SHA1(CONCAT(username,':','%s'))", accid, passwd.c_str()); if (result) { delete result; diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp index e86fb543ed5..09ae58eea8d 100644 --- a/src/game/AchievementMgr.cpp +++ b/src/game/AchievementMgr.cpp @@ -35,6 +35,9 @@ #include "ProgressBar.h" #include "SpellMgr.h" +#include "MapManager.h" +#include "BattleGround.h" +#include "BattleGroundAB.h" INSTANTIATE_SINGLETON_1(AchievementGlobalMgr); @@ -81,11 +84,13 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) switch(criteria->requiredType) { case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: // only hardcoded list + case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: // only hardcoded list case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: + case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: break; @@ -139,7 +144,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD: if (player_dead.own_team_flag > 1) { - sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong boolean value1 (%u).", + sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD (%u) have wrong boolean value1 (%u).", criteria->ID, criteria->requiredType,dataType,player_dead.own_team_flag); return false; } @@ -228,10 +233,12 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) if (!sHolidaysStore.LookupEntry(holiday.id)) { sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY (%u) have unknown holiday in value1 (%u), ignore.", - criteria->ID, criteria->requiredType,dataType,drunk.state); + criteria->ID, criteria->requiredType,dataType,holiday.id); return false; } return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE: + return true; // not check correctness node indexes default: sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) have data for not supported data type (%u), ignore.", criteria->ID, criteria->requiredType,dataType); return false; @@ -300,6 +307,13 @@ bool AchievementCriteriaData::Meets(Player const* source, Unit const* target, ui return Player::GetDrunkenstateByValue(source->GetDrunkValue()) >= drunk.state; case ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY: return IsHolidayActive(HolidayIds(holiday.id)); + case ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE: + { + BattleGround* bg = source->GetBattleGround(); + if(!bg) + return false; + return bg->IsTeamScoreInRange(source->GetTeam()==ALLIANCE ? HORDE : ALLIANCE,bg_loss_team_score.min_score,bg_loss_team_score.max_score); + } } return false; } @@ -535,7 +549,7 @@ void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *cri if (!criteria) { // we will remove not existed criteria for all characters - sLog.outError("Not existed achievement creataria %u data removed from table `character_achievement_progress`.",id); + sLog.outError("Not existed achievement criteria %u data removed from table `character_achievement_progress`.",id); CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE criteria = %u",id); continue; } @@ -678,7 +692,6 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui // std. case: increment at 1 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED: case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED: @@ -719,6 +732,54 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui // specialized cases + case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: + { + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + if (achievementCriteria->win_bg.bgMapID != GetPlayer()->GetMapId()) + continue; + + if (achievementCriteria->win_bg.additionalRequirement1_type) + { + // those requirements couldn't be found in the dbc + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if (!data || !data->Meets(GetPlayer(),unit)) + continue; + } + // some hardcoded requirements + else + { + BattleGround* bg = GetPlayer()->GetBattleGround(); + if (!bg) + continue; + + switch(achievementCriteria->referredAchievement) + { + case 161: // AB, Overcome a 500 resource disadvantage + { + if (bg->GetTypeID() != BATTLEGROUND_AB) + continue; + if(!((BattleGroundAB*)bg)->IsTeamScores500disadvantage(GetPlayer()->GetTeam())) + continue; + break; + } + case 156: // AB, win while controlling all 5 flags (all nodes) + case 784: // EY, win while holding 4 bases (all nodes) + { + if(!bg->IsAllNodesConrolledByTeam(GetPlayer()->GetTeam())) + continue; + break; + } + case 1762: // SA, win without losing any siege vehicles + case 2192: // SA, win without losing any siege vehicles + continue; // not implemented + } + } + + SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); + break; + } case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: { // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case @@ -827,8 +888,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if(!miscvalue1) continue; - Map const* map = GetPlayer()->GetMap(); - if(!map->IsDungeon()) + Map const* map = GetPlayer()->IsInWorld() ? GetPlayer()->GetMap() : MapManager::Instance().FindMap(GetPlayer()->GetMapId(), GetPlayer()->GetInstanceId()); + if(!map || !map->IsDungeon()) continue; // search case @@ -1194,6 +1255,24 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui SetCriteriaProgress(achievementCriteria, spellCount); break; } + case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + + if(achievementCriteria->win_duel.duelCount) + { + // those requirements couldn't be found in the dbc + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if(!data) + continue; + + if(!data->Meets(GetPlayer(),unit)) + continue; + } + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetReveredFactionCount()); break; @@ -1270,7 +1349,6 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING: break; // FIXME: not triggered in code as result, need to implement - case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY: case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID: case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE: @@ -1298,7 +1376,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if(IsCompletedCriteria(achievementCriteria,achievement)) CompletedCriteriaFor(achievement); - // check again the completeness for SUMM and REQ COUNT achievements, + // check again the completeness for SUMM and REQ COUNT achievements, // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria if (achievement->flags & ACHIEVEMENT_FLAG_SUMM) { @@ -1339,6 +1417,8 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve switch(achievementCriteria->requiredType) { + case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: + return progress->counter >= achievementCriteria->win_bg.winCount; case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: return progress->counter >= achievementCriteria->kill_creature.creatureCount; case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: @@ -1430,7 +1510,6 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve return progress->counter >= achievementCriteria->learn_skill_line.spellCount; case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: return progress->counter >= achievementCriteria->honorable_kill.killCount; - // handle all statistic-only criteria here case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: @@ -1514,7 +1593,7 @@ bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry) return false; } - // Default case - need complete all or + // Default case - need complete all or bool completed_all = true; for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr) { @@ -1844,6 +1923,10 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData() switch(criteria->requiredType) { + case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: + if(!criteria->win_bg.additionalRequirement1_type) + continue; + break; case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: break; // any cases case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: @@ -1878,6 +1961,10 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData() if(criteria->do_emote.count==0) continue; break; + case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: // skip statistics + if(criteria->win_duel.duelCount==0) + continue; + break; case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: // any cases break; case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: // need skip generic cases diff --git a/src/game/AchievementMgr.h b/src/game/AchievementMgr.h index 9a3ff496e7b..5020539bb5c 100644 --- a/src/game/AchievementMgr.h +++ b/src/game/AchievementMgr.h @@ -51,17 +51,18 @@ enum AchievementCriteriaDataType ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA = 6, // area id 0 ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA = 7, // spell_id effect_idx ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE = 8, // minvalue value provided with achievement update must be not less that limit - ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL = 9, // minlevel minlevel of target - ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER = 10,// gender 0=male; 1=female + ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL = 9, // minlevel minlevel of target + ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER = 10,// gender 0=male; 1=female ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED = 11,// used to prevent achievement creteria complete if not all requirement implemented and listed in table ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY = 12,// difficulty normal/heroic difficulty for current event map ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT = 13,// count "with less than %u people in the zone" ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM = 14,// team HORDE(67), ALLIANCE(469) ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK = 15,// drunken_state 0 (enum DrunkenState) of player ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY = 16,// holiday_id 0 event in holiday time + ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE = 17,// min_score max_score player's team win bg and opposition team have team score in range }; -#define MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE 17 // maximum value in AchievementCriteriaDataType enum +#define MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE 18 // maximum value in AchievementCriteriaDataType enum class Player; class Unit; @@ -141,11 +142,17 @@ struct AchievementCriteriaData { uint32 state; } drunk; - // ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY + // ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY = 16 struct { - uint16 id; + uint32 id; } holiday; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE= 17 + struct + { + uint32 min_score; + uint32 max_score; + } bg_loss_team_score; // ... struct { @@ -227,6 +234,7 @@ class AchievementMgr void SaveToDB(); void ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0); void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0, Unit *unit=NULL, uint32 time=0); + void CompletedAchievement(AchievementEntry const* entry); void CheckAllAchievementCriteria(); void SendAllAchievementData(); void SendRespondInspectAchievements(Player* player); @@ -238,7 +246,6 @@ class AchievementMgr void SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress); void SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype = PROGRESS_SET); void CompletedCriteriaFor(AchievementEntry const* achievement); - void CompletedAchievement(AchievementEntry const* entry); bool IsCompletedCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement); bool IsCompletedAchievement(AchievementEntry const* entry); void CompleteAchievementsWithRefs(AchievementEntry const* entry); diff --git a/src/game/AggressorAI.cpp b/src/game/AggressorAI.cpp index d827e3e2672..3050c772e28 100644 --- a/src/game/AggressorAI.cpp +++ b/src/game/AggressorAI.cpp @@ -91,3 +91,58 @@ void SpellAI::UpdateAI(const uint32 diff) else DoMeleeAttackIfReady(); } + +void SpellCasterAI::InitializeAI() +{ + SpellAI::InitializeAI(); + float m_attackDist = 30.0f; + for(SpellVct::iterator itr = spells.begin(); itr != spells.end(); ++itr) + { + if (AISpellInfo[*itr].condition == AICOND_COMBAT && m_attackDist > GetAISpellInfo(*itr)->maxRange) + m_attackDist = GetAISpellInfo(*itr)->maxRange; + } + if (m_attackDist == 30.0f) + m_attackDist = MELEE_RANGE; +} + +void SpellCasterAI::EnterCombat(Unit *who) +{ + if (spells.empty()) + return; + + uint32 spell = rand() % spells.size(); + uint32 count = 0; + for(SpellVct::iterator itr = spells.begin(); itr != spells.end(); ++itr, ++count) + { + if(AISpellInfo[*itr].condition == AICOND_AGGRO) + me->CastSpell(who, *itr, false); + else if (AISpellInfo[*itr].condition == AICOND_COMBAT) + { + uint32 cooldown = GetAISpellInfo(*itr)->realCooldown; + if (count == spell) + { + DoCast(spells[spell]); + cooldown += me->GetCurrentSpellCastTime(*itr); + } + events.ScheduleEvent(*itr, cooldown); + } + } +} + +void SpellCasterAI::UpdateAI(const uint32 diff) +{ + if(!UpdateVictim()) + return; + + events.Update(diff); + + if(me->hasUnitState(UNIT_STAT_CASTING)) + return; + + if(uint32 spellId = events.ExecuteEvent()) + { + DoCast(spellId); + uint32 casttime = me->GetCurrentSpellCastTime(spellId); + events.ScheduleEvent(spellId, (casttime ? casttime : 500) + GetAISpellInfo(spellId)->realCooldown); + } +} diff --git a/src/game/AggressorAI.h b/src/game/AggressorAI.h index 2c43ccf82b7..cec11c0fb22 100644 --- a/src/game/AggressorAI.h +++ b/src/game/AggressorAI.h @@ -48,9 +48,21 @@ class TRINITY_DLL_SPEC SpellAI : public CreatureAI void JustDied(Unit *killer); void UpdateAI(const uint32 diff); static int Permissible(const Creature *); - private: + protected: EventMap events; SpellVct spells; }; +class TRINITY_DLL_SPEC SpellCasterAI : public SpellAI +{ + public: + explicit SpellCasterAI(Creature *c) : SpellAI(c) {m_attackDist = MELEE_RANGE;} + void InitializeAI(); + void AttackStart(Unit * victim){SpellAI::AttackStartCaster(victim, m_attackDist);} + void UpdateAI(const uint32 diff); + void EnterCombat(Unit *who); + private: + float m_attackDist; +}; + #endif diff --git a/src/game/ArenaTeam.cpp b/src/game/ArenaTeam.cpp index 23050dcf25c..22d121c38c1 100644 --- a/src/game/ArenaTeam.cpp +++ b/src/game/ArenaTeam.cpp @@ -20,6 +20,7 @@ #include "WorldPacket.h" #include "ArenaTeam.h" +#include "World.h" void ArenaTeamMember::ModifyPersonalRating(Player* plr, int32 mod, uint32 slot) { @@ -32,21 +33,24 @@ void ArenaTeamMember::ModifyPersonalRating(Player* plr, int32 mod, uint32 slot) ArenaTeam::ArenaTeam() { - Id = 0; - Type = 0; - Name = ""; - CaptainGuid = 0; - BackgroundColor = 0; // background - EmblemStyle = 0; // icon - EmblemColor = 0; // icon color - BorderStyle = 0; // border - BorderColor = 0; // border color - stats.games_week = 0; - stats.games_season = 0; - stats.rank = 0; - stats.rating = ARENA_NEW_TEAM_RATING; - stats.wins_week = 0; - stats.wins_season = 0; + m_TeamId = 0; + m_Type = 0; + m_Name = ""; + m_CaptainGuid = 0; + m_BackgroundColor = 0; // background + m_EmblemStyle = 0; // icon + m_EmblemColor = 0; // icon color + m_BorderStyle = 0; // border + m_BorderColor = 0; // border color + m_stats.games_week = 0; + m_stats.games_season = 0; + m_stats.rank = 0; + if (sWorld.getConfig(CONFIG_ARENA_SEASON_ID) >= 6) + m_stats.rating = 0; + else + m_stats.rating = 1500; + m_stats.wins_week = 0; + m_stats.wins_season = 0; } ArenaTeam::~ArenaTeam() @@ -62,27 +66,27 @@ bool ArenaTeam::Create(uint64 captainGuid, uint32 type, std::string ArenaTeamNam sLog.outDebug("GUILD: creating arena team %s to leader: %u", ArenaTeamName.c_str(), GUID_LOPART(captainGuid)); - CaptainGuid = captainGuid; - Name = ArenaTeamName; - Type = type; + m_CaptainGuid = captainGuid; + m_Name = ArenaTeamName; + m_Type = type; - Id = objmgr.GenerateArenaTeamId(); + m_TeamId = objmgr.GenerateArenaTeamId(); // ArenaTeamName already assigned to ArenaTeam::name, use it to encode string for DB CharacterDatabase.escape_string(ArenaTeamName); CharacterDatabase.BeginTransaction(); - // CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid='%u'", Id); - MAX(arenateam)+1 not exist - CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid='%u'", Id); + // CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid='%u'", m_TeamId); - MAX(arenateam)+1 not exist + CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid='%u'", m_TeamId); CharacterDatabase.PExecute("INSERT INTO arena_team (arenateamid,name,captainguid,type,BackgroundColor,EmblemStyle,EmblemColor,BorderStyle,BorderColor) " "VALUES('%u','%s','%u','%u','%u','%u','%u','%u','%u')", - Id, ArenaTeamName.c_str(), GUID_LOPART(CaptainGuid), Type, BackgroundColor, EmblemStyle, EmblemColor, BorderStyle, BorderColor); + m_TeamId, ArenaTeamName.c_str(), GUID_LOPART(m_CaptainGuid), m_Type, m_BackgroundColor, m_EmblemStyle, m_EmblemColor, m_BorderStyle, m_BorderColor); CharacterDatabase.PExecute("INSERT INTO arena_team_stats (arenateamid, rating, games, wins, played, wins2, rank) VALUES " - "('%u', '%u', '%u', '%u', '%u', '%u', '%u')", Id, stats.rating, stats.games_week, stats.wins_week, stats.games_season, stats.wins_season, stats.rank); + "('%u', '%u', '%u', '%u', '%u', '%u', '%u')", m_TeamId, m_stats.rating, m_stats.games_week, m_stats.wins_week, m_stats.games_season, m_stats.wins_season, m_stats.rank); CharacterDatabase.CommitTransaction(); - AddMember(CaptainGuid); + AddMember(m_CaptainGuid); sLog.outArena("New ArenaTeam created [Id: %u] [Type: %u] [Captain GUID: %u]", GetId(), GetType(), GetCaptain()); return true; } @@ -139,19 +143,29 @@ bool ArenaTeam::AddMember(const uint64& PlayerGuid) newmember.games_week = 0; newmember.wins_season = 0; newmember.wins_week = 0; - newmember.personal_rating = AREAN_NEW_PERSONAL_RATING; - members.push_back(newmember); + if (sWorld.getConfig(CONFIG_ARENA_SEASON_ID) >= 6) + { + if (m_stats.rating < 1000) + newmember.personal_rating = m_stats.rating; + else + newmember.personal_rating = 1000; + } + else + { + newmember.personal_rating = 1500; + } + m_members.push_back(newmember); - CharacterDatabase.PExecute("INSERT INTO arena_team_member (arenateamid, guid, personal_rating) VALUES ('%u', '%u', '%u')", Id, GUID_LOPART(newmember.guid), newmember.personal_rating ); + CharacterDatabase.PExecute("INSERT INTO arena_team_member (arenateamid, guid, personal_rating) VALUES ('%u', '%u', '%u')", m_TeamId, GUID_LOPART(newmember.guid), newmember.personal_rating ); if(pl) { - pl->SetInArenaTeam(Id, GetSlot()); + pl->SetInArenaTeam(m_TeamId, GetSlot()); pl->SetArenaTeamIdInvited(0); pl->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot()*6) + 5, newmember.personal_rating ); // hide promote/remove buttons - if(CaptainGuid != PlayerGuid) + if(m_CaptainGuid != PlayerGuid) pl->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6) + 1, 1); sLog.outArena("Player: %s [GUID: %u] joined arena team type: %u [Id: %u].", pl->GetName(), pl->GetGUIDLow(), GetType(), GetId()); } @@ -167,15 +181,15 @@ bool ArenaTeam::LoadArenaTeamFromDB(uint32 ArenaTeamId) Field *fields = result->Fetch(); - Id = fields[0].GetUInt32(); - Name = fields[1].GetCppString(); - CaptainGuid = MAKE_NEW_GUID(fields[2].GetUInt32(), 0, HIGHGUID_PLAYER); - Type = fields[3].GetUInt32(); - BackgroundColor = fields[4].GetUInt32(); - EmblemStyle = fields[5].GetUInt32(); - EmblemColor = fields[6].GetUInt32(); - BorderStyle = fields[7].GetUInt32(); - BorderColor = fields[8].GetUInt32(); + m_TeamId = fields[0].GetUInt32(); + m_Name = fields[1].GetCppString(); + m_CaptainGuid = MAKE_NEW_GUID(fields[2].GetUInt32(), 0, HIGHGUID_PLAYER); + m_Type = fields[3].GetUInt32(); + m_BackgroundColor = fields[4].GetUInt32(); + m_EmblemStyle = fields[5].GetUInt32(); + m_EmblemColor = fields[6].GetUInt32(); + m_BorderStyle = fields[7].GetUInt32(); + m_BorderColor = fields[8].GetUInt32(); delete result; @@ -207,12 +221,12 @@ void ArenaTeam::LoadStatsFromDB(uint32 ArenaTeamId) Field *fields = result->Fetch(); - stats.rating = fields[0].GetUInt32(); - stats.games_week = fields[1].GetUInt32(); - stats.wins_week = fields[2].GetUInt32(); - stats.games_season = fields[3].GetUInt32(); - stats.wins_season = fields[4].GetUInt32(); - stats.rank = fields[5].GetUInt32(); + m_stats.rating = fields[0].GetUInt32(); + m_stats.games_week = fields[1].GetUInt32(); + m_stats.wins_week = fields[2].GetUInt32(); + m_stats.games_season = fields[3].GetUInt32(); + m_stats.wins_season = fields[4].GetUInt32(); + m_stats.rank = fields[5].GetUInt32(); delete result; } @@ -239,7 +253,7 @@ void ArenaTeam::LoadMembersFromDB(uint32 ArenaTeamId) newmember.personal_rating = fields[5].GetUInt32(); newmember.name = fields[6].GetCppString(); newmember.Class = fields[7].GetUInt8(); - members.push_back(newmember); + m_members.push_back(newmember); }while( result->NextRow() ); delete result; } @@ -252,10 +266,10 @@ void ArenaTeam::SetCaptain(const uint64& guid) oldcaptain->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1); // set new captain - CaptainGuid = guid; + m_CaptainGuid = guid; // update database - CharacterDatabase.PExecute("UPDATE arena_team SET captainguid = '%u' WHERE arenateamid = '%u'", GUID_LOPART(guid), Id); + CharacterDatabase.PExecute("UPDATE arena_team SET captainguid = '%u' WHERE arenateamid = '%u'", GUID_LOPART(guid), m_TeamId); // enable remove/promote buttons Player *newcaptain = objmgr.GetPlayer(guid); @@ -268,11 +282,11 @@ void ArenaTeam::SetCaptain(const uint64& guid) void ArenaTeam::DelMember(uint64 guid) { - for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { if (itr->guid == guid) { - members.erase(itr); + m_members.erase(itr); break; } } @@ -300,21 +314,21 @@ void ArenaTeam::Disband(WorldSession *session) session->BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_DISBANDED_S, 2, session->GetPlayerName(), GetName(), ""); BroadcastPacket(&data); - while (!members.empty()) + while (!m_members.empty()) { // Removing from members is done in DelMember. - DelMember(members.front().guid); + DelMember(m_members.front().guid); } if(Player *player = session->GetPlayer()) sLog.outArena("Player: %s [GUID: %u] disbanded arena team type: %u [Id: %u].", player->GetName(), player->GetGUIDLow(), GetType(), GetId()); CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid = '%u'", Id); - CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u'", Id); //< this should be alredy done by calling DelMember(memberGuids[j]); for each member - CharacterDatabase.PExecute("DELETE FROM arena_team_stats WHERE arenateamid = '%u'", Id); + CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid = '%u'", m_TeamId); + CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u'", m_TeamId); //< this should be alredy done by calling DelMember(memberGuids[j]); for each member + CharacterDatabase.PExecute("DELETE FROM arena_team_stats WHERE arenateamid = '%u'", m_TeamId); CharacterDatabase.CommitTransaction(); - objmgr.RemoveArenaTeam(Id); + objmgr.RemoveArenaTeam(m_TeamId); } void ArenaTeam::Roster(WorldSession *session) @@ -324,12 +338,12 @@ void ArenaTeam::Roster(WorldSession *session) uint8 unk308 = 0; WorldPacket data(SMSG_ARENA_TEAM_ROSTER, 100); - data << uint32(GetId()); // arena team id + data << uint32(GetId()); // team id data << uint8(unk308); // 308 unknown value but affect packet structure data << uint32(GetMembersSize()); // members count data << uint32(GetType()); // arena team type? - for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr) + for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { pl = objmgr.GetPlayer(itr->guid); @@ -361,11 +375,11 @@ void ArenaTeam::Query(WorldSession *session) data << uint32(GetId()); // team id data << GetName(); // team name data << uint32(GetType()); // arena team type (2=2x2, 3=3x3 or 5=5x5) - data << uint32(BackgroundColor); // background color - data << uint32(EmblemStyle); // emblem style - data << uint32(EmblemColor); // emblem color - data << uint32(BorderStyle); // border style - data << uint32(BorderColor); // border color + data << uint32(m_BackgroundColor); // background color + data << uint32(m_EmblemStyle); // emblem style + data << uint32(m_EmblemColor); // emblem color + data << uint32(m_BorderStyle); // border style + data << uint32(m_BorderColor); // border color session->SendPacket(&data); sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_QUERY_RESPONSE"); } @@ -373,13 +387,13 @@ void ArenaTeam::Query(WorldSession *session) void ArenaTeam::Stats(WorldSession *session) { WorldPacket data(SMSG_ARENA_TEAM_STATS, 4*7); - data << uint32(GetId()); // arena team id - data << uint32(stats.rating); // rating - data << uint32(stats.games_week); // games this week - data << uint32(stats.wins_week); // wins this week - data << uint32(stats.games_season); // played this season - data << uint32(stats.wins_season); // wins this season - data << uint32(stats.rank); // rank + data << uint32(GetId()); // team id + data << uint32(m_stats.rating); // rating + data << uint32(m_stats.games_week); // games this week + data << uint32(m_stats.wins_week); // wins this week + data << uint32(m_stats.games_season); // played this season + data << uint32(m_stats.wins_season); // wins this season + data << uint32(m_stats.rank); // rank session->SendPacket(&data); } @@ -387,7 +401,7 @@ void ArenaTeam::NotifyStatsChanged() { // this is called after a rated match ended // updates arena team stats for every member of the team (not only the ones who participated!) - for(MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr) + for(MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { Player * plr = objmgr.GetPlayer(itr->guid); if(plr) @@ -405,9 +419,9 @@ void ArenaTeam::InspectStats(WorldSession *session, uint64 guid) data << uint64(guid); // player guid data << uint8(GetSlot()); // slot (0...2) data << uint32(GetId()); // arena team id - data << uint32(stats.rating); // rating - data << uint32(stats.games_season); // season played - data << uint32(stats.wins_season); // season wins + data << uint32(m_stats.rating); // rating + data << uint32(m_stats.games_season); // season played + data << uint32(m_stats.wins_season); // season wins data << uint32(member->games_season); // played (count of all games, that the inspected member participated...) data << uint32(member->personal_rating); // personal rating session->SendPacket(&data); @@ -415,13 +429,13 @@ void ArenaTeam::InspectStats(WorldSession *session, uint64 guid) void ArenaTeam::SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor) { - BackgroundColor = backgroundColor; - EmblemStyle = emblemStyle; - EmblemColor = emblemColor; - BorderStyle = borderStyle; - BorderColor = borderColor; + m_BackgroundColor = backgroundColor; + m_EmblemStyle = emblemStyle; + m_EmblemColor = emblemColor; + m_BorderStyle = borderStyle; + m_BorderColor = borderColor; - CharacterDatabase.PExecute("UPDATE arena_team SET BackgroundColor='%u', EmblemStyle='%u', EmblemColor='%u', BorderStyle='%u', BorderColor='%u' WHERE arenateamid='%u'", BackgroundColor, EmblemStyle, EmblemColor, BorderStyle, BorderColor, Id); + CharacterDatabase.PExecute("UPDATE arena_team SET BackgroundColor='%u', EmblemStyle='%u', EmblemColor='%u', BorderStyle='%u', BorderColor='%u' WHERE arenateamid='%u'", m_BackgroundColor, m_EmblemStyle, m_EmblemColor, m_BorderStyle, m_BorderColor, m_TeamId); } void ArenaTeam::SetStats(uint32 stat_type, uint32 value) @@ -429,27 +443,27 @@ void ArenaTeam::SetStats(uint32 stat_type, uint32 value) switch(stat_type) { case STAT_TYPE_RATING: - stats.rating = value; + m_stats.rating = value; CharacterDatabase.PExecute("UPDATE arena_team_stats SET rating = '%u' WHERE arenateamid = '%u'", value, GetId()); break; case STAT_TYPE_GAMES_WEEK: - stats.games_week = value; + m_stats.games_week = value; CharacterDatabase.PExecute("UPDATE arena_team_stats SET games = '%u' WHERE arenateamid = '%u'", value, GetId()); break; case STAT_TYPE_WINS_WEEK: - stats.wins_week = value; + m_stats.wins_week = value; CharacterDatabase.PExecute("UPDATE arena_team_stats SET wins = '%u' WHERE arenateamid = '%u'", value, GetId()); break; case STAT_TYPE_GAMES_SEASON: - stats.games_season = value; + m_stats.games_season = value; CharacterDatabase.PExecute("UPDATE arena_team_stats SET played = '%u' WHERE arenateamid = '%u'", value, GetId()); break; case STAT_TYPE_WINS_SEASON: - stats.wins_season = value; + m_stats.wins_season = value; CharacterDatabase.PExecute("UPDATE arena_team_stats SET wins2 = '%u' WHERE arenateamid = '%u'", value, GetId()); break; case STAT_TYPE_RANK: - stats.rank = value; + m_stats.rank = value; CharacterDatabase.PExecute("UPDATE arena_team_stats SET rank = '%u' WHERE arenateamid = '%u'", value, GetId()); break; default: @@ -460,7 +474,7 @@ void ArenaTeam::SetStats(uint32 stat_type, uint32 value) void ArenaTeam::BroadcastPacket(WorldPacket *packet) { - for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr) + for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { Player *player = objmgr.GetPlayer(itr->guid); if(player) @@ -484,7 +498,7 @@ uint8 ArenaTeam::GetSlotByType( uint32 type ) bool ArenaTeam::HaveMember( const uint64& guid ) const { - for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr) + for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) if(itr->guid == guid) return true; @@ -496,17 +510,18 @@ uint32 ArenaTeam::GetPoints(uint32 MemberRating) // returns how many points would be awarded with this team type with this rating float points; - uint32 rating = MemberRating + 150 < stats.rating ? MemberRating : stats.rating; + uint32 rating = MemberRating + 150 < m_stats.rating ? MemberRating : m_stats.rating; if(rating<=1500) - points = (float)rating * 0.22f + 14.0f; + // points = (float)1500 * 0.22f + 14.0f; + points = 344.0f; // 3.1 change - teams with rating below 1500 get arena points for 1500 rating else points = 1511.26f / (1.0f + 1639.28f * exp(-0.00412f * (float)rating)); // type penalties for <5v5 teams - if(Type == ARENA_TEAM_2v2) + if(m_Type == ARENA_TEAM_2v2) points *= 0.76f; - else if(Type == ARENA_TEAM_3v3) + else if(m_Type == ARENA_TEAM_3v3) points *= 0.88f; return (uint32) points; @@ -516,31 +531,43 @@ float ArenaTeam::GetChanceAgainst(uint32 own_rating, uint32 enemy_rating) { // returns the chance to win against a team with the given rating, used in the rating adjustment calculation // ELO system + + if (sWorld.getConfig(CONFIG_ARENA_SEASON_ID) >= 6) + if (enemy_rating < 1300) + enemy_rating = 1300; return 1.0f/(1.0f+exp(log(10.0f)*(float)((float)enemy_rating - (float)own_rating)/400.0f)); } +void ArenaTeam::FinishGame(int32 mod) +{ + if (int32(m_stats.rating) + mod < 0) + m_stats.rating = 0; + else + m_stats.rating += mod; + + m_stats.games_week += 1; + m_stats.games_season += 1; + // update team's rank + m_stats.rank = 1; + ObjectMgr::ArenaTeamMap::const_iterator i = objmgr.GetArenaTeamMapBegin(); + for ( ; i != objmgr.GetArenaTeamMapEnd(); ++i) + { + if (i->second->GetType() == m_Type && i->second->GetStats().rating > m_stats.rating) + ++m_stats.rank; + } +} + int32 ArenaTeam::WonAgainst(uint32 againstRating) { // called when the team has won //'chance' calculation - to beat the opponent - float chance = GetChanceAgainst(stats.rating,againstRating); + float chance = GetChanceAgainst(m_stats.rating, againstRating); // calculate the rating modification (ELO system with k=32) int32 mod = (int32)floor(32.0f * (1.0f - chance)); // modify the team stats accordingly - int32 newTeamRating = (int32)stats.rating + mod; - stats.rating = newTeamRating > 0 ? newTeamRating : 0; - stats.games_week += 1; - stats.wins_week += 1; - stats.games_season += 1; - stats.wins_season += 1; - //update team's rank - stats.rank = 1; - ObjectMgr::ArenaTeamMap::const_iterator i = objmgr.GetArenaTeamMapBegin(); - for ( ; i != objmgr.GetArenaTeamMapEnd(); ++i) - { - if (i->second->GetType() == this->Type && i->second->GetStats().rating > stats.rating) - ++stats.rank; - } + FinishGame(mod); + m_stats.wins_week += 1; + m_stats.wins_season += 1; // return the rating change, used to display it on the results screen return mod; @@ -550,23 +577,11 @@ int32 ArenaTeam::LostAgainst(uint32 againstRating) { // called when the team has lost //'chance' calculation - to loose to the opponent - float chance = GetChanceAgainst(stats.rating,againstRating); + float chance = GetChanceAgainst(m_stats.rating, againstRating); // calculate the rating modification (ELO system with k=32) int32 mod = (int32)ceil(32.0f * (0.0f - chance)); // modify the team stats accordingly - int32 newTeamRating = (int32)stats.rating + mod; - stats.rating = newTeamRating > 0 ? newTeamRating : 0; - stats.games_week += 1; - stats.games_season += 1; - //update team's rank - - stats.rank = 1; - ObjectMgr::ArenaTeamMap::const_iterator i = objmgr.GetArenaTeamMapBegin(); - for ( ; i != objmgr.GetArenaTeamMapEnd(); ++i) - { - if (i->second->GetType() == this->Type && i->second->GetStats().rating > stats.rating) - ++stats.rank; - } + FinishGame(mod); // return the rating change, used to display it on the results screen return mod; @@ -575,7 +590,7 @@ int32 ArenaTeam::LostAgainst(uint32 againstRating) void ArenaTeam::MemberLost(Player * plr, uint32 againstRating) { // called for each participant of a match after losing - for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + for(MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { if(itr->guid == plr->GetGUID()) { @@ -597,7 +612,7 @@ void ArenaTeam::MemberLost(Player * plr, uint32 againstRating) void ArenaTeam::OfflineMemberLost(uint64 guid, uint32 againstRating) { // called for offline player after ending rated arena match! - for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + for(MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { if(itr->guid == guid) { @@ -619,7 +634,7 @@ void ArenaTeam::OfflineMemberLost(uint64 guid, uint32 againstRating) void ArenaTeam::MemberWon(Player * plr, uint32 againstRating) { // called for each participant after winning a match - for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + for(MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { if(itr->guid == plr->GetGUID()) { @@ -645,17 +660,17 @@ void ArenaTeam::UpdateArenaPointsHelper(std::map<uint32, uint32>& PlayerPoints) // called after a match has ended and the stats are already modified // helper function for arena point distribution (this way, when distributing, no actual calculation is required, just a few comparisons) // 10 played games per week is a minimum - if (stats.games_week < 10) + if (m_stats.games_week < 10) return; // to get points, a player has to participate in at least 30% of the matches - uint32 min_plays = (uint32) ceil(stats.games_week * 0.3); - for(MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr) + uint32 min_plays = (uint32) ceil(m_stats.games_week * 0.3); + for(MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { // the player participated in enough games, update his points uint32 points_to_add = 0; if (itr->games_week >= min_plays) points_to_add = GetPoints(itr->personal_rating); - // OBSOLETE : CharacterDatabase.PExecute("UPDATE arena_team_member SET points_to_add = '%u' WHERE arenateamid = '%u' AND guid = '%u'", points_to_add, Id, itr->guid); + // OBSOLETE : CharacterDatabase.PExecute("UPDATE arena_team_member SET points_to_add = '%u' WHERE arenateamid = '%u' AND guid = '%u'", points_to_add, m_TeamId, itr->guid); std::map<uint32, uint32>::iterator plr_itr = PlayerPoints.find(GUID_LOPART(itr->guid)); if (plr_itr != PlayerPoints.end()) @@ -674,19 +689,19 @@ void ArenaTeam::SaveToDB() // save team and member stats to db // called after a match has ended, or when calculating arena_points CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("UPDATE arena_team_stats SET rating = '%u',games = '%u',played = '%u',rank = '%u',wins = '%u',wins2 = '%u' WHERE arenateamid = '%u'", stats.rating, stats.games_week, stats.games_season, stats.rank, stats.wins_week, stats.wins_season, GetId()); - for(MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr) + CharacterDatabase.PExecute("UPDATE arena_team_stats SET rating = '%u',games = '%u',played = '%u',rank = '%u',wins = '%u',wins2 = '%u' WHERE arenateamid = '%u'", m_stats.rating, m_stats.games_week, m_stats.games_season, m_stats.rank, m_stats.wins_week, m_stats.wins_season, GetId()); + for(MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { - CharacterDatabase.PExecute("UPDATE arena_team_member SET played_week = '%u', wons_week = '%u', played_season = '%u', wons_season = '%u', personal_rating = '%u' WHERE arenateamid = '%u' AND guid = '%u'", itr->games_week, itr->wins_week, itr->games_season, itr->wins_season, itr->personal_rating, Id, GUID_LOPART(itr->guid)); + CharacterDatabase.PExecute("UPDATE arena_team_member SET played_week = '%u', wons_week = '%u', played_season = '%u', wons_season = '%u', personal_rating = '%u' WHERE arenateamid = '%u' AND guid = '%u'", itr->games_week, itr->wins_week, itr->games_season, itr->wins_season, itr->personal_rating, m_TeamId, GUID_LOPART(itr->guid)); } CharacterDatabase.CommitTransaction(); } void ArenaTeam::FinishWeek() { - stats.games_week = 0; // played this week - stats.wins_week = 0; // wins this week - for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + m_stats.games_week = 0; // played this week + m_stats.wins_week = 0; // wins this week + for(MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { itr->games_week = 0; itr->wins_week = 0; @@ -695,7 +710,7 @@ void ArenaTeam::FinishWeek() bool ArenaTeam::IsFighting() const { - for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr) + for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { if (Player *p = objmgr.GetPlayer(itr->guid)) { diff --git a/src/game/ArenaTeam.h b/src/game/ArenaTeam.h index 2dc5b45e880..dbeac2f09f0 100644 --- a/src/game/ArenaTeam.h +++ b/src/game/ArenaTeam.h @@ -85,9 +85,6 @@ enum ArenaTeamTypes ARENA_TEAM_5v5 = 5 }; -#define ARENA_NEW_TEAM_RATING 0 -#define AREAN_NEW_PERSONAL_RATING 0 - struct ArenaTeamMember { uint64 guid; @@ -120,26 +117,26 @@ class ArenaTeam ArenaTeam(); ~ArenaTeam(); - bool Create(uint64 CaptainGuid, uint32 type, std::string ArenaTeamName); + bool Create(uint64 captainGuid, uint32 type, std::string ArenaTeamName); void Disband(WorldSession *session); typedef std::list<ArenaTeamMember> MemberList; - uint32 GetId() const { return Id; } - uint32 GetType() const { return Type; } + uint32 GetId() const { return m_TeamId; } + uint32 GetType() const { return m_Type; } uint8 GetSlot() const { return GetSlotByType(GetType()); } static uint8 GetSlotByType(uint32 type); - const uint64& GetCaptain() const { return CaptainGuid; } - std::string GetName() const { return Name; } - const ArenaTeamStats& GetStats() const { return stats; } + const uint64& GetCaptain() const { return m_CaptainGuid; } + std::string GetName() const { return m_Name; } + const ArenaTeamStats& GetStats() const { return m_stats; } void SetStats(uint32 stat_type, uint32 value); - uint32 GetRating() const { return stats.rating; } + uint32 GetRating() const { return m_stats.rating; } - uint32 GetEmblemStyle() const { return EmblemStyle; } - uint32 GetEmblemColor() const { return EmblemColor; } - uint32 GetBorderStyle() const { return BorderStyle; } - uint32 GetBorderColor() const { return BorderColor; } - uint32 GetBackgroundColor() const { return BackgroundColor; } + uint32 GetEmblemStyle() const { return m_EmblemStyle; } + uint32 GetEmblemColor() const { return m_EmblemColor; } + uint32 GetBorderStyle() const { return m_BorderStyle; } + uint32 GetBorderColor() const { return m_BorderColor; } + uint32 GetBackgroundColor() const { return m_BackgroundColor; } void SetCaptain(const uint64& guid); bool AddMember(const uint64& PlayerGuid); @@ -150,15 +147,15 @@ class ArenaTeam void SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor); - size_t GetMembersSize() const { return members.size(); } - bool Empty() const { return members.empty(); } - MemberList::iterator membersBegin() { return members.begin(); } - MemberList::iterator membersEnd() { return members.end(); } + size_t GetMembersSize() const { return m_members.size(); } + bool Empty() const { return m_members.empty(); } + MemberList::iterator m_membersBegin() { return m_members.begin(); } + MemberList::iterator m_membersEnd() { return m_members.end(); } bool HaveMember(const uint64& guid) const; ArenaTeamMember* GetMember(const uint64& guid) { - for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) if(itr->guid == guid) return &(*itr); @@ -167,7 +164,7 @@ class ArenaTeam ArenaTeamMember* GetMember(const std::string& name) { - for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) if(itr->name == name) return &(*itr); @@ -202,22 +199,23 @@ class ArenaTeam void NotifyStatsChanged(); void FinishWeek(); + void FinishGame(int32 mod); protected: - uint32 Id; - uint32 Type; - std::string Name; - uint64 CaptainGuid; + uint32 m_TeamId; + uint32 m_Type; + std::string m_Name; + uint64 m_CaptainGuid; - uint32 BackgroundColor; // ARGB format - uint32 EmblemStyle; // icon id - uint32 EmblemColor; // ARGB format - uint32 BorderStyle; // border image id - uint32 BorderColor; // ARGB format + uint32 m_BackgroundColor; // ARGB format + uint32 m_EmblemStyle; // icon id + uint32 m_EmblemColor; // ARGB format + uint32 m_BorderStyle; // border image id + uint32 m_BorderColor; // ARGB format - MemberList members; - ArenaTeamStats stats; + MemberList m_members; + ArenaTeamStats m_stats; }; #endif diff --git a/src/game/AuctionHouseBot.cpp b/src/game/AuctionHouseBot.cpp index c2af4c92c15..3e53f3f4e9b 100644 --- a/src/game/AuctionHouseBot.cpp +++ b/src/game/AuctionHouseBot.cpp @@ -1,73 +1,83 @@ -#include <vector> -#include <iostream> -#include "time.h" - -#include "ObjectMgr.h" -#include "World.h" -#include "WorldSession.h" -#include "Config/ConfigEnv.h" -#include "Database/DatabaseEnv.h" - #include "AuctionHouseBot.h" +#include "ObjectMgr.h" #include "AuctionHouseMgr.h" -#include "Bag.h" -#include "Item.h" -#include "Log.h" -#include "Player.h" + +#include "Policies/SingletonImp.h" +INSTANTIATE_SINGLETON_1(AuctionHouseBot); using namespace std; -static bool debug_Out = sConfig.GetBoolDefault("AuctionHouseBot.DEBUG", false); - -static vector<uint32> npcItems; -static vector<uint32> lootItems; -static vector<uint32> greyTradeGoodsBin; -static vector<uint32> whiteTradeGoodsBin; -static vector<uint32> greenTradeGoodsBin; -static vector<uint32> blueTradeGoodsBin; -static vector<uint32> purpleTradeGoodsBin; -static vector<uint32> orangeTradeGoodsBin; -static vector<uint32> yellowTradeGoodsBin; -static vector<uint32> greyItemsBin; -static vector<uint32> whiteItemsBin; -static vector<uint32> greenItemsBin; -static vector<uint32> blueItemsBin; -static vector<uint32> purpleItemsBin; -static vector<uint32> orangeItemsBin; -static vector<uint32> yellowItemsBin; - -static bool AHBSeller = 0; -static bool AHBBuyer = 0; - -static bool Vendor_Items = 0; -static bool Loot_Items = 0; -static bool Other_Items = 0; - -static bool No_Bind = 0; -static bool Bind_When_Picked_Up = 0; -static bool Bind_When_Equipped = 0; -static bool Bind_When_Use = 0; -static bool Bind_Quest_Item = 0; - -static AHBConfig AllianceConfig = AHBConfig(2); -static AHBConfig HordeConfig = AHBConfig(6); -static AHBConfig NeutralConfig = AHBConfig(7); -time_t _lastrun_a; -time_t _lastrun_h; -time_t _lastrun_n; - -/////////////////////////////////////////////////////////////////////////////// -// -/////////////////////////////////////////////////////////////////////////////// -static inline uint32 minValue(uint32 a, uint32 b) +AuctionHouseBot::AuctionHouseBot() +{ + debug_Out = false; + + AHBSeller = false; + AHBBuyer = false; + + //Begin Filters + + Vendor_Items = false; + Loot_Items = false; + Other_Items = false; + + No_Bind = false; + Bind_When_Picked_Up = false; + Bind_When_Equipped = false; + Bind_When_Use = false; + Bind_Quest_Item = false; + + DisableBeta_PTR_Unused = false; + DisablePermEnchant = false; + DisableConjured = false; + DisableGems = false; + DisableMoney = false; + DisableMoneyLoot = false; + DisableLootable = false; + DisableKeys = false; + DisableDuration = false; + DisableBOP_Or_Quest_NoReqLevel = false; + + DisableWarriorItems = false; + DisablePaladinItems = false; + DisableHunterItems = false; + DisableRogueItems = false; + DisablePriestItems = false; + DisableDKItems = false; + DisableShamanItems = false; + DisableMageItems = false; + DisableWarlockItems = false; + DisableUnusedClassItems = false; + DisableDruidItems = false; + + DisableItemsBelowLevel = 0; + DisableItemsAboveLevel = 0; + DisableTGsBelowLevel = 0; + DisableTGsAboveLevel = 0; + DisableItemsBelowGUID = 0; + DisableItemsAboveGUID = 0; + DisableTGsBelowGUID = 0; + DisableTGsAboveGUID = 0; + DisableItemsBelowReqLevel = 0; + DisableItemsAboveReqLevel = 0; + DisableTGsBelowReqLevel = 0; + DisableTGsAboveReqLevel = 0; + DisableItemsBelowReqSkillRank = 0; + DisableItemsAboveReqSkillRank = 0; + DisableTGsBelowReqSkillRank = 0; + DisableTGsAboveReqSkillRank = 0; + + //End Filters + + AllianceConfig = AHBConfig(2); + HordeConfig = AHBConfig(6); + NeutralConfig = AHBConfig(7); +} + +AuctionHouseBot::~AuctionHouseBot() { - return a <= b ? a : b; } -/////////////////////////////////////////////////////////////////////////////// -// -/////////////////////////////////////////////////////////////////////////////// -static void addNewAuctions(Player *AHBplayer, AHBConfig *config) +void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config) { if (!AHBSeller) return; @@ -78,19 +88,26 @@ static void addNewAuctions(Player *AHBplayer, AHBConfig *config) uint32 maxItems = config->GetMaxItems(); uint32 auctions = auctionHouse->Getcount(); uint32 AuctioneerGUID = 0; - switch (config->GetAHID()){ - case 2: - AuctioneerGUID = 79707; //Human in stormwind. - case 6: - AuctioneerGUID = 4656; //orc in Orgrimmar - case 7: - AuctioneerGUID = 23442; //goblin in GZ - default: - AuctioneerGUID = 23442; //default to neutral 7 + switch (config->GetAHID()) + { + case 2: + AuctioneerGUID = 79707; //Human in stormwind. + break; + case 6: + AuctioneerGUID = 4656; //orc in Orgrimmar + break; + case 7: + AuctioneerGUID = 23442; //goblin in GZ + break; + default: + if (debug_Out) sLog.outError("GetAHID() - Default switch reached"); + AuctioneerGUID = 23442; //default to neutral 7 + break; } if (auctions >= minItems) - return; + return; + if (auctions <= maxItems) { if ((maxItems - auctions) > ItemsPerCycle) @@ -113,9 +130,9 @@ static void addNewAuctions(Player *AHBplayer, AHBConfig *config) uint32 orangeIcount = config->GetPercents(AHB_ORANGE_I); uint32 yellowIcount = config->GetPercents(AHB_YELLOW_I); uint32 total = greyTGcount + whiteTGcount + greenTGcount + blueTGcount - + purpleTGcount + orangeTGcount + yellowTGcount - + whiteIcount + greenIcount + blueIcount + purpleIcount - + orangeIcount + yellowIcount; + + purpleTGcount + orangeTGcount + yellowTGcount + + whiteIcount + greenIcount + blueIcount + purpleIcount + + orangeIcount + yellowIcount; uint32 greyTGoods = 0; uint32 whiteTGoods = 0; @@ -133,74 +150,71 @@ static void addNewAuctions(Player *AHBplayer, AHBConfig *config) uint32 orangeItems = 0; uint32 yellowItems = 0; - for (AuctionHouseObject::AuctionEntryMap::iterator itr = auctionHouse->GetAuctionsBegin();itr != auctionHouse->GetAuctionsEnd();++itr) + for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = auctionHouse->GetAuctionsBegin();itr != auctionHouse->GetAuctionsEnd();++itr) { AuctionEntry *Aentry = itr->second; Item *item = auctionmgr.GetAItem(Aentry->item_guidlow); - if( item ) + if (item) { ItemPrototype const *prototype = item->GetProto(); - if( prototype ) + if (prototype) { switch (prototype->Quality) { - case 0: + case 0: if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - ++greyTGoods; + ++greyTGoods; else - ++greyItems; + ++greyItems; break; - - case 1: + case 1: if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - ++whiteTGoods; + ++whiteTGoods; else - ++whiteItems; + ++whiteItems; break; - - case 2: + case 2: if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - ++greenTGoods; + ++greenTGoods; else - ++greenItems; + ++greenItems; break; - - case 3: + case 3: if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - ++blueTGoods; + ++blueTGoods; else - ++blueItems; + ++blueItems; break; - - case 4: + case 4: if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - ++purpleTGoods; + ++purpleTGoods; else - ++purpleItems; + ++purpleItems; break; - - case 5: + case 5: if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - ++orangeTGoods; + ++orangeTGoods; else - ++orangeItems; + ++orangeItems; break; - - case 6: + case 6: if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - ++yellowTGoods; + ++yellowTGoods; else - ++yellowItems; + ++yellowItems; break; } } } } + // only insert a few at a time, so as not to peg the processor for (uint32 cnt = 1;cnt <= items;cnt++) { uint32 itemID = 0; - while (itemID == 0) + uint32 loopBreaker = 0; + uint32 itemColor = 99; + while (itemID == 0 && loopBreaker < 50) { uint32 choice = urand(0, 13); switch (choice) @@ -208,269 +222,224 @@ static void addNewAuctions(Player *AHBplayer, AHBConfig *config) case 0: { if ((greyItemsBin.size() > 0) && (greyItems < greyIcount)) - { - itemID = greyItemsBin[urand(0, greyItemsBin.size() - 1)]; - ++greyItems; - break; - } + itemID = greyItemsBin[urand(0, greyItemsBin.size() - 1)]; + else continue; + break; } case 1: { if ((whiteItemsBin.size() > 0) && (whiteItems < whiteIcount)) - { - itemID = whiteItemsBin[urand(0, whiteItemsBin.size() - 1)]; - ++whiteItems; - break; - } + itemID = whiteItemsBin[urand(0, whiteItemsBin.size() - 1)]; + else continue; + break; } case 2: { if ((greenItemsBin.size() > 0) && (greenItems < greenIcount)) - { - itemID = greenItemsBin[urand(0, greenItemsBin.size() - 1)]; - ++greenItems; - break; - } + itemID = greenItemsBin[urand(0, greenItemsBin.size() - 1)]; + else continue; + break; } case 3: { if ((blueItemsBin.size() > 0) && (blueItems < blueIcount)) - { - itemID = blueItemsBin[urand(0, blueItemsBin.size() - 1)]; - ++blueItems; - break; - } + itemID = blueItemsBin[urand(0, blueItemsBin.size() - 1)]; + else continue; + break; } case 4: { if ((purpleItemsBin.size() > 0) && (purpleItems < purpleIcount)) - { itemID = purpleItemsBin[urand(0, purpleItemsBin.size() - 1)]; - ++purpleItems; - break; - } + else continue; + break; } case 5: { if ((orangeItemsBin.size() > 0) && (orangeItems < orangeIcount)) - { itemID = orangeItemsBin[urand(0, orangeItemsBin.size() - 1)]; - ++orangeItems; - break; - } + else continue; + break; } case 6: { if ((yellowItemsBin.size() > 0) && (yellowItems < yellowIcount)) - { itemID = yellowItemsBin[urand(0, yellowItemsBin.size() - 1)]; - ++yellowItems; - break; - } + else continue; + break; } case 7: { if ((greyTradeGoodsBin.size() > 0) && (greyTGoods < greyTGcount)) - { - itemID = whiteTradeGoodsBin[urand(0, whiteTradeGoodsBin.size() - 1)]; - ++greyTGoods; - break; - } + itemID = greyTradeGoodsBin[urand(0, greyTradeGoodsBin.size() - 1)]; + else continue; + break; } case 8: { if ((whiteTradeGoodsBin.size() > 0) && (whiteTGoods < whiteTGcount)) - { - itemID = whiteTradeGoodsBin[urand(0, whiteTradeGoodsBin.size() - 1)]; - ++whiteTGoods; - break; - } + itemID = whiteTradeGoodsBin[urand(0, whiteTradeGoodsBin.size() - 1)]; + else continue; + break; } case 9: { if ((greenTradeGoodsBin.size() > 0) && (greenTGoods < greenTGcount)) - { - itemID = greenTradeGoodsBin[urand(0, greenTradeGoodsBin.size() - 1)]; - ++greenTGoods; - break; - } + itemID = greenTradeGoodsBin[urand(0, greenTradeGoodsBin.size() - 1)]; + else continue; + break; } case 10: { if ((blueTradeGoodsBin.size() > 0) && (blueTGoods < blueTGcount)) - { - itemID = blueTradeGoodsBin[urand(0, blueTradeGoodsBin.size() - 1)]; - ++blueTGoods; - break; - } + itemID = blueTradeGoodsBin[urand(0, blueTradeGoodsBin.size() - 1)]; + else continue; + break; } case 11: { if ((purpleTradeGoodsBin.size() > 0) && (purpleTGoods < purpleTGcount)) - { - itemID = purpleTradeGoodsBin[urand(0, purpleTradeGoodsBin.size() - 1)]; - ++purpleTGoods; - break; - } + itemID = purpleTradeGoodsBin[urand(0, purpleTradeGoodsBin.size() - 1)]; + else continue; + break; } case 12: { if ((orangeTradeGoodsBin.size() > 0) && (orangeTGoods < orangeTGcount)) - { - itemID = orangeTradeGoodsBin[urand(0, orangeTradeGoodsBin.size() - 1)]; - ++orangeTGoods; - break; - } + itemID = orangeTradeGoodsBin[urand(0, orangeTradeGoodsBin.size() - 1)]; + else continue; + break; } case 13: { if ((yellowTradeGoodsBin.size() > 0) && (yellowTGoods < yellowTGcount)) - { - itemID = yellowTradeGoodsBin[urand(0, yellowTradeGoodsBin.size() - 1)]; - ++yellowTGoods; - break; - } + itemID = yellowTradeGoodsBin[urand(0, yellowTradeGoodsBin.size() - 1)]; + else continue; + break; } default: { + if (debug_Out) sLog.outError("AuctionHouseBot: itemID Switch - Default Reached"); break; } + ++loopBreaker; } - } - - ItemPrototype const* prototype = objmgr.GetItemPrototype(itemID); - if (prototype == NULL) - { - sLog.outString("AuctionHouseBot: Huh?!?! prototype == NULL"); - continue; - } - - Item* item = Item::CreateItem(itemID, 1, AHBplayer); - item->AddToUpdateQueueOf(AHBplayer); - if (item == NULL) - { - sLog.outString("AuctionHouseBot: Item::CreateItem() returned NULL"); - break; - } - - uint32 randomPropertyId = Item::GenerateItemRandomPropertyId(itemID); - if (randomPropertyId != 0) - item->SetItemRandomProperties(randomPropertyId); - - uint32 buyoutPrice; - uint32 bidPrice = 0; - uint32 stackCount = urand(1, item->GetMaxStackCount()); - - switch (SellMethod) - { - case 0: - buyoutPrice = prototype->SellPrice * item->GetCount(); - break; - case 1: - buyoutPrice = prototype->BuyPrice * item->GetCount(); - break; - default: - buyoutPrice = 0; - break; - } - - switch (prototype->Quality) - { - case 0: - if (config->GetMaxStack(AHB_GREY) != 0) + if (itemID == 0) { - stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(AHB_GREY))); + if (debug_Out) sLog.outError("AuctionHouseBot: Item::CreateItem() - ItemID is 0"); + continue; } - buyoutPrice *= urand(config->GetMinPrice(AHB_GREY), config->GetMaxPrice(AHB_GREY)) * stackCount; - buyoutPrice /= 100; - bidPrice = buyoutPrice * urand(config->GetMinBidPrice(AHB_GREY), config->GetMaxBidPrice(AHB_GREY)); - bidPrice /= 100; - break; - case 1: - if (config->GetMaxStack(AHB_WHITE) != 0) + ItemPrototype const* prototype = objmgr.GetItemPrototype(itemID); + if (prototype == NULL) { - stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(AHB_WHITE))); + if (debug_Out) sLog.outError("AuctionHouseBot: Huh?!?! prototype == NULL"); + continue; } - buyoutPrice *= urand(config->GetMinPrice(AHB_WHITE), config->GetMaxPrice(AHB_WHITE)) * stackCount; - buyoutPrice /= 100; - bidPrice = buyoutPrice * urand(config->GetMinBidPrice(AHB_WHITE), config->GetMaxBidPrice(AHB_WHITE)); - bidPrice /= 100; - break; - case 2: - if (config->GetMaxStack(AHB_GREEN) != 0) + Item* item = Item::CreateItem(itemID, 1, AHBplayer); + item->AddToUpdateQueueOf(AHBplayer); + if (item == NULL) { - stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(AHB_GREEN))); + if (debug_Out) sLog.outError("AuctionHouseBot: Item::CreateItem() returned NULL"); + break; } - buyoutPrice *= urand(config->GetMinPrice(AHB_GREEN), config->GetMaxPrice(AHB_GREEN)) * stackCount; - buyoutPrice /= 100; - bidPrice = buyoutPrice * urand(config->GetMinBidPrice(AHB_GREEN), config->GetMaxBidPrice(AHB_GREEN)); - bidPrice /= 100; - break; - case 3: - if (config->GetMaxStack(AHB_BLUE) != 0) - { - stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(AHB_BLUE))); - } - buyoutPrice *= urand(config->GetMinPrice(AHB_BLUE), config->GetMaxPrice(AHB_BLUE)) * stackCount; - buyoutPrice /= 100; - bidPrice = buyoutPrice * urand(config->GetMinBidPrice(AHB_BLUE), config->GetMaxBidPrice(AHB_BLUE)); - bidPrice /= 100; - break; + uint32 randomPropertyId = Item::GenerateItemRandomPropertyId(itemID); + if (randomPropertyId != 0) + item->SetItemRandomProperties(randomPropertyId); - case 4: - if (config->GetMaxStack(AHB_PURPLE) != 0) - { - stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(AHB_PURPLE))); - } - buyoutPrice *= urand(config->GetMinPrice(AHB_PURPLE), config->GetMaxPrice(AHB_PURPLE)) * stackCount; - buyoutPrice /= 100; - bidPrice = buyoutPrice * urand(config->GetMinBidPrice(AHB_PURPLE), config->GetMaxBidPrice(AHB_PURPLE)); - bidPrice /= 100; - break; - case 5: - if (config->GetMaxStack(AHB_ORANGE) != 0) + uint64 buyoutPrice = 0; + uint64 bidPrice = 0; + uint32 stackCount = 1; + + switch (SellMethod) { - stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(AHB_ORANGE))); + case 0: + buyoutPrice = prototype->SellPrice; + break; + case 1: + buyoutPrice = prototype->BuyPrice; + break; } - buyoutPrice *= urand(config->GetMinPrice(AHB_ORANGE), config->GetMaxPrice(AHB_ORANGE)) * stackCount; - buyoutPrice /= 100; - bidPrice = buyoutPrice * urand(config->GetMinBidPrice(AHB_ORANGE), config->GetMaxBidPrice(AHB_ORANGE)); - bidPrice /= 100; - break; - case 6: - if (config->GetMaxStack(AHB_YELLOW) != 0) + + switch (prototype->Quality) { - stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(AHB_YELLOW))); + case AHB_GREY: + if (config->GetMaxStack(AHB_GREY) > 1 && item->GetMaxStackCount() > 1) + stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(AHB_GREY))); + else + stackCount = 1; + buyoutPrice *= urand(config->GetMinPrice(AHB_GREY), config->GetMaxPrice(AHB_GREY)); + buyoutPrice /= 100; + bidPrice = buyoutPrice * urand(config->GetMinBidPrice(AHB_GREY), config->GetMaxBidPrice(AHB_GREY)); + bidPrice /= 100; + break; + case AHB_WHITE: + if (config->GetMaxStack(AHB_WHITE) > 1 && item->GetMaxStackCount() > 1) + stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(AHB_WHITE))); + else + stackCount = 1; + buyoutPrice *= urand(config->GetMinPrice(AHB_WHITE), config->GetMaxPrice(AHB_WHITE)); + buyoutPrice /= 100; + bidPrice = buyoutPrice * urand(config->GetMinBidPrice(AHB_WHITE), config->GetMaxBidPrice(AHB_WHITE)); + bidPrice /= 100; + break; + case AHB_GREEN: + if (config->GetMaxStack(AHB_GREEN) > 1 && item->GetMaxStackCount() > 1) + stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(AHB_GREEN))); + else + stackCount = 1; + buyoutPrice *= urand(config->GetMinPrice(AHB_GREEN), config->GetMaxPrice(AHB_GREEN)); + buyoutPrice /= 100; + bidPrice = buyoutPrice * urand(config->GetMinBidPrice(AHB_GREEN), config->GetMaxBidPrice(AHB_GREEN)); + bidPrice /= 100; + break; + case AHB_BLUE: + if (config->GetMaxStack(AHB_BLUE) > 1 && item->GetMaxStackCount() > 1) + stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(AHB_BLUE))); + else + stackCount = 1; + buyoutPrice *= urand(config->GetMinPrice(AHB_BLUE), config->GetMaxPrice(AHB_BLUE)); + buyoutPrice /= 100; + bidPrice = buyoutPrice * urand(config->GetMinBidPrice(AHB_BLUE), config->GetMaxBidPrice(AHB_BLUE)); + bidPrice /= 100; + break; + case AHB_PURPLE: + if (config->GetMaxStack(AHB_PURPLE) > 1 && item->GetMaxStackCount() > 1) + stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(AHB_PURPLE))); + else + stackCount = 1; + buyoutPrice *= urand(config->GetMinPrice(AHB_PURPLE), config->GetMaxPrice(AHB_PURPLE)); + buyoutPrice /= 100; + bidPrice = buyoutPrice * urand(config->GetMinBidPrice(AHB_PURPLE), config->GetMaxBidPrice(AHB_PURPLE)); + bidPrice /= 100; + break; + case AHB_ORANGE: + if (config->GetMaxStack(AHB_ORANGE) > 1 && item->GetMaxStackCount() > 1) + stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(AHB_ORANGE))); + else + stackCount = 1; + buyoutPrice *= urand(config->GetMinPrice(AHB_ORANGE), config->GetMaxPrice(AHB_ORANGE)); + buyoutPrice /= 100; + bidPrice = buyoutPrice * urand(config->GetMinBidPrice(AHB_ORANGE), config->GetMaxBidPrice(AHB_ORANGE)); + bidPrice /= 100; + break; + case AHB_YELLOW: + if (config->GetMaxStack(AHB_YELLOW) > 1 && item->GetMaxStackCount() > 1) + stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(AHB_YELLOW))); + else + stackCount = 1; + buyoutPrice *= urand(config->GetMinPrice(AHB_YELLOW), config->GetMaxPrice(AHB_YELLOW)); + buyoutPrice /= 100; + bidPrice = buyoutPrice * urand(config->GetMinBidPrice(AHB_YELLOW), config->GetMaxBidPrice(AHB_YELLOW)); + bidPrice /= 100; + break; } - buyoutPrice *= urand(config->GetMinPrice(AHB_YELLOW), config->GetMaxPrice(AHB_YELLOW)) * stackCount; - buyoutPrice /= 100; - bidPrice = buyoutPrice * urand(config->GetMinBidPrice(AHB_YELLOW), config->GetMaxBidPrice(AHB_YELLOW)); - bidPrice /= 100; - break; - } - - if(auctionmgr.GetAItem(GUID_LOPART(item->GetGUID()))) - { - sLog.outError("Item %u not found", item->GetEntry()); - break; - } - if(!item->CanBeTraded()) - { - sLog.outError("Item %u can't be traded", item->GetEntry()); - break; - } - if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || item->GetUInt32Value(ITEM_FIELD_DURATION)) - { - sLog.outError("Item %u is conjured or has a duration", item->GetEntry()); - break; - } - uint32 etime = urand(1,3); - switch(etime) - { + uint32 etime = urand(1,3); + switch(etime) + { case 1: etime = 43200; break; @@ -483,91 +452,144 @@ static void addNewAuctions(Player *AHBplayer, AHBConfig *config) default: etime = 86400; break; + } + item->SetCount(stackCount); + + uint32 dep = auctionmgr.GetAuctionDeposit(ahEntry, etime, item); + + AuctionEntry* auctionEntry = new AuctionEntry; + auctionEntry->Id = objmgr.GenerateAuctionID(); + auctionEntry->auctioneer = AuctioneerGUID; + auctionEntry->item_guidlow = item->GetGUIDLow(); + auctionEntry->item_template = item->GetEntry(); + auctionEntry->owner = AHBplayer->GetGUIDLow(); + auctionEntry->startbid = bidPrice * stackCount; + auctionEntry->buyout = buyoutPrice * stackCount; + auctionEntry->bidder = 0; + auctionEntry->bid = 0; + auctionEntry->deposit = dep; + auctionEntry->expire_time = (time_t) etime + time(NULL); + auctionEntry->auctionHouseEntry = ahEntry; + item->SaveToDB(); + item->RemoveFromUpdateQueueOf(AHBplayer); + auctionmgr.AddAItem(item); + auctionHouse->AddAuction(auctionEntry); + auctionEntry->SaveToDB(); + + switch(itemColor) + { + case 0: + ++greyItems; + break; + case 1: + ++whiteItems; + break; + case 2: + ++greenItems; + break; + case 3: + ++blueItems; + break; + case 4: + ++purpleItems; + break; + case 5: + ++orangeItems; + break; + case 6: + ++yellowItems; + break; + case 7: + ++greyTGoods; + break; + case 8: + ++whiteTGoods; + break; + case 9: + ++greenTGoods; + break; + case 10: + ++blueTGoods; + break; + case 11: + ++purpleTGoods; + break; + case 12: + ++orangeTGoods; + break; + case 13: + ++yellowTGoods; + break; + default: + break; + } } - uint32 dep = auctionmgr.GetAuctionDeposit( ahEntry, etime, item ); - - item->SetCount(stackCount); - - AuctionEntry* auctionEntry = new AuctionEntry; - auctionEntry->Id = objmgr.GenerateAuctionID(); - auctionEntry->auctioneer = AuctioneerGUID; - auctionEntry->item_guidlow = item->GetGUIDLow(); - auctionEntry->item_template = item->GetEntry(); - auctionEntry->owner = AHBplayer->GetGUIDLow(); - auctionEntry->startbid = bidPrice; - auctionEntry->buyout = buyoutPrice; - auctionEntry->bidder = 0; - auctionEntry->bid = 0; - auctionEntry->deposit = dep; - auctionEntry->expire_time = (time_t) etime + time(NULL); - auctionEntry->auctionHouseEntry = ahEntry; - item->SaveToDB(); - item->RemoveFromUpdateQueueOf(AHBplayer); - auctionmgr.AddAItem(item); - auctionHouse->AddAuction(auctionEntry); - auctionEntry->SaveToDB(); } } - -static void addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *config, WorldSession *session) +void AuctionHouseBot::addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *config, WorldSession *session) { if (!AHBBuyer) return; // Fetches content of selected AH AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(config->GetAHFID()); - AuctionHouseObject::AuctionEntryMap::iterator itr; - - itr = auctionHouse->GetAuctionsBegin(); vector<uint32> possibleBids; - while (itr != auctionHouse->GetAuctionsEnd()) + for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = auctionHouse->GetAuctionsBegin();itr != auctionHouse->GetAuctionsEnd();++itr) { - AuctionHouseObject::AuctionEntryMap::iterator tmp = itr; - ++itr; // Check if the auction is ours // if it is, we skip this iteration. - if(tmp->second->owner == AHBplayerGUID) + if (itr->second->owner == AHBplayerGUID) { continue; } // Check that we haven't bidded in this auction already. - if(tmp->second->bidder != AHBplayerGUID) + if (itr->second->bidder != AHBplayerGUID) { - uint32 tmpdata = tmp->second->Id; + uint32 tmpdata = itr->second->Id; possibleBids.push_back(tmpdata); } } - uint32 bids = config->GetBidsPerInterval(); - for (uint32 count = 0; count < bids; ++count) + for (uint32 count = 1;count < config->GetBidsPerInterval();++count) { // Do we have anything to bid? If not, stop here. - if(possibleBids.empty()) - return; + if (possibleBids.empty()) + { + count = config->GetBidsPerInterval(); + continue; + } // Choose random auction from possible auctions uint32 vectorPos = urand(0, possibleBids.size() - 1); vector<uint32>::iterator iter = possibleBids.begin(); advance(iter, vectorPos); + // from auctionhousehandler.cpp, creates auction pointer & player pointer AuctionEntry* auction = auctionHouse->GetAuction(*iter); - // Erase the auction from the vector to prevent bidding on item in next itteration. + + // Erase the auction from the vector to prevent bidding on item in next iteration. possibleBids.erase(iter); + if (!auction) + { + if (debug_Out) sLog.outError("Item doesn't exist, perhaps bought already?"); + continue; + } + // get exact item information Item *pItem = auctionmgr.GetAItem(auction->item_guidlow); if (!pItem) { - sLog.outError("Item doesn't exists, perhaps bought already?"); - return; + if (debug_Out) sLog.outError("Item doesn't exist, perhaps bought already?"); + continue; } // get item prototype ItemPrototype const* prototype = objmgr.GetItemPrototype(auction->item_template); // check which price we have to use, startbid or if it is bidded already - if(debug_Out) + if (debug_Out) { sLog.outError("Auction Number: %u", auction->Id); sLog.outError("Item Template: %u", auction->item_template); @@ -577,17 +599,15 @@ static void addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *config, World } uint32 currentprice; - if(auction->bid) + if (auction->bid) { currentprice = auction->bid; - if(debug_Out) - {sLog.outError("Current Price: %u", auction->bid);} + if (debug_Out) sLog.outError("Current Price: %u", auction->bid); } else { currentprice = auction->startbid; - if(debug_Out) - {sLog.outError("Current Price: %u", auction->startbid);} + if (debug_Out) sLog.outError("Current Price: %u", auction->startbid); } // Prepare portion from maximum bid @@ -595,7 +615,7 @@ static void addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *config, World double tmprate = static_cast<double>(tmprate2); double bidrate = tmprate / 100; long double bidMax = 0; - if(debug_Out) + if (debug_Out) { sLog.outError("tmprate: %f", tmprate); sLog.outError("bidrate: %f", bidrate); @@ -604,140 +624,135 @@ static void addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *config, World // check that bid has acceptable value and take bid based on vendorprice, stacksize and quality switch (BuyMethod) { - case 0: + case 0: { switch (prototype->Quality) { - case 0: - if(currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREY)) - bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREY); - break; - case 1: - if(currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_WHITE)) - bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_WHITE); - break; - case 2: - if(currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREEN)) - bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREEN); - break; - case 3: - if(currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_BLUE)) - bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_BLUE); - break; - case 4: - if(currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_PURPLE)) - bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_PURPLE); - case 5: - if(currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_ORANGE)) - bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_ORANGE); - case 6: - if(currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_YELLOW)) - bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_YELLOW); - break; - default: - // quality is something it shouldn't be, let's get out of here - if(debug_Out) - sLog.outError("bidMax(fail): %f", bidMax); - return; + case 0: + if (currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREY)) + bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREY); + break; + case 1: + if (currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_WHITE)) + bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_WHITE); + break; + case 2: + if (currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREEN)) + bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREEN); + break; + case 3: + if (currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_BLUE)) + bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_BLUE); + break; + case 4: + if (currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_PURPLE)) + bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_PURPLE); + case 5: + if (currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_ORANGE)) + bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_ORANGE); + case 6: + if (currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_YELLOW)) + bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_YELLOW); + break; + default: + // quality is something it shouldn't be, let's get out of here + if (debug_Out) sLog.outError("bidMax(fail): %f", bidMax); + continue; + break; } break; } - case 1: + case 1: { switch (prototype->Quality) { - case 0: - if(currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREY)) - bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREY); - break; - case 1: - if(currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_WHITE)) - bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_WHITE); - break; - case 2: - if(currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREEN)) - bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREEN); - break; - case 3: - if(currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_BLUE)) - bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_BLUE); - break; - case 4: - if(currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_PURPLE)) - bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_PURPLE); - case 5: - if(currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_ORANGE)) - bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_ORANGE); - case 6: - if(currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_YELLOW)) - bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_YELLOW); - break; - default: - // quality is something it shouldn't be, let's get out of here - if(debug_Out) - sLog.outError("bidMax(fail): %f", bidMax); - return; + case 0: + if (currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREY)) + bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREY); + break; + case 1: + if (currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_WHITE)) + bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_WHITE); + break; + case 2: + if (currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREEN)) + bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_GREEN); + break; + case 3: + if (currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_BLUE)) + bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_BLUE); + break; + case 4: + if (currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_PURPLE)) + bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_PURPLE); + case 5: + if (currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_ORANGE)) + bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_ORANGE); + case 6: + if (currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_YELLOW)) + bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(AHB_YELLOW); + break; + default: + // quality is something it shouldn't be, let's get out of here + if (debug_Out) sLog.outError("bidMax(fail): %f", bidMax); + continue; + break; } break; } - default: - bidMax = 0; - break; } - if(debug_Out) - sLog.outError("bidMax(succeed): %f", bidMax); + if (debug_Out) sLog.outError("bidMax(succeed): %f", bidMax); // check some special items, and do recalculating to their prices switch (prototype->Class) { // ammo - case 6: - bidMax = 0; - break; - default: - break; + case 6: + bidMax = 0; + break; + default: + break; } - if(bidMax == 0) + if (bidMax == 0) { // quality check failed to get bidmax, let's get out of here - return; + continue; } // Calculate our bid - long double bidvalue = currentprice + ( (bidMax - currentprice) * bidrate); + long double bidvalue = currentprice + ((bidMax - currentprice) * bidrate); // Convert to uint32 uint32 bidprice = static_cast<uint32>(bidvalue); - if(debug_Out) + if (debug_Out) { sLog.outError("bidprice: %u", bidprice); sLog.outError("bidvalue: %f", bidvalue); } // Check our bid is high enough to be valid. If not, correct it to minimum. - if((currentprice + auction->GetAuctionOutBid()) > bidprice) + if ((currentprice + auction->GetAuctionOutBid()) > bidprice) { bidprice = currentprice + auction->GetAuctionOutBid(); - if(debug_Out) - sLog.outError("bidprice(>): %u", bidprice); + if (debug_Out) sLog.outError("bidprice(>): %u", bidprice); } - // Check wether we do normal bid, or buyout + // Check whether we do normal bid, or buyout if ((bidprice < auction->buyout) || (auction->buyout == 0)) { if (auction->bidder > 0) { - if ( auction->bidder == AHBplayer->GetGUIDLow() ) + if (auction->bidder == AHBplayer->GetGUIDLow()) { - //pl->ModifyMoney( -int32(price - auction->bid)); + //pl->ModifyMoney(-int32(price - auction->bid)); } else { // mail to last bidder and return money - session->SendAuctionOutbiddedMail( auction , bidprice ); - //pl->ModifyMoney( -int32(price) ); + session->SendAuctionOutbiddedMail(auction , bidprice); + //pl->ModifyMoney(-int32(price)); } } @@ -750,24 +765,24 @@ static void addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *config, World else { //buyout - if (AHBplayer->GetGUIDLow() == auction->bidder ) + if (AHBplayer->GetGUIDLow() == auction->bidder) { //pl->ModifyMoney(-int32(auction->buyout - auction->bid)); } else { //pl->ModifyMoney(-int32(auction->buyout)); - if ( auction->bidder ) + if (auction->bidder) { - session->SendAuctionOutbiddedMail( auction, auction->buyout ); + session->SendAuctionOutbiddedMail(auction, auction->buyout); } } auction->bidder = AHBplayer->GetGUIDLow(); auction->bid = auction->buyout; // Send mails to buyer & seller - auctionmgr.SendAuctionSuccessfulMail( auction ); - auctionmgr.SendAuctionWonMail( auction ); + auctionmgr.SendAuctionSuccessfulMail(auction); + auctionmgr.SendAuctionWonMail(auction); // Remove item from auctionhouse auctionmgr.RemoveAItem(auction->item_guidlow); @@ -779,21 +794,20 @@ static void addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *config, World } } } -/////////////////////////////////////////////////////////////////////////////// -// -/////////////////////////////////////////////////////////////////////////////// -void AuctionHouseBot() + +void AuctionHouseBot::Update() { time_t _newrun = time(NULL); if ((!AHBSeller) && (!AHBBuyer)) - return; + return; WorldSession _session(AHBplayerAccount, NULL, SEC_PLAYER, true, 0, LOCALE_enUS); Player _AHBplayer(&_session); _AHBplayer.MinimalLoadFromDB(NULL, AHBplayerGUID); ObjectAccessor::Instance().AddObject(&_AHBplayer); - if(!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) + // Add New Bids + if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) { addNewAuctions(&_AHBplayer, &AllianceConfig); if (((_newrun - _lastrun_a) > (AllianceConfig.GetBiddingInterval() * 60)) && (AllianceConfig.GetBidsPerInterval() > 0)) @@ -801,6 +815,7 @@ void AuctionHouseBot() addNewAuctionBuyerBotBid(&_AHBplayer, &AllianceConfig, &_session); _lastrun_a = _newrun; } + addNewAuctions(&_AHBplayer, &HordeConfig); if (((_newrun - _lastrun_h) > (HordeConfig.GetBiddingInterval() *60)) && (HordeConfig.GetBidsPerInterval() > 0)) { @@ -808,6 +823,7 @@ void AuctionHouseBot() _lastrun_h = _newrun; } } + addNewAuctions(&_AHBplayer, &NeutralConfig); if (((_newrun - _lastrun_n) > (NeutralConfig.GetBiddingInterval() * 60)) && (NeutralConfig.GetBidsPerInterval() > 0)) { @@ -816,75 +832,128 @@ void AuctionHouseBot() } ObjectAccessor::Instance().RemoveObject(&_AHBplayer); } -/////////////////////////////////////////////////////////////////////////////// -// -/////////////////////////////////////////////////////////////////////////////// -void AuctionHouseBotInit() + +void AuctionHouseBot::Initialize() { + debug_Out = sConfig.GetBoolDefault("AuctionHouseBot.DEBUG", false); + AHBSeller = sConfig.GetBoolDefault("AuctionHouseBot.EnableSeller", false); AHBBuyer = sConfig.GetBoolDefault("AuctionHouseBot.EnableBuyer", false); + SellMethod = sConfig.GetBoolDefault("AuctionHouseBot.UseBuyPriceForSeller", false); + BuyMethod = sConfig.GetBoolDefault("AuctionHouseBot.UseBuyPriceForBuyer", false); + + AHBplayerAccount = sConfig.GetIntDefault("AuctionHouseBot.Account", 0); + AHBplayerGUID = sConfig.GetIntDefault("AuctionHouseBot.GUID", 0); + ItemsPerCycle = sConfig.GetIntDefault("AuctionHouseBot.ItemsPerCycle", 200); + + + //Begin Filters + + Vendor_Items = sConfig.GetBoolDefault("AuctionHouseBot.VendorItems", false); + Loot_Items = sConfig.GetBoolDefault("AuctionHouseBot.LootItems", true); + Other_Items = sConfig.GetBoolDefault("AuctionHouseBot.OtherItems", false); + No_Bind = sConfig.GetBoolDefault("AuctionHouseBot.No_Bind", true); Bind_When_Picked_Up = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Picked_Up", false); Bind_When_Equipped = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Equipped", true); Bind_When_Use = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Use", true); Bind_Quest_Item = sConfig.GetBoolDefault("AuctionHouseBot.Bind_Quest_Item", false); - if(!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) + DisableBeta_PTR_Unused = sConfig.GetBoolDefault("AuctionHouseBot.DisableBeta_PTR_Unused", false); + DisablePermEnchant = sConfig.GetBoolDefault("AuctionHouseBot.DisablePermEnchant", false); + DisableConjured = sConfig.GetBoolDefault("AuctionHouseBot.DisableConjured", false); + DisableGems = sConfig.GetBoolDefault("AuctionHouseBot.DisableGems", false); + DisableMoney = sConfig.GetBoolDefault("AuctionHouseBot.DisableMoney", false); + DisableMoneyLoot = sConfig.GetBoolDefault("AuctionHouseBot.DisableMoneyLoot", false); + DisableLootable = sConfig.GetBoolDefault("AuctionHouseBot.DisableLootable", false); + DisableKeys = sConfig.GetBoolDefault("AuctionHouseBot.DisableKeys", false); + DisableDuration = sConfig.GetBoolDefault("AuctionHouseBot.DisableDuration", false); + DisableBOP_Or_Quest_NoReqLevel = sConfig.GetBoolDefault("AuctionHouseBot.DisableBOP_Or_Quest_NoReqLevel", false); + + DisableWarriorItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableWarriorItems", false); + DisablePaladinItems = sConfig.GetBoolDefault("AuctionHouseBot.DisablePaladinItems", false); + DisableHunterItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableHunterItems", false); + DisableRogueItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableRogueItems", false); + DisablePriestItems = sConfig.GetBoolDefault("AuctionHouseBot.DisablePriestItems", false); + DisableDKItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableDKItems", false); + DisableShamanItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableShamanItems", false); + DisableMageItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableMageItems", false); + DisableWarlockItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableWarlockItems", false); + DisableUnusedClassItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableUnusedClassItems", false); + DisableDruidItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableDruidItems", false); + + DisableItemsBelowLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsBelowLevel", 0); + DisableItemsAboveLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsAboveLevel", 0); + DisableTGsBelowLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsBelowLevel", 0); + DisableTGsAboveLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsAboveLevel", 0); + DisableItemsBelowGUID = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsBelowGUID", 0); + DisableItemsAboveGUID = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsAboveGUID", 0); + DisableTGsBelowGUID = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsBelowGUID", 0); + DisableTGsAboveGUID = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsAboveGUID", 0); + DisableItemsBelowReqLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsBelowReqLevel", 0); + DisableItemsAboveReqLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsAboveReqLevel", 0); + DisableTGsBelowReqLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsBelowReqLevel", 0); + DisableTGsAboveReqLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsAboveReqLevel", 0); + DisableItemsBelowReqSkillRank = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsBelowReqSkillRank", 0); + DisableItemsAboveReqSkillRank = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsAboveReqSkillRank", 0); + DisableTGsBelowReqSkillRank = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsBelowReqSkillRank", 0); + DisableTGsAboveReqSkillRank = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsAboveReqSkillRank", 0); + + //End Filters + + if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) { - AuctionHouseBotLoadValues(&AllianceConfig); - AuctionHouseBotLoadValues(&HordeConfig); + LoadValues(&AllianceConfig); + LoadValues(&HordeConfig); } - AuctionHouseBotLoadValues(&NeutralConfig); + LoadValues(&NeutralConfig); if (AHBSeller) { - Vendor_Items = sConfig.GetBoolDefault("AuctionHouseBot.VendorItems", false); - Loot_Items = sConfig.GetBoolDefault("AuctionHouseBot.LootItems", true); - Other_Items = sConfig.GetBoolDefault("AuctionHouseBot.OtherItems", false); - QueryResult* results = (QueryResult*) NULL; char npcQuery[] = "SELECT distinct `item` FROM `npc_vendor`"; results = WorldDatabase.PQuery(npcQuery); if (results != NULL) { - do - { - Field* fields = results->Fetch(); - npcItems.push_back(fields[0].GetUInt32()); + do + { + Field* fields = results->Fetch(); + npcItems.push_back(fields[0].GetUInt32()); - } while (results->NextRow()); + } while (results->NextRow()); - delete results; + delete results; } else { - sLog.outString("AuctionHouseBot: \"%s\" failed", npcQuery); + if (debug_Out) sLog.outString("AuctionHouseBot: \"%s\" failed", npcQuery); } char lootQuery[] = "SELECT `item` FROM `creature_loot_template` UNION " - "SELECT `item` FROM `disenchant_loot_template` UNION " - "SELECT `item` FROM `fishing_loot_template` UNION " - "SELECT `item` FROM `gameobject_loot_template` UNION " - "SELECT `item` FROM `item_loot_template` UNION " - "SELECT `item` FROM `pickpocketing_loot_template` UNION " - "SELECT `item` FROM `prospecting_loot_template` UNION " - "SELECT `item` FROM `skinning_loot_template`"; + "SELECT `item` FROM `disenchant_loot_template` UNION " + "SELECT `item` FROM `fishing_loot_template` UNION " + "SELECT `item` FROM `gameobject_loot_template` UNION " + "SELECT `item` FROM `item_loot_template` UNION " + "SELECT `item` FROM `milling_loot_template` UNION " + "SELECT `item` FROM `pickpocketing_loot_template` UNION " + "SELECT `item` FROM `prospecting_loot_template` UNION " + "SELECT `item` FROM `skinning_loot_template`"; results = WorldDatabase.PQuery(lootQuery); if (results != NULL) { - do - { - Field* fields = results->Fetch(); - lootItems.push_back(fields[0].GetUInt32()); + do + { + Field* fields = results->Fetch(); + lootItems.push_back(fields[0].GetUInt32()); - } while (results->NextRow()); + } while (results->NextRow()); - delete results; + delete results; } else { - sLog.outString("AuctionHouseBot: \"%s\" failed", lootQuery); + if (debug_Out) sLog.outString("AuctionHouseBot: \"%s\" failed", lootQuery); } for (uint32 itemID = 0; itemID < sItemStorage.MaxEntry; itemID++) @@ -892,7 +961,7 @@ void AuctionHouseBotInit() ItemPrototype const* prototype = objmgr.GetItemPrototype(itemID); if (prototype == NULL) - continue; + continue; switch (prototype->Bonding) { @@ -931,13 +1000,10 @@ void AuctionHouseBotInit() if (prototype->BuyPrice == 0) continue; break; - default: - continue; - break; } if ((prototype->Quality < 0) || (prototype->Quality > 6)) - continue; + continue; if (Vendor_Items == 0) { @@ -945,12 +1011,12 @@ void AuctionHouseBotInit() for (unsigned int i = 0; (i < npcItems.size()) && (!isVendorItem); i++) { - if (itemID == npcItems[i]) - isVendorItem = true; + if (itemID == npcItems[i]) + isVendorItem = true; } if (isVendorItem) - continue; + continue; } if (Loot_Items == 0) @@ -959,12 +1025,12 @@ void AuctionHouseBotInit() for (unsigned int i = 0; (i < lootItems.size()) && (!isLootItem); i++) { - if (itemID == lootItems[i]) - isLootItem = true; + if (itemID == lootItems[i]) + isLootItem = true; } if (isLootItem) - continue; + continue; } if (Other_Items == 0) @@ -974,92 +1040,349 @@ void AuctionHouseBotInit() for (unsigned int i = 0; (i < npcItems.size()) && (!isVendorItem); i++) { - if (itemID == npcItems[i]) - isVendorItem = true; + if (itemID == npcItems[i]) + isVendorItem = true; } for (unsigned int i = 0; (i < lootItems.size()) && (!isLootItem); i++) { - if (itemID == lootItems[i]) - isLootItem = true; + if (itemID == lootItems[i]) + isLootItem = true; } if ((!isLootItem) && (!isVendorItem)) continue; } + //TODO:Make list of items and create a vector + // Disable PTR/Beta/Unused items + if ((DisableBeta_PTR_Unused) && ((prototype->ItemId == 21878) || (prototype->ItemId == 27774) || (prototype->ItemId == 27811) || (prototype->ItemId == 28117) || (prototype->ItemId == 28112))) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (PTR/Beta/Unused Item)", prototype->ItemId); + continue; + } + + // Disable permanent enchants items + if ((DisablePermEnchant) && (prototype->Class == ITEM_CLASS_PERMANENT)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Permanent Enchant Item)", prototype->ItemId); + continue; + } + + // Disable conjured items + if ((DisableConjured) && (prototype->IsConjuredConsumable())) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Conjured Consumable)", prototype->ItemId); + continue; + } + + // Disable gems + if ((DisableGems) && (prototype->Class == ITEM_CLASS_GEM)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Gem)", prototype->ItemId); + continue; + } + + // Disable money + if ((DisableMoney) && (prototype->Class == ITEM_CLASS_MONEY)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Money)", prototype->ItemId); + continue; + } + + // Disable moneyloot + if ((DisableMoneyLoot) && (prototype->MinMoneyLoot > 0)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (MoneyLoot)", prototype->ItemId); + continue; + } + + // Disable lootable items + if ((DisableLootable) && (prototype->Flags & 4)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Lootable Item)", prototype->ItemId); + continue; + } + + // Disable Keys + if ((DisableKeys) && (prototype->Class == ITEM_CLASS_KEY)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Quest Item)", prototype->ItemId); + continue; + } + + // Disable items with duration + if ((DisableDuration) && (prototype->Duration > 0)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Has a Duration)", prototype->ItemId); + continue; + } + + // Disable items which are BOP or Quest Items and have a required level lower than the item level + if ((DisableBOP_Or_Quest_NoReqLevel) && ((prototype->Bonding == BIND_WHEN_PICKED_UP || prototype->Bonding == BIND_QUEST_ITEM) && (prototype->RequiredLevel < prototype->ItemLevel))) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (BOP or BQI and Required Level is less than Item Level)", prototype->ItemId); + continue; + } + + // Disable items specifically for Warrior + if ((DisableWarriorItems) && (prototype->AllowableClass == 1)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Warrior Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Paladin + if ((DisablePaladinItems) && (prototype->AllowableClass == 2)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Paladin Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Hunter + if ((DisableHunterItems) && (prototype->AllowableClass == 4)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Hunter Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Rogue + if ((DisableRogueItems) && (prototype->AllowableClass == 8)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Rogue Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Priest + if ((DisablePriestItems) && (prototype->AllowableClass == 16)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Priest Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for DK + if ((DisableDKItems) && (prototype->AllowableClass == 32)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (DK Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Shaman + if ((DisableShamanItems) && (prototype->AllowableClass == 64)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Shaman Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Mage + if ((DisableMageItems) && (prototype->AllowableClass == 128)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Mage Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Warlock + if ((DisableWarlockItems) && (prototype->AllowableClass == 256)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Warlock Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Unused Class + if ((DisableUnusedClassItems) && (prototype->AllowableClass == 512)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Unused Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Druid + if ((DisableDruidItems) && (prototype->AllowableClass == 1024)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Druid Item)", prototype->ItemId); + continue; + } + + // Disable Items below level X + if ((DisableItemsBelowLevel) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel < DisableItemsBelowLevel)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Items above level X + if ((DisableItemsAboveLevel) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel > DisableItemsAboveLevel)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Trade Goods below level X + if ((DisableTGsBelowLevel) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel < DisableTGsBelowLevel)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Trade Good %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Trade Goods above level X + if ((DisableTGsAboveLevel) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel > DisableTGsAboveLevel)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Trade Good %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Items below GUID X + if ((DisableItemsBelowGUID) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId < DisableItemsBelowGUID)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Items above GUID X + if ((DisableItemsAboveGUID) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId > DisableItemsAboveGUID)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Trade Goods below GUID X + if ((DisableTGsBelowGUID) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId < DisableTGsBelowGUID)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Trade Goods above GUID X + if ((DisableTGsAboveGUID) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId > DisableTGsAboveGUID)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Items for level lower than X + if ((DisableItemsBelowReqLevel) && (prototype->RequiredLevel < DisableItemsBelowReqLevel)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel); + continue; + } + + // Disable Items for level higher than X + if ((DisableItemsAboveReqLevel) && (prototype->RequiredLevel > DisableItemsAboveReqLevel)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel); + continue; + } + + // Disable Trade Goods for level lower than X + if ((DisableTGsBelowReqLevel) && (prototype->RequiredLevel < DisableTGsBelowReqLevel)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Trade Good %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel); + continue; + } + + // Disable Trade Goods for level higher than X + if ((DisableTGsAboveReqLevel) && (prototype->RequiredLevel > DisableTGsAboveReqLevel)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Trade Good %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel); + continue; + } + + // Disable Items that require skill lower than X + if ((DisableItemsBelowReqSkillRank) && (prototype->RequiredSkillRank < DisableItemsBelowReqSkillRank)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank); + continue; + } + + // Disable Items that require skill higher than X + if ((DisableItemsAboveReqSkillRank) && (prototype->RequiredSkillRank > DisableItemsAboveReqSkillRank)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank); + continue; + } + + // Disable Trade Goods that require skill lower than X + if ((DisableTGsBelowReqSkillRank) && (prototype->RequiredSkillRank < DisableTGsBelowReqSkillRank)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank); + continue; + } + + // Disable Trade Goods that require skill higher than X + if ((DisableTGsAboveReqSkillRank) && (prototype->RequiredSkillRank > DisableTGsAboveReqSkillRank)) + { + if (debug_Out) sLog.outError("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank); + continue; + } + switch (prototype->Quality) { - case 0: + case 0: if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - greyTradeGoodsBin.push_back(itemID); + greyTradeGoodsBin.push_back(itemID); else - greyItemsBin.push_back(itemID); + greyItemsBin.push_back(itemID); break; - case 1: + case 1: if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - whiteTradeGoodsBin.push_back(itemID); + whiteTradeGoodsBin.push_back(itemID); else - whiteItemsBin.push_back(itemID); + whiteItemsBin.push_back(itemID); break; - case 2: + case 2: if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - greenTradeGoodsBin.push_back(itemID); + greenTradeGoodsBin.push_back(itemID); else - greenItemsBin.push_back(itemID); + greenItemsBin.push_back(itemID); break; - case 3: + case 3: if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - blueTradeGoodsBin.push_back(itemID); + blueTradeGoodsBin.push_back(itemID); else - blueItemsBin.push_back(itemID); + blueItemsBin.push_back(itemID); break; - case 4: + case 4: if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - purpleTradeGoodsBin.push_back(itemID); + purpleTradeGoodsBin.push_back(itemID); else - purpleItemsBin.push_back(itemID); + purpleItemsBin.push_back(itemID); break; - case 5: + case 5: if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - orangeTradeGoodsBin.push_back(itemID); + orangeTradeGoodsBin.push_back(itemID); else - orangeItemsBin.push_back(itemID); + orangeItemsBin.push_back(itemID); break; - case 6: + case 6: if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - yellowTradeGoodsBin.push_back(itemID); + yellowTradeGoodsBin.push_back(itemID); else - yellowItemsBin.push_back(itemID); + yellowItemsBin.push_back(itemID); break; } } - if ( - (greyTradeGoodsBin.size() == 0) && - (whiteTradeGoodsBin.size() == 0) && - (greenTradeGoodsBin.size() == 0) && - (blueTradeGoodsBin.size() == 0) && - (purpleTradeGoodsBin.size() == 0) && - (orangeTradeGoodsBin.size() == 0) && - (yellowTradeGoodsBin.size() == 0) && - (greyItemsBin.size() == 0) && - (whiteItemsBin.size() == 0) && - (greenItemsBin.size() == 0) && - (blueItemsBin.size() == 0) && - (purpleItemsBin.size() == 0) && - (orangeItemsBin.size() == 0) && - (yellowItemsBin.size() == 0) - ) + if ((greyTradeGoodsBin.size() == 0) && + (whiteTradeGoodsBin.size() == 0) && + (greenTradeGoodsBin.size() == 0) && + (blueTradeGoodsBin.size() == 0) && + (purpleTradeGoodsBin.size() == 0) && + (orangeTradeGoodsBin.size() == 0) && + (yellowTradeGoodsBin.size() == 0) && + (greyItemsBin.size() == 0) && + (whiteItemsBin.size() == 0) && + (greenItemsBin.size() == 0) && + (blueItemsBin.size() == 0) && + (purpleItemsBin.size() == 0) && + (orangeItemsBin.size() == 0) && + (yellowItemsBin.size() == 0)) { - sLog.outString("AuctionHouseBot: No items"); - AHBSeller = 0; + sLog.outString("AuctionHouseBot: No items"); + AHBSeller = 0; } - sLog.outString("AuctionHouseBot:"); sLog.outString("loaded %u grey trade goods", greyTradeGoodsBin.size()); sLog.outString("loaded %u white trade goods", whiteTradeGoodsBin.size()); @@ -1078,9 +1401,9 @@ void AuctionHouseBotInit() } sLog.outString("AuctionHouseBot by Paradox (original by ChrisK) has been loaded."); sLog.outString("AuctionHouseBot now includes AHBuyer by Kerbe and Paradox"); - } -void AuctionHouseBotCommands(uint32 command, uint32 ahMapID, uint32 col, char* args) + +void AuctionHouseBot::Commands(uint32 command, uint32 ahMapID, uint32 col, char* args) { AHBConfig *config; switch (ahMapID) @@ -1133,32 +1456,33 @@ void AuctionHouseBotCommands(uint32 command, uint32 ahMapID, uint32 col, char* a while (itr != auctionHouse->GetAuctionsEnd()) { - if (itr->second->owner == AHBplayerGUID) - itr->second->expire_time = sWorld.GetGameTime(); + if (itr->second->owner == AHBplayerGUID) + itr->second->expire_time = sWorld.GetGameTime(); - ++itr; + ++itr; } - }break; + } + break; case 1: //min items { char * param1 = strtok(args, " "); uint32 minItems = (uint32) strtoul(param1, NULL, 0); CharacterDatabase.PExecute("UPDATE auctionhousebot SET minitems = '%u' WHERE auctionhouse = '%u'", minItems, ahMapID); config->SetMinItems(minItems); - }break; + } + break; case 2: //max items { char * param1 = strtok(args, " "); uint32 maxItems = (uint32) strtoul(param1, NULL, 0); CharacterDatabase.PExecute("UPDATE auctionhousebot SET maxitems = '%u' WHERE auctionhouse = '%u'", maxItems, ahMapID); config->SetMaxItems(maxItems); - }break; + } + break; case 3: //min time Deprecated (Place holder for future commands) - { - }break; + break; case 4: //max time Deprecated (Place holder for future commands) - { - }break; + break; case 5: //percentages { char * param1 = strtok(args, " "); @@ -1207,77 +1531,89 @@ void AuctionHouseBotCommands(uint32 command, uint32 ahMapID, uint32 col, char* a CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentyellowitems = '%u' WHERE auctionhouse = '%u'", yellowi, ahMapID); CharacterDatabase.CommitTransaction(); config->SetPercentages(greytg, whitetg, greentg, bluetg, purpletg, orangetg, yellowtg, greyi, whitei, greeni, bluei, purplei, orangei, yellowi); - }break; + } + break; case 6: //min prices { char * param1 = strtok(args, " "); uint32 minPrice = (uint32) strtoul(param1, NULL, 0); CharacterDatabase.PExecute("UPDATE auctionhousebot SET minprice%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), minPrice, ahMapID); config->SetMinPrice(col, minPrice); - }break; + } + break; case 7: //max prices { char * param1 = strtok(args, " "); uint32 maxPrice = (uint32) strtoul(param1, NULL, 0); CharacterDatabase.PExecute("UPDATE auctionhousebot SET maxprice%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), maxPrice, ahMapID); config->SetMaxPrice(col, maxPrice); - }break; + } + break; case 8: //min bid price { char * param1 = strtok(args, " "); uint32 minBidPrice = (uint32) strtoul(param1, NULL, 0); CharacterDatabase.PExecute("UPDATE auctionhousebot SET minbidprice%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), minBidPrice, ahMapID); config->SetMinBidPrice(col, minBidPrice); - }break; + } + break; case 9: //max bid price { char * param1 = strtok(args, " "); uint32 maxBidPrice = (uint32) strtoul(param1, NULL, 0); CharacterDatabase.PExecute("UPDATE auctionhousebot SET maxbidprice%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), maxBidPrice, ahMapID); config->SetMaxBidPrice(col, maxBidPrice); - }break; + } + break; case 10: //max stacks { char * param1 = strtok(args, " "); uint32 maxStack = (uint32) strtoul(param1, NULL, 0); CharacterDatabase.PExecute("UPDATE auctionhousebot SET maxstack%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), maxStack, ahMapID); config->SetMaxStack(col, maxStack); - }break; + } + break; case 11: //buyer bid prices { char * param1 = strtok(args, " "); uint32 buyerPrice = (uint32) strtoul(param1, NULL, 0); CharacterDatabase.PExecute("UPDATE auctionhousebot SET buyerprice%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), buyerPrice, ahMapID); config->SetBuyerPrice(col, buyerPrice); - }break; + } + break; case 12: //buyer bidding interval { char * param1 = strtok(args, " "); uint32 bidInterval = (uint32) strtoul(param1, NULL, 0); CharacterDatabase.PExecute("UPDATE auctionhousebot SET buyerbiddinginterval = '%u' WHERE auctionhouse = '%u'", bidInterval, ahMapID); config->SetBiddingInterval(bidInterval); - }break; + } + break; case 13: //buyer bids per interval { char * param1 = strtok(args, " "); uint32 bidsPerInterval = (uint32) strtoul(param1, NULL, 0); CharacterDatabase.PExecute("UPDATE auctionhousebot SET buyerbidsperinterval = '%u' WHERE auctionhouse = '%u'", bidsPerInterval, ahMapID); config->SetBidsPerInterval(bidsPerInterval); - }break; + } + break; default: break; } } -void AuctionHouseBotLoadValues(AHBConfig *config) + +void AuctionHouseBot::LoadValues(AHBConfig *config) { if (AHBSeller) { //load min and max items config->SetMinItems(CharacterDatabase.PQuery("SELECT minitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); config->SetMaxItems(CharacterDatabase.PQuery("SELECT maxitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("minItems = %u", config->GetMinItems()); - sLog.outError("maxItems = %u", config->GetMaxItems());} + if (debug_Out) + { + sLog.outError("minItems = %u", config->GetMinItems()); + sLog.outError("maxItems = %u", config->GetMaxItems()); + } //load percentages uint32 greytg = CharacterDatabase.PQuery("SELECT percentgreytradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); uint32 whitetg = CharacterDatabase.PQuery("SELECT percentwhitetradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); @@ -1294,7 +1630,7 @@ void AuctionHouseBotLoadValues(AHBConfig *config) uint32 orangei = CharacterDatabase.PQuery("SELECT percentorangeitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); uint32 yellowi = CharacterDatabase.PQuery("SELECT percentyellowitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); config->SetPercentages(greytg, whitetg, greentg, bluetg, purpletg, orangetg, yellowtg, greyi, whitei, greeni, bluei, purplei, orangei, yellowi); - if(debug_Out) + if (debug_Out) { sLog.outError("percentGreyTradeGoods = %u", config->GetPercentages(AHB_GREY_TG)); sLog.outError("percentWhiteTradeGoods = %u", config->GetPercentages(AHB_WHITE_TG)); @@ -1314,104 +1650,160 @@ void AuctionHouseBotLoadValues(AHBConfig *config) //load min and max prices config->SetMinPrice(AHB_GREY, CharacterDatabase.PQuery("SELECT minpricegrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); config->SetMaxPrice(AHB_GREY, CharacterDatabase.PQuery("SELECT maxpricegrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("minPriceGrey = %u", config->GetMinPrice(AHB_GREY)); - sLog.outError("maxPriceGrey = %u", config->GetMaxPrice(AHB_GREY));} + if (debug_Out) + { + sLog.outError("minPriceGrey = %u", config->GetMinPrice(AHB_GREY)); + sLog.outError("maxPriceGrey = %u", config->GetMaxPrice(AHB_GREY)); + } config->SetMinPrice(AHB_WHITE, CharacterDatabase.PQuery("SELECT minpricewhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); config->SetMaxPrice(AHB_WHITE, CharacterDatabase.PQuery("SELECT maxpricewhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("minPriceWhite = %u", config->GetMinPrice(AHB_WHITE)); - sLog.outError("maxPriceWhite = %u", config->GetMaxPrice(AHB_WHITE));} + if (debug_Out) + { + sLog.outError("minPriceWhite = %u", config->GetMinPrice(AHB_WHITE)); + sLog.outError("maxPriceWhite = %u", config->GetMaxPrice(AHB_WHITE)); + } config->SetMinPrice(AHB_GREEN, CharacterDatabase.PQuery("SELECT minpricegreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); config->SetMaxPrice(AHB_GREEN, CharacterDatabase.PQuery("SELECT maxpricegreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("minPriceGreen = %u", config->GetMinPrice(AHB_GREEN)); - sLog.outError("maxPriceGreen = %u", config->GetMaxPrice(AHB_GREEN));} + if (debug_Out) + { + sLog.outError("minPriceGreen = %u", config->GetMinPrice(AHB_GREEN)); + sLog.outError("maxPriceGreen = %u", config->GetMaxPrice(AHB_GREEN)); + } config->SetMinPrice(AHB_BLUE, CharacterDatabase.PQuery("SELECT minpriceblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); config->SetMaxPrice(AHB_BLUE, CharacterDatabase.PQuery("SELECT maxpriceblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("minPriceBlue = %u", config->GetMinPrice(AHB_BLUE)); - sLog.outError("maxPriceBlue = %u", config->GetMaxPrice(AHB_BLUE));} + if (debug_Out) + { + sLog.outError("minPriceBlue = %u", config->GetMinPrice(AHB_BLUE)); + sLog.outError("maxPriceBlue = %u", config->GetMaxPrice(AHB_BLUE)); + } config->SetMinPrice(AHB_PURPLE, CharacterDatabase.PQuery("SELECT minpricepurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); config->SetMaxPrice(AHB_PURPLE, CharacterDatabase.PQuery("SELECT maxpricepurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("minPricePurple = %u", config->GetMinPrice(AHB_PURPLE)); - sLog.outError("maxPricePurple = %u", config->GetMaxPrice(AHB_PURPLE));} + if (debug_Out) + { + sLog.outError("minPricePurple = %u", config->GetMinPrice(AHB_PURPLE)); + sLog.outError("maxPricePurple = %u", config->GetMaxPrice(AHB_PURPLE)); + } config->SetMinPrice(AHB_ORANGE, CharacterDatabase.PQuery("SELECT minpriceorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); config->SetMaxPrice(AHB_ORANGE, CharacterDatabase.PQuery("SELECT maxpriceorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("minPriceOrange = %u", config->GetMinPrice(AHB_ORANGE)); - sLog.outError("maxPriceOrange = %u", config->GetMaxPrice(AHB_ORANGE));} + if (debug_Out) + { + sLog.outError("minPriceOrange = %u", config->GetMinPrice(AHB_ORANGE)); + sLog.outError("maxPriceOrange = %u", config->GetMaxPrice(AHB_ORANGE)); + } config->SetMinPrice(AHB_YELLOW, CharacterDatabase.PQuery("SELECT minpriceyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); config->SetMaxPrice(AHB_YELLOW, CharacterDatabase.PQuery("SELECT maxpriceyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("minPriceYellow = %u", config->GetMinPrice(AHB_YELLOW)); - sLog.outError("maxPriceYellow = %u", config->GetMaxPrice(AHB_YELLOW));} + if (debug_Out) + { + sLog.outError("minPriceYellow = %u", config->GetMinPrice(AHB_YELLOW)); + sLog.outError("maxPriceYellow = %u", config->GetMaxPrice(AHB_YELLOW)); + } //load min and max bid prices config->SetMinBidPrice(AHB_GREY, CharacterDatabase.PQuery("SELECT minbidpricegrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError(",minBidPriceGrey = %u", config->GetMinBidPrice(AHB_GREY));} + if (debug_Out) + { + sLog.outError("minBidPriceGrey = %u", config->GetMinBidPrice(AHB_GREY)); + } config->SetMaxBidPrice(AHB_GREY, CharacterDatabase.PQuery("SELECT maxbidpricegrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("maxBidPriceGrey = %u", config->GetMaxBidPrice(AHB_GREY));} + if (debug_Out) + { + sLog.outError("maxBidPriceGrey = %u", config->GetMaxBidPrice(AHB_GREY)); + } config->SetMinBidPrice(AHB_WHITE, CharacterDatabase.PQuery("SELECT minbidpricewhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError(",minBidPriceWhite = %u", config->GetMinBidPrice(AHB_WHITE));} + if (debug_Out) + { + sLog.outError("minBidPriceWhite = %u", config->GetMinBidPrice(AHB_WHITE)); + } config->SetMaxBidPrice(AHB_WHITE, CharacterDatabase.PQuery("SELECT maxbidpricewhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("maxBidPriceWhite = %u", config->GetMaxBidPrice(AHB_WHITE));} + if (debug_Out) + { + sLog.outError("maxBidPriceWhite = %u", config->GetMaxBidPrice(AHB_WHITE)); + } config->SetMinBidPrice(AHB_GREEN, CharacterDatabase.PQuery("SELECT minbidpricegreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("minBidPriceGreen = %u", config->GetMinBidPrice(AHB_GREEN));} + if (debug_Out) + { + sLog.outError("minBidPriceGreen = %u", config->GetMinBidPrice(AHB_GREEN)); + } config->SetMaxBidPrice(AHB_GREEN, CharacterDatabase.PQuery("SELECT maxbidpricegreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("maxBidPriceGreen = %u", config->GetMaxBidPrice(AHB_GREEN));} + if (debug_Out) + { + sLog.outError("maxBidPriceGreen = %u", config->GetMaxBidPrice(AHB_GREEN)); + } config->SetMinBidPrice(AHB_BLUE, CharacterDatabase.PQuery("SELECT minbidpriceblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("minBidPriceBlue = %u", config->GetMinBidPrice(AHB_BLUE));} + if (debug_Out) + { + sLog.outError("minBidPriceBlue = %u", config->GetMinBidPrice(AHB_BLUE)); + } config->SetMaxBidPrice(AHB_BLUE, CharacterDatabase.PQuery("SELECT maxbidpriceblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("maxBidPriceBlue = %u", config->GetMinBidPrice(AHB_BLUE));} + if (debug_Out) + { + sLog.outError("maxBidPriceBlue = %u", config->GetMinBidPrice(AHB_BLUE)); + } config->SetMinBidPrice(AHB_PURPLE, CharacterDatabase.PQuery("SELECT minbidpricepurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("minBidPricePurple = %u", config->GetMinBidPrice(AHB_PURPLE));} + if (debug_Out) + { + sLog.outError("minBidPricePurple = %u", config->GetMinBidPrice(AHB_PURPLE)); + } config->SetMaxBidPrice(AHB_PURPLE, CharacterDatabase.PQuery("SELECT maxbidpricepurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("maxBidPricePurple = %u", config->GetMaxBidPrice(AHB_PURPLE));} + if (debug_Out) + { + sLog.outError("maxBidPricePurple = %u", config->GetMaxBidPrice(AHB_PURPLE)); + } config->SetMinBidPrice(AHB_ORANGE, CharacterDatabase.PQuery("SELECT minbidpriceorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("minBidPriceOrange = %u", config->GetMinBidPrice(AHB_ORANGE));} + if (debug_Out) + { + sLog.outError("minBidPriceOrange = %u", config->GetMinBidPrice(AHB_ORANGE)); + } config->SetMaxBidPrice(AHB_ORANGE, CharacterDatabase.PQuery("SELECT maxbidpriceorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("maxBidPriceOrange = %u", config->GetMaxBidPrice(AHB_ORANGE));} + if (debug_Out) + { + sLog.outError("maxBidPriceOrange = %u", config->GetMaxBidPrice(AHB_ORANGE)); + } config->SetMinBidPrice(AHB_YELLOW, CharacterDatabase.PQuery("SELECT minbidpriceyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("minBidPriceYellow = %u", config->GetMinBidPrice(AHB_YELLOW));} + if (debug_Out) + { + sLog.outError("minBidPriceYellow = %u", config->GetMinBidPrice(AHB_YELLOW)); + } config->SetMaxBidPrice(AHB_YELLOW, CharacterDatabase.PQuery("SELECT maxbidpriceyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("maxBidPriceYellow = %u", config->GetMaxBidPrice(AHB_YELLOW));} + if (debug_Out) + { + sLog.outError("maxBidPriceYellow = %u", config->GetMaxBidPrice(AHB_YELLOW)); + } //load max stacks config->SetMaxStack(AHB_GREY, CharacterDatabase.PQuery("SELECT maxstackgrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("maxStackGrey = %u", config->GetMaxStack(AHB_GREY));} + if (debug_Out) + { + sLog.outError("maxStackGrey = %u", config->GetMaxStack(AHB_GREY)); + } config->SetMaxStack(AHB_WHITE, CharacterDatabase.PQuery("SELECT maxstackwhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("maxStackWhite = %u", config->GetMaxStack(AHB_WHITE));} + if (debug_Out) + { + sLog.outError("maxStackWhite = %u", config->GetMaxStack(AHB_WHITE)); + } config->SetMaxStack(AHB_GREEN, CharacterDatabase.PQuery("SELECT maxstackgreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("maxStackGreen = %u", config->GetMaxStack(AHB_GREEN));} + if (debug_Out) + { + sLog.outError("maxStackGreen = %u", config->GetMaxStack(AHB_GREEN)); + } config->SetMaxStack(AHB_BLUE, CharacterDatabase.PQuery("SELECT maxstackblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("maxStackBlue = %u", config->GetMaxStack(AHB_BLUE));} + if (debug_Out) + { + sLog.outError("maxStackBlue = %u", config->GetMaxStack(AHB_BLUE)); + } config->SetMaxStack(AHB_PURPLE, CharacterDatabase.PQuery("SELECT maxstackpurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("maxStackPurple = %u", config->GetMaxStack(AHB_PURPLE));} + if (debug_Out) + { + sLog.outError("maxStackPurple = %u", config->GetMaxStack(AHB_PURPLE)); + } config->SetMaxStack(AHB_ORANGE, CharacterDatabase.PQuery("SELECT maxstackorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("maxStackOrange = %u", config->GetMaxStack(AHB_ORANGE));} + if (debug_Out) + { + sLog.outError("maxStackOrange = %u", config->GetMaxStack(AHB_ORANGE)); + } config->SetMaxStack(AHB_YELLOW, CharacterDatabase.PQuery("SELECT maxstackyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("maxStackYellow = %u", config->GetMaxStack(AHB_YELLOW));} + if (debug_Out) + { + sLog.outError("maxStackYellow = %u", config->GetMaxStack(AHB_YELLOW)); + } } if (AHBBuyer) { @@ -1423,7 +1815,7 @@ void AuctionHouseBotLoadValues(AHBConfig *config) config->SetBuyerPrice(AHB_PURPLE, CharacterDatabase.PQuery("SELECT buyerpricepurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); config->SetBuyerPrice(AHB_ORANGE, CharacterDatabase.PQuery("SELECT buyerpriceorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); config->SetBuyerPrice(AHB_YELLOW, CharacterDatabase.PQuery("SELECT buyerpriceyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) + if (debug_Out) { sLog.outError("buyerPriceGrey = %u", config->GetBuyerPrice(AHB_GREY)); sLog.outError("buyerPriceWhite = %u", config->GetBuyerPrice(AHB_WHITE)); @@ -1435,11 +1827,15 @@ void AuctionHouseBotLoadValues(AHBConfig *config) } //load bidding interval config->SetBiddingInterval(CharacterDatabase.PQuery("SELECT buyerbiddinginterval FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("buyerBiddingInterval = %u", config->GetBiddingInterval());} + if (debug_Out) + { + sLog.outError("buyerBiddingInterval = %u", config->GetBiddingInterval()); + } //load bids per interval config->SetBidsPerInterval(CharacterDatabase.PQuery("SELECT buyerbidsperinterval FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if(debug_Out) - {sLog.outError("buyerBidsPerInterval = %u", config->GetBidsPerInterval());} + if (debug_Out) + { + sLog.outError("buyerBidsPerInterval = %u", config->GetBidsPerInterval()); + } } } diff --git a/src/game/AuctionHouseBot.h b/src/game/AuctionHouseBot.h index 906a857d206..df7e842ad4f 100644 --- a/src/game/AuctionHouseBot.h +++ b/src/game/AuctionHouseBot.h @@ -1,10 +1,9 @@ #ifndef AUCTION_HOUSE_BOT_H #define AUCTION_HOUSE_BOT_H -#include "Common.h" +#include "World.h" #include "Config/ConfigEnv.h" - -#include "Log.h" +#include "ace/Vector_T.h" #define AHB_GREY 0 #define AHB_WHITE 1 @@ -27,153 +26,149 @@ #define AHB_PURPLE_I 11 #define AHB_ORANGE_I 12 #define AHB_YELLOW_I 13 -#define AHBplayerAccount sConfig.GetIntDefault("AuctionHouseBot.Account", 0) -#define AHBplayerGUID sConfig.GetIntDefault("AuctionHouseBot.GUID", 0) -#define ItemsPerCycle sConfig.GetIntDefault("AuctionHouseBot.ItemsPerCycle", 200) -#define SellMethod sConfig.GetIntDefault("AuctionHouseBot.UseBuyPriceForSeller", 1) -#define BuyMethod sConfig.GetIntDefault("AuctionHouseBot.UseBuyPriceForBuyer", 0) class AHBConfig { - private: - uint32 AHID; - uint32 AHFID; - uint32 minItems; - uint32 maxItems; - uint32 percentGreyTradeGoods; - uint32 percentWhiteTradeGoods; - uint32 percentGreenTradeGoods; - uint32 percentBlueTradeGoods; - uint32 percentPurpleTradeGoods; - uint32 percentOrangeTradeGoods; - uint32 percentYellowTradeGoods; - uint32 percentGreyItems; - uint32 percentWhiteItems; - uint32 percentGreenItems; - uint32 percentBlueItems; - uint32 percentPurpleItems; - uint32 percentOrangeItems; - uint32 percentYellowItems; - uint32 minPriceGrey; - uint32 maxPriceGrey; - uint32 minBidPriceGrey; - uint32 maxBidPriceGrey; - uint32 maxStackGrey; - uint32 minPriceWhite; - uint32 maxPriceWhite; - uint32 minBidPriceWhite; - uint32 maxBidPriceWhite; - uint32 maxStackWhite; - uint32 minPriceGreen; - uint32 maxPriceGreen; - uint32 minBidPriceGreen; - uint32 maxBidPriceGreen; - uint32 maxStackGreen; - uint32 minPriceBlue; - uint32 maxPriceBlue; - uint32 minBidPriceBlue; - uint32 maxBidPriceBlue; - uint32 maxStackBlue; - uint32 minPricePurple; - uint32 maxPricePurple; - uint32 minBidPricePurple; - uint32 maxBidPricePurple; - uint32 maxStackPurple; - uint32 minPriceOrange; - uint32 maxPriceOrange; - uint32 minBidPriceOrange; - uint32 maxBidPriceOrange; - uint32 maxStackOrange; - uint32 minPriceYellow; - uint32 maxPriceYellow; - uint32 minBidPriceYellow; - uint32 maxBidPriceYellow; - uint32 maxStackYellow; +private: + uint32 AHID; + uint32 AHFID; + uint32 minItems; + uint32 maxItems; + uint32 percentGreyTradeGoods; + uint32 percentWhiteTradeGoods; + uint32 percentGreenTradeGoods; + uint32 percentBlueTradeGoods; + uint32 percentPurpleTradeGoods; + uint32 percentOrangeTradeGoods; + uint32 percentYellowTradeGoods; + uint32 percentGreyItems; + uint32 percentWhiteItems; + uint32 percentGreenItems; + uint32 percentBlueItems; + uint32 percentPurpleItems; + uint32 percentOrangeItems; + uint32 percentYellowItems; + uint32 minPriceGrey; + uint32 maxPriceGrey; + uint32 minBidPriceGrey; + uint32 maxBidPriceGrey; + uint32 maxStackGrey; + uint32 minPriceWhite; + uint32 maxPriceWhite; + uint32 minBidPriceWhite; + uint32 maxBidPriceWhite; + uint32 maxStackWhite; + uint32 minPriceGreen; + uint32 maxPriceGreen; + uint32 minBidPriceGreen; + uint32 maxBidPriceGreen; + uint32 maxStackGreen; + uint32 minPriceBlue; + uint32 maxPriceBlue; + uint32 minBidPriceBlue; + uint32 maxBidPriceBlue; + uint32 maxStackBlue; + uint32 minPricePurple; + uint32 maxPricePurple; + uint32 minBidPricePurple; + uint32 maxBidPricePurple; + uint32 maxStackPurple; + uint32 minPriceOrange; + uint32 maxPriceOrange; + uint32 minBidPriceOrange; + uint32 maxBidPriceOrange; + uint32 maxStackOrange; + uint32 minPriceYellow; + uint32 maxPriceYellow; + uint32 minBidPriceYellow; + uint32 maxBidPriceYellow; + uint32 maxStackYellow; + + uint32 buyerPriceGrey; + uint32 buyerPriceWhite; + uint32 buyerPriceGreen; + uint32 buyerPriceBlue; + uint32 buyerPricePurple; + uint32 buyerPriceOrange; + uint32 buyerPriceYellow; + uint32 buyerBiddingInterval; + uint32 buyerBidsPerInterval; - uint32 buyerPriceGrey; - uint32 buyerPriceWhite; - uint32 buyerPriceGreen; - uint32 buyerPriceBlue; - uint32 buyerPricePurple; - uint32 buyerPriceOrange; - uint32 buyerPriceYellow; - uint32 buyerBiddingInterval; - uint32 buyerBidsPerInterval; + uint32 greytgp; + uint32 whitetgp; + uint32 greentgp; + uint32 bluetgp; + uint32 purpletgp; + uint32 orangetgp; + uint32 yellowtgp; + uint32 greyip; + uint32 whiteip; + uint32 greenip; + uint32 blueip; + uint32 purpleip; + uint32 orangeip; + uint32 yellowip; - uint32 greytgp; - uint32 whitetgp; - uint32 greentgp; - uint32 bluetgp; - uint32 purpletgp; - uint32 orangetgp; - uint32 yellowtgp; - uint32 greyip; - uint32 whiteip; - uint32 greenip; - uint32 blueip; - uint32 purpleip; - uint32 orangeip; - uint32 yellowip; - public: +public: AHBConfig(uint32 ahid) - { - AHID = ahid; - switch(ahid) - { - case 2: - AHFID = 55; - break; - case 6: + { + AHID = ahid; + switch(ahid) + { + case 2: + AHFID = 55; + break; + case 6: AHFID = 29; - break; - case 7: + break; + case 7: AHFID = 120; - break; - default: + break; + default: AHFID = 120; - break; - } - } - AHBConfig() - { - } - uint32 GetAHID() - { - return AHID; - } - uint32 GetAHFID() - { - return AHFID; - } - void SetMinItems(uint32 value) - { - minItems = value; - } - uint32 GetMinItems() - { + break; + } + } + AHBConfig() + { + } + uint32 GetAHID() + { + return AHID; + } + uint32 GetAHFID() + { + return AHFID; + } + void SetMinItems(uint32 value) + { + minItems = value; + } + uint32 GetMinItems() + { if ((minItems == 0) && (maxItems)) return maxItems; else if ((maxItems) && (minItems > maxItems)) return maxItems; else return minItems; - } - void SetMaxItems(uint32 value) - { - maxItems = value; - CalculatePercents(); - } - uint32 GetMaxItems() - { + } + void SetMaxItems(uint32 value) + { + maxItems = value; + CalculatePercents(); + } + uint32 GetMaxItems() + { return maxItems; - } - void SetPercentages(uint32 greytg, uint32 whitetg, uint32 greentg, uint32 bluetg, uint32 purpletg, uint32 orangetg, uint32 yellowtg, uint32 greyi, uint32 whitei, uint32 greeni, uint32 bluei, uint32 purplei, uint32 orangei, uint32 yellowi) - { + } + void SetPercentages(uint32 greytg, uint32 whitetg, uint32 greentg, uint32 bluetg, uint32 purpletg, uint32 orangetg, uint32 yellowtg, uint32 greyi, uint32 whitei, uint32 greeni, uint32 bluei, uint32 purplei, uint32 orangei, uint32 yellowi) + { uint32 totalPercent = greytg + whitetg + greentg + bluetg + purpletg + orangetg + yellowtg + greyi + whitei + greeni + bluei + purplei + orangei + yellowi; if (totalPercent == 0) { - maxItems = 0; + maxItems = 0; } else if (totalPercent != 100) { @@ -207,92 +202,92 @@ class AHBConfig percentOrangeItems = orangei; percentYellowItems = yellowi; CalculatePercents(); - } - uint32 GetPercentages(uint32 color) - { - switch(color) - { - case AHB_GREY_TG: + } + uint32 GetPercentages(uint32 color) + { + switch(color) + { + case AHB_GREY_TG: return percentGreyTradeGoods; - break; - case AHB_WHITE_TG: + break; + case AHB_WHITE_TG: return percentWhiteTradeGoods; - break; - case AHB_GREEN_TG: + break; + case AHB_GREEN_TG: return percentGreenTradeGoods; - break; - case AHB_BLUE_TG: + break; + case AHB_BLUE_TG: return percentBlueTradeGoods; - break; - case AHB_PURPLE_TG: + break; + case AHB_PURPLE_TG: return percentPurpleTradeGoods; - break; - case AHB_ORANGE_TG: + break; + case AHB_ORANGE_TG: return percentOrangeTradeGoods; - break; - case AHB_YELLOW_TG: + break; + case AHB_YELLOW_TG: return percentYellowTradeGoods; - break; - case AHB_GREY_I: + break; + case AHB_GREY_I: return percentGreyItems; - break; - case AHB_WHITE_I: + break; + case AHB_WHITE_I: return percentWhiteItems; - break; - case AHB_GREEN_I: + break; + case AHB_GREEN_I: return percentGreenItems; - break; - case AHB_BLUE_I: + break; + case AHB_BLUE_I: return percentBlueItems; - break; - case AHB_PURPLE_I: + break; + case AHB_PURPLE_I: return percentPurpleItems; - break; - case AHB_ORANGE_I: + break; + case AHB_ORANGE_I: return percentOrangeItems; - break; - case AHB_YELLOW_I: + break; + case AHB_YELLOW_I: return percentYellowItems; - break; - default: + break; + default: return 0; - break; - } - } - void SetMinPrice(uint32 color, uint32 value) - { - switch(color) - { - case AHB_GREY: + break; + } + } + void SetMinPrice(uint32 color, uint32 value) + { + switch(color) + { + case AHB_GREY: minPriceGrey = value; - break; - case AHB_WHITE: + break; + case AHB_WHITE: minPriceWhite = value; - break; - case AHB_GREEN: + break; + case AHB_GREEN: minPriceGreen = value; - break; - case AHB_BLUE: + break; + case AHB_BLUE: minPriceBlue = value; - break; - case AHB_PURPLE: + break; + case AHB_PURPLE: minPricePurple = value; - break; - case AHB_ORANGE: + break; + case AHB_ORANGE: minPriceOrange = value; - break; - case AHB_YELLOW: + break; + case AHB_YELLOW: minPriceYellow = value; - break; - default: - break; - } - } - uint32 GetMinPrice(uint32 color) - { - switch(color) - { - case AHB_GREY: + break; + default: + break; + } + } + uint32 GetMinPrice(uint32 color) + { + switch(color) + { + case AHB_GREY: { if (minPriceGrey == 0) return 100; @@ -302,7 +297,7 @@ class AHBConfig return minPriceGrey; break; } - case AHB_WHITE: + case AHB_WHITE: { if (minPriceWhite == 0) return 150; @@ -312,7 +307,7 @@ class AHBConfig return minPriceWhite; break; } - case AHB_GREEN: + case AHB_GREEN: { if (minPriceGreen == 0) return 200; @@ -322,7 +317,7 @@ class AHBConfig return minPriceGreen; break; } - case AHB_BLUE: + case AHB_BLUE: { if (minPriceBlue == 0) return 250; @@ -332,7 +327,7 @@ class AHBConfig return minPriceBlue; break; } - case AHB_PURPLE: + case AHB_PURPLE: { if (minPricePurple == 0) return 300; @@ -342,7 +337,7 @@ class AHBConfig return minPricePurple; break; } - case AHB_ORANGE: + case AHB_ORANGE: { if (minPriceOrange == 0) return 400; @@ -352,7 +347,7 @@ class AHBConfig return minPriceOrange; break; } - case AHB_YELLOW: + case AHB_YELLOW: { if (minPriceYellow == 0) return 500; @@ -362,47 +357,47 @@ class AHBConfig return minPriceYellow; break; } - default: - { - return 0; - break; - } + default: + { + return 0; + break; + } } - } - void SetMaxPrice(uint32 color, uint32 value) - { - switch(color) - { - case AHB_GREY: + } + void SetMaxPrice(uint32 color, uint32 value) + { + switch(color) + { + case AHB_GREY: maxPriceGrey = value; - break; - case AHB_WHITE: + break; + case AHB_WHITE: maxPriceWhite = value; - break; - case AHB_GREEN: + break; + case AHB_GREEN: maxPriceGreen = value; - break; - case AHB_BLUE: + break; + case AHB_BLUE: maxPriceBlue = value; - break; - case AHB_PURPLE: + break; + case AHB_PURPLE: maxPricePurple = value; - break; - case AHB_ORANGE: + break; + case AHB_ORANGE: maxPriceOrange = value; - break; - case AHB_YELLOW: + break; + case AHB_YELLOW: maxPriceYellow = value; - break; - default: - break; - } - } - uint32 GetMaxPrice(uint32 color) - { - switch(color) - { - case AHB_GREY: + break; + default: + break; + } + } + uint32 GetMaxPrice(uint32 color) + { + switch(color) + { + case AHB_GREY: { if (maxPriceGrey == 0) return 150; @@ -410,7 +405,7 @@ class AHBConfig return maxPriceGrey; break; } - case AHB_WHITE: + case AHB_WHITE: { if (maxPriceWhite == 0) return 250; @@ -418,7 +413,7 @@ class AHBConfig return maxPriceWhite; break; } - case AHB_GREEN: + case AHB_GREEN: { if (maxPriceGreen == 0) return 300; @@ -426,7 +421,7 @@ class AHBConfig return maxPriceGreen; break; } - case AHB_BLUE: + case AHB_BLUE: { if (maxPriceBlue == 0) return 350; @@ -434,7 +429,7 @@ class AHBConfig return maxPriceBlue; break; } - case AHB_PURPLE: + case AHB_PURPLE: { if (maxPricePurple == 0) return 450; @@ -442,7 +437,7 @@ class AHBConfig return maxPricePurple; break; } - case AHB_ORANGE: + case AHB_ORANGE: { if (maxPriceOrange == 0) return 550; @@ -450,7 +445,7 @@ class AHBConfig return maxPriceOrange; break; } - case AHB_YELLOW: + case AHB_YELLOW: { if (maxPriceYellow == 0) return 650; @@ -458,47 +453,47 @@ class AHBConfig return maxPriceYellow; break; } - default: - { - return 0; - break; - } + default: + { + return 0; + break; + } } - } - void SetMinBidPrice(uint32 color, uint32 value) - { - switch(color) - { - case AHB_GREY: + } + void SetMinBidPrice(uint32 color, uint32 value) + { + switch(color) + { + case AHB_GREY: minBidPriceGrey = value; - break; - case AHB_WHITE: + break; + case AHB_WHITE: minBidPriceWhite = value; - break; - case AHB_GREEN: + break; + case AHB_GREEN: minBidPriceGreen = value; - break; - case AHB_BLUE: + break; + case AHB_BLUE: minBidPriceBlue = value; - break; - case AHB_PURPLE: + break; + case AHB_PURPLE: minBidPricePurple = value; - break; - case AHB_ORANGE: + break; + case AHB_ORANGE: minBidPriceOrange = value; - break; - case AHB_YELLOW: + break; + case AHB_YELLOW: minBidPriceYellow = value; - break; - default: - break; - } - } - uint32 GetMinBidPrice(uint32 color) - { - switch(color) - { - case AHB_GREY: + break; + default: + break; + } + } + uint32 GetMinBidPrice(uint32 color) + { + switch(color) + { + case AHB_GREY: { if (minBidPriceGrey > 100) return 100; @@ -506,7 +501,7 @@ class AHBConfig return minBidPriceGrey; break; } - case AHB_WHITE: + case AHB_WHITE: { if (minBidPriceWhite > 100) return 100; @@ -514,7 +509,7 @@ class AHBConfig return minBidPriceWhite; break; } - case AHB_GREEN: + case AHB_GREEN: { if (minBidPriceGreen > 100) return 100; @@ -522,7 +517,7 @@ class AHBConfig return minBidPriceGreen; break; } - case AHB_BLUE: + case AHB_BLUE: { if (minBidPriceBlue > 100) return 100; @@ -530,7 +525,7 @@ class AHBConfig return minBidPriceBlue; break; } - case AHB_PURPLE: + case AHB_PURPLE: { if (minBidPricePurple > 100) return 100; @@ -538,7 +533,7 @@ class AHBConfig return minBidPricePurple; break; } - case AHB_ORANGE: + case AHB_ORANGE: { if (minBidPriceOrange > 100) return 100; @@ -546,7 +541,7 @@ class AHBConfig return minBidPriceOrange; break; } - case AHB_YELLOW: + case AHB_YELLOW: { if (minBidPriceYellow > 100) return 100; @@ -554,47 +549,47 @@ class AHBConfig return minBidPriceYellow; break; } - default: - { - return 0; - break; - } - } - } - void SetMaxBidPrice(uint32 color, uint32 value) - { - switch(color) - { - case AHB_GREY: + default: + { + return 0; + break; + } + } + } + void SetMaxBidPrice(uint32 color, uint32 value) + { + switch(color) + { + case AHB_GREY: maxBidPriceGrey = value; - break; - case AHB_WHITE: + break; + case AHB_WHITE: maxBidPriceWhite = value; - break; - case AHB_GREEN: + break; + case AHB_GREEN: maxBidPriceGreen = value; - break; - case AHB_BLUE: + break; + case AHB_BLUE: maxBidPriceBlue = value; - break; - case AHB_PURPLE: + break; + case AHB_PURPLE: maxBidPricePurple = value; - break; - case AHB_ORANGE: + break; + case AHB_ORANGE: maxBidPriceOrange = value; - break; - case AHB_YELLOW: + break; + case AHB_YELLOW: maxBidPriceYellow = value; - break; - default: - break; - } - } - uint32 GetMaxBidPrice(uint32 color) - { - switch(color) - { - case AHB_GREY: + break; + default: + break; + } + } + uint32 GetMaxBidPrice(uint32 color) + { + switch(color) + { + case AHB_GREY: { if (maxBidPriceGrey > 100) return 100; @@ -602,7 +597,7 @@ class AHBConfig return maxBidPriceGrey; break; } - case AHB_WHITE: + case AHB_WHITE: { if (maxBidPriceWhite > 100) return 100; @@ -610,7 +605,7 @@ class AHBConfig return maxBidPriceWhite; break; } - case AHB_GREEN: + case AHB_GREEN: { if (maxBidPriceGreen > 100) return 100; @@ -618,7 +613,7 @@ class AHBConfig return maxBidPriceGreen; break; } - case AHB_BLUE: + case AHB_BLUE: { if (maxBidPriceBlue > 100) return 100; @@ -626,7 +621,7 @@ class AHBConfig return maxBidPriceBlue; break; } - case AHB_PURPLE: + case AHB_PURPLE: { if (maxBidPricePurple > 100) return 100; @@ -634,7 +629,7 @@ class AHBConfig return maxBidPricePurple; break; } - case AHB_ORANGE: + case AHB_ORANGE: { if (maxBidPriceOrange > 100) return 100; @@ -642,7 +637,7 @@ class AHBConfig return maxBidPriceOrange; break; } - case AHB_YELLOW: + case AHB_YELLOW: { if (maxBidPriceYellow > 100) return 100; @@ -650,157 +645,157 @@ class AHBConfig return maxBidPriceYellow; break; } - default: - { - return 0; - break; - } - } - } - void SetMaxStack(uint32 color, uint32 value) - { - switch(color) - { - case AHB_GREY: + default: + { + return 0; + break; + } + } + } + void SetMaxStack(uint32 color, uint32 value) + { + switch(color) + { + case AHB_GREY: maxStackGrey = value; - break; - case AHB_WHITE: + break; + case AHB_WHITE: maxStackWhite = value; - break; - case AHB_GREEN: + break; + case AHB_GREEN: maxStackGreen = value; - break; - case AHB_BLUE: + break; + case AHB_BLUE: maxStackBlue = value; - break; - case AHB_PURPLE: + break; + case AHB_PURPLE: maxStackPurple = value; - break; - case AHB_ORANGE: + break; + case AHB_ORANGE: maxStackOrange = value; - break; - case AHB_YELLOW: + break; + case AHB_YELLOW: maxStackYellow = value; - break; - default: - break; - } - } - uint32 GetMaxStack(uint32 color) - { - switch(color) - { - case AHB_GREY: + break; + default: + break; + } + } + uint32 GetMaxStack(uint32 color) + { + switch(color) + { + case AHB_GREY: { return maxStackGrey; break; } - case AHB_WHITE: + case AHB_WHITE: { return maxStackWhite; break; } - case AHB_GREEN: + case AHB_GREEN: { return maxStackGreen; break; } - case AHB_BLUE: + case AHB_BLUE: { return maxStackBlue; break; } - case AHB_PURPLE: + case AHB_PURPLE: { return maxStackPurple; break; } - case AHB_ORANGE: + case AHB_ORANGE: { return maxStackOrange; break; } - case AHB_YELLOW: + case AHB_YELLOW: { return maxStackYellow; break; } - default: - { - return 0; - break; - } - } - } - void SetBuyerPrice(uint32 color, uint32 value) - { - switch(color) - { - case AHB_GREY: + default: + { + return 0; + break; + } + } + } + void SetBuyerPrice(uint32 color, uint32 value) + { + switch(color) + { + case AHB_GREY: buyerPriceGrey = value; - break; - case AHB_WHITE: + break; + case AHB_WHITE: buyerPriceWhite = value; - break; - case AHB_GREEN: + break; + case AHB_GREEN: buyerPriceGreen = value; - break; - case AHB_BLUE: + break; + case AHB_BLUE: buyerPriceBlue = value; - break; - case AHB_PURPLE: + break; + case AHB_PURPLE: buyerPricePurple = value; - break; - case AHB_ORANGE: + break; + case AHB_ORANGE: buyerPriceOrange = value; - break; - case AHB_YELLOW: + break; + case AHB_YELLOW: buyerPriceYellow = value; - break; - default: - break; - } - } - uint32 GetBuyerPrice(uint32 color) - { - switch(color) - { - case AHB_GREY: + break; + default: + break; + } + } + uint32 GetBuyerPrice(uint32 color) + { + switch(color) + { + case AHB_GREY: return buyerPriceGrey; - break; - case AHB_WHITE: + break; + case AHB_WHITE: return buyerPriceWhite; - break; - case AHB_GREEN: + break; + case AHB_GREEN: return buyerPriceGreen; - break; - case AHB_BLUE: + break; + case AHB_BLUE: return buyerPriceBlue; - break; - case AHB_PURPLE: + break; + case AHB_PURPLE: return buyerPricePurple; - break; - case AHB_ORANGE: + break; + case AHB_ORANGE: return buyerPriceOrange; - break; - case AHB_YELLOW: + break; + case AHB_YELLOW: return buyerPriceYellow; - break; - default: - return 0; - break; - } - } - void SetBiddingInterval(uint32 value) - { - buyerBiddingInterval = value; - } - uint32 GetBiddingInterval() - { - return buyerBiddingInterval; - } - void CalculatePercents() - { + break; + default: + return 0; + break; + } + } + void SetBiddingInterval(uint32 value) + { + buyerBiddingInterval = value; + } + uint32 GetBiddingInterval() + { + return buyerBiddingInterval; + } + void CalculatePercents() + { greytgp = (uint32) (((double)percentGreyTradeGoods / 100.0) * maxItems); whitetgp = (uint32) (((double)percentWhiteTradeGoods / 100.0) * maxItems); greentgp = (uint32) (((double)percentGreenTradeGoods / 100.0) * maxItems); @@ -828,72 +823,177 @@ class AHBConfig { whiteip += diff; } - } - uint32 GetPercents(uint32 color) - { - switch(color) - { - case AHB_GREY_TG: + } + uint32 GetPercents(uint32 color) + { + switch(color) + { + case AHB_GREY_TG: return greytgp; - break; - case AHB_WHITE_TG: + break; + case AHB_WHITE_TG: return whitetgp; - break; - case AHB_GREEN_TG: + break; + case AHB_GREEN_TG: return greentgp; - break; - case AHB_BLUE_TG: + break; + case AHB_BLUE_TG: return bluetgp; - break; - case AHB_PURPLE_TG: + break; + case AHB_PURPLE_TG: return purpletgp; - break; - case AHB_ORANGE_TG: + break; + case AHB_ORANGE_TG: return orangetgp; - break; - case AHB_YELLOW_TG: + break; + case AHB_YELLOW_TG: return yellowtgp; - break; - case AHB_GREY_I: - return greyip; - break; - case AHB_WHITE_I: + break; + case AHB_GREY_I: + return greyip; + break; + case AHB_WHITE_I: return whiteip; - break; - case AHB_GREEN_I: + break; + case AHB_GREEN_I: return greenip; - break; - case AHB_BLUE_I: + break; + case AHB_BLUE_I: return blueip; - break; - case AHB_PURPLE_I: + break; + case AHB_PURPLE_I: return purpleip; - break; - case AHB_ORANGE_I: + break; + case AHB_ORANGE_I: return orangeip; - break; - case AHB_YELLOW_I: + break; + case AHB_YELLOW_I: return yellowip; - break; - default: - return 0; - break; - } - } - void SetBidsPerInterval(uint32 value) - { - buyerBidsPerInterval = value; - } - uint32 GetBidsPerInterval() - { - return buyerBidsPerInterval; - } - ~AHBConfig() - { - } + break; + default: + return 0; + break; + } + } + void SetBidsPerInterval(uint32 value) + { + buyerBidsPerInterval = value; + } + uint32 GetBidsPerInterval() + { + return buyerBidsPerInterval; + } + ~AHBConfig() + { + } }; -void AuctionHouseBot(); -void AuctionHouseBotInit(); -void AuctionHouseBotLoadValues(AHBConfig*); -void AuctionHouseBotCommands(uint32, uint32, uint32, char*); +class AuctionHouseBot +{ +private: + ACE_Vector<uint32> npcItems; + ACE_Vector<uint32> lootItems; + ACE_Vector<uint32> greyTradeGoodsBin; + ACE_Vector<uint32> whiteTradeGoodsBin; + ACE_Vector<uint32> greenTradeGoodsBin; + ACE_Vector<uint32> blueTradeGoodsBin; + ACE_Vector<uint32> purpleTradeGoodsBin; + ACE_Vector<uint32> orangeTradeGoodsBin; + ACE_Vector<uint32> yellowTradeGoodsBin; + ACE_Vector<uint32> greyItemsBin; + ACE_Vector<uint32> whiteItemsBin; + ACE_Vector<uint32> greenItemsBin; + ACE_Vector<uint32> blueItemsBin; + ACE_Vector<uint32> purpleItemsBin; + ACE_Vector<uint32> orangeItemsBin; + ACE_Vector<uint32> yellowItemsBin; + + bool debug_Out; + + bool AHBSeller; + bool AHBBuyer; + bool BuyMethod; + bool SellMethod; + + uint32 AHBplayerAccount; + uint32 AHBplayerGUID; + uint32 ItemsPerCycle; + + //Begin Filters + + bool Vendor_Items; + bool Loot_Items; + bool Other_Items; + + bool No_Bind; + bool Bind_When_Picked_Up; + bool Bind_When_Equipped; + bool Bind_When_Use; + bool Bind_Quest_Item; + + bool DisableBeta_PTR_Unused; + bool DisablePermEnchant; + bool DisableConjured; + bool DisableGems; + bool DisableMoney; + bool DisableMoneyLoot; + bool DisableLootable; + bool DisableKeys; + bool DisableDuration; + bool DisableBOP_Or_Quest_NoReqLevel; + + bool DisableWarriorItems; + bool DisablePaladinItems; + bool DisableHunterItems; + bool DisableRogueItems; + bool DisablePriestItems; + bool DisableDKItems; + bool DisableShamanItems; + bool DisableMageItems; + bool DisableWarlockItems; + bool DisableUnusedClassItems; + bool DisableDruidItems; + + uint32 DisableItemsBelowLevel; + uint32 DisableItemsAboveLevel; + uint32 DisableTGsBelowLevel; + uint32 DisableTGsAboveLevel; + uint32 DisableItemsBelowGUID; + uint32 DisableItemsAboveGUID; + uint32 DisableTGsBelowGUID; + uint32 DisableTGsAboveGUID; + uint32 DisableItemsBelowReqLevel; + uint32 DisableItemsAboveReqLevel; + uint32 DisableTGsBelowReqLevel; + uint32 DisableTGsAboveReqLevel; + uint32 DisableItemsBelowReqSkillRank; + uint32 DisableItemsAboveReqSkillRank; + uint32 DisableTGsBelowReqSkillRank; + uint32 DisableTGsAboveReqSkillRank; + + //End Filters + + AHBConfig AllianceConfig; + AHBConfig HordeConfig; + AHBConfig NeutralConfig; + + time_t _lastrun_a; + time_t _lastrun_h; + time_t _lastrun_n; + + inline uint32 minValue(uint32 a, uint32 b) { return a <= b ? a : b; }; + void addNewAuctions(Player *AHBplayer, AHBConfig *config); + void addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *config, WorldSession *session); + +public: + AuctionHouseBot(); + ~AuctionHouseBot(); + void Update(); + void Initialize(); + void LoadValues(AHBConfig*); + void Commands(uint32, uint32, uint32, char*); + uint32 GetAHBplayerGUID() { return AHBplayerGUID; }; +}; + +#define auctionbot Trinity::Singleton<AuctionHouseBot>::Instance() + #endif diff --git a/src/game/AuctionHouseHandler.cpp b/src/game/AuctionHouseHandler.cpp index d1f399bf570..9962a16bc77 100644 --- a/src/game/AuctionHouseHandler.cpp +++ b/src/game/AuctionHouseHandler.cpp @@ -127,7 +127,7 @@ void WorldSession::SendAuctionOutbiddedMail(AuctionEntry *auction, uint32 newPri msgAuctionOutbiddedSubject << auction->item_template << ":0:" << AUCTION_OUTBIDDED; if (oldBidder && !_player) - oldBidder->GetSession()->SendAuctionBidderNotification( auction->GetHouseId(), auction->Id, AHBplayerGUID, newPrice, auction->GetAuctionOutBid(), auction->item_template); + oldBidder->GetSession()->SendAuctionBidderNotification( auction->GetHouseId(), auction->Id, auctionbot.GetAHBplayerGUID(), newPrice, auction->GetAuctionOutBid(), auction->item_template); if (oldBidder && _player) oldBidder->GetSession()->SendAuctionBidderNotification( auction->GetHouseId(), auction->Id, _player->GetGUID(), newPrice, auction->GetAuctionOutBid(), auction->item_template); diff --git a/src/game/BattleGround.cpp b/src/game/BattleGround.cpp index 71062657c0b..709cac492d5 100644 --- a/src/game/BattleGround.cpp +++ b/src/game/BattleGround.cpp @@ -182,6 +182,9 @@ BattleGround::BattleGround() m_PlayersCount[BG_TEAM_ALLIANCE] = 0; m_PlayersCount[BG_TEAM_HORDE] = 0; + m_TeamScores[BG_TEAM_ALLIANCE] = 0; + m_TeamScores[BG_TEAM_HORDE] = 0; + m_PrematureCountDown = false; m_PrematureCountDown = 0; @@ -204,16 +207,13 @@ BattleGround::~BattleGround() // (this is done automatically in mapmanager update, when the instance is reset after the reset time) int size = m_BgCreatures.size(); for(int i = 0; i < size; ++i) - { DelCreature(i); - } + size = m_BgObjects.size(); for(int i = 0; i < size; ++i) - { DelObject(i); - } - if(GetInstanceID()) // not spam by useless queries in case BG templates + if (GetInstanceID()) // not spam by useless queries in case BG templates { // delete creature and go respawn times WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'",GetInstanceID()); @@ -230,6 +230,9 @@ BattleGround::~BattleGround() ((BattleGroundMap*)map)->SetUnload(); // remove from bg free slot queue this->RemoveFromBGFreeSlotQueue(); + + for(BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr) + delete itr->second; } void BattleGround::Update(uint32 diff) @@ -473,7 +476,7 @@ void BattleGround::Update(uint32 diff) void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O) { - uint8 idx = GetTeamIndexByTeamId(TeamID); + BattleGroundTeamId idx = GetTeamIndexByTeamId(TeamID); m_TeamStartLocX[idx] = X; m_TeamStartLocY[idx] = Y; m_TeamStartLocZ[idx] = Z; @@ -759,6 +762,7 @@ void BattleGround::EndBattleGround(uint32 winner) { RewardMark(plr,ITEM_WINNER_COUNT); RewardQuestComplete(plr); + plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_BG, 1); } else if(winner) RewardMark(plr,ITEM_LOSER_COUNT); @@ -843,7 +847,7 @@ void BattleGround::RewardMark(Player *plr,uint32 count) void BattleGround::RewardSpellCast(Player *plr, uint32 spell_id) { // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens - if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE)) + if (plr->HasAura(SPELL_AURA_PLAYER_INACTIVE)) return; SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id); @@ -859,7 +863,7 @@ void BattleGround::RewardSpellCast(Player *plr, uint32 spell_id) void BattleGround::RewardItem(Player *plr, uint32 item_id, uint32 count) { // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens - if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE)) + if (plr->HasAura(SPELL_AURA_PLAYER_INACTIVE)) return; ItemPosCountVec dest; @@ -963,7 +967,7 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac participant = true; } - std::map<uint64, BattleGroundScore*>::iterator itr2 = m_PlayerScores.find(guid); + BattleGroundScoreMap::iterator itr2 = m_PlayerScores.find(guid); if (itr2 != m_PlayerScores.end()) { delete itr2->second; // delete player's score @@ -1105,7 +1109,12 @@ void BattleGround::Reset() m_InBGFreeSlotQueue = false; m_Players.clear(); + + for(BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr) + delete itr->second; m_PlayerScores.clear(); + + ResetBGSubclass(); } void BattleGround::StartBattleGround() @@ -1342,7 +1351,7 @@ bool BattleGround::HasFreeSlots() const void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value) { //this procedure is called from virtual function implemented in bg subclass - std::map<uint64, BattleGroundScore*>::const_iterator itr = m_PlayerScores.find(Source->GetGUID()); + BattleGroundScoreMap::const_iterator itr = m_PlayerScores.find(Source->GetGUID()); if(itr == m_PlayerScores.end()) // player not found... return; @@ -1609,8 +1618,6 @@ bool BattleGround::DelCreature(uint32 type) sLog.outError("Can't find creature guid: %u",GUID_LOPART(m_BgCreatures[type])); return false; } - //TODO: only delete creature after not in combat - cr->CleanupsBeforeDelete(); cr->AddObjectToRemoveList(); m_BgCreatures[type] = 0; return true; @@ -1876,3 +1883,9 @@ WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard( Player* player ) { return objmgr.GetClosestGraveYard( player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam() ); } + +bool BattleGround::IsTeamScoreInRange(uint32 team, uint32 minScore, uint32 maxScore) const +{ + BattleGroundTeamId team_idx = GetTeamIndexByTeamId(team); + return m_TeamScores[team_idx] >= minScore && m_TeamScores[team_idx] <= maxScore; +} diff --git a/src/game/BattleGround.h b/src/game/BattleGround.h index 509ba6eb972..4ed660a3595 100644 --- a/src/game/BattleGround.h +++ b/src/game/BattleGround.h @@ -297,6 +297,13 @@ class BattleGround virtual void Reset(); // resets all common properties for battlegrounds, must be implemented and called in BG subclass virtual void StartingEventCloseDoors() {} virtual void StartingEventOpenDoors() {} + virtual void ResetBGSubclass() // must be implemented in BG subclass + { + } + + /* achievement req. */ + virtual bool IsAllNodesConrolledByTeam(uint32 /*team*/) const { return false; } + bool IsTeamScoreInRange(uint32 team, uint32 minScore, uint32 maxScore) const; /* Battleground */ // Get methods: @@ -377,8 +384,9 @@ class BattleGround BattleGroundPlayerMap const& GetPlayers() const { return m_Players; } uint32 GetPlayersSize() const { return m_Players.size(); } - std::map<uint64, BattleGroundScore*>::const_iterator GetPlayerScoresBegin() const { return m_PlayerScores.begin(); } - std::map<uint64, BattleGroundScore*>::const_iterator GetPlayerScoresEnd() const { return m_PlayerScores.end(); } + typedef std::map<uint64, BattleGroundScore*> BattleGroundScoreMap; + BattleGroundScoreMap::const_iterator GetPlayerScoresBegin() const { return m_PlayerScores.begin(); } + BattleGroundScoreMap::const_iterator GetPlayerScoresEnd() const { return m_PlayerScores.end(); } uint32 GetPlayerScoresSize() const { return m_PlayerScores.size(); } uint32 GetReviveQueueSize() const { return m_ReviveQueue.size(); } @@ -397,7 +405,7 @@ class BattleGround void SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O); void GetTeamStartLoc(uint32 TeamID, float &X, float &Y, float &Z, float &O) const { - uint8 idx = GetTeamIndexByTeamId(TeamID); + BattleGroundTeamId idx = GetTeamIndexByTeamId(TeamID); X = m_TeamStartLocX[idx]; Y = m_TeamStartLocY[idx]; Z = m_TeamStartLocZ[idx]; @@ -441,7 +449,7 @@ class BattleGround virtual void UpdatePlayerScore(Player *Source, uint32 type, uint32 value); - uint8 GetTeamIndexByTeamId(uint32 Team) const { return Team == ALLIANCE ? BG_TEAM_ALLIANCE : BG_TEAM_HORDE; } + static BattleGroundTeamId GetTeamIndexByTeamId(uint32 Team) { return Team == ALLIANCE ? BG_TEAM_ALLIANCE : BG_TEAM_HORDE; } uint32 GetPlayersCountByTeam(uint32 Team) const { return m_PlayersCount[GetTeamIndexByTeamId(Team)]; } uint32 GetAlivePlayersCountByTeam(uint32 Team) const; // used in arenas to correctly handle death in spirit of redemption / last stand etc. (killer = killed) cases void UpdatePlayersCountByTeam(uint32 Team, bool remove) @@ -514,14 +522,17 @@ class BattleGround void SetDeleteThis() {m_SetDeleteThis = true;} + /* virtual score-array - get's used in bg-subclasses */ + int32 m_TeamScores[BG_TEAMS_COUNT]; + protected: //this method is called, when BG cannot spawn its own spirit guide, or something is wrong, It correctly ends BattleGround void EndNow(); void PlayerAddedToBGCheckIfBGIsRunning(Player* plr); /* Scorekeeping */ - // Player scores - std::map<uint64, BattleGroundScore*> m_PlayerScores; + + BattleGroundScoreMap m_PlayerScores; // Player scores // must be implemented in BG subclass virtual void RemovePlayer(Player * /*player*/, uint64 /*guid*/) {} diff --git a/src/game/BattleGroundAB.cpp b/src/game/BattleGroundAB.cpp index f5fba6f4186..70709af42ad 100644 --- a/src/game/BattleGroundAB.cpp +++ b/src/game/BattleGroundAB.cpp @@ -161,6 +161,18 @@ void BattleGroundAB::Update(uint32 diff) } } + // achievements flags + if (m_TeamScores[BG_TEAM_ALLIANCE] > m_TeamScores[BG_TEAM_HORDE]) + { + if (m_TeamScores[BG_TEAM_ALLIANCE] - m_TeamScores[BG_TEAM_HORDE] >= 500) + m_TeamScores500disadvantage[BG_TEAM_HORDE] = true; + } + else + { + if (m_TeamScores[BG_TEAM_HORDE] - m_TeamScores[BG_TEAM_ALLIANCE] >= 500) + m_TeamScores500disadvantage[BG_TEAM_ALLIANCE] = true; + } + // Test win condition if (m_TeamScores[BG_TEAM_ALLIANCE] >= BG_AB_MAX_TEAM_SCORE) EndBattleGround(ALLIANCE); @@ -428,7 +440,7 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ return; } - uint8 teamIndex = GetTeamIndexByTeamId(source->GetTeam()); + BattleGroundTeamId teamIndex = GetTeamIndexByTeamId(source->GetTeam()); // Check if player really could use this banner, not cheated if (!(m_Nodes[node] == 0 || teamIndex == m_Nodes[node]%2)) @@ -474,7 +486,7 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; // FIXME: node names not localized - if (teamIndex == 0) + if (teamIndex == BG_TEAM_ALLIANCE) SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node)); else SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); @@ -491,15 +503,15 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ _CreateBanner(node, BG_AB_NODE_TYPE_OCCUPIED, teamIndex, true); _SendNodeUpdate(node); m_NodeTimers[node] = 0; - _NodeOccupied(node,(teamIndex == 0) ? ALLIANCE:HORDE); + _NodeOccupied(node,(teamIndex == BG_TEAM_ALLIANCE) ? ALLIANCE:HORDE); // FIXME: node names not localized - if (teamIndex == 0) + if (teamIndex == BG_TEAM_ALLIANCE) SendMessage2ToAll(LANG_BG_AB_NODE_DEFENDED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node)); else SendMessage2ToAll(LANG_BG_AB_NODE_DEFENDED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); } - sound = (teamIndex == 0) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; + sound = (teamIndex == BG_TEAM_ALLIANCE) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; } // If node is occupied, change to enemy-contested else @@ -516,19 +528,19 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; // FIXME: node names not localized - if (teamIndex == 0) + if (teamIndex == BG_TEAM_ALLIANCE) SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node)); else SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); - sound = (teamIndex == 0) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; + sound = (teamIndex == BG_TEAM_ALLIANCE) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; } // If node is occupied again, send "X has taken the Y" msg. if (m_Nodes[node] >= BG_AB_NODE_TYPE_OCCUPIED) { // FIXME: team and node names not localized - if (teamIndex == 0) + if (teamIndex == BG_TEAM_ALLIANCE) SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_ALLIANCE, NULL, LANG_BG_AB_ALLY, _GetNodeNameId(node)); else SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_HORDE, NULL, LANG_BG_AB_HORDE, _GetNodeNameId(node)); @@ -591,6 +603,8 @@ void BattleGroundAB::Reset() bool isBGWeekend = false; //TODO FIXME - call sBattleGroundMgr.IsBGWeekend(m_TypeID); - you must also implement that call! m_HonorTics = (isBGWeekend) ? BG_AB_ABBGWeekendHonorTicks : BG_AB_NotABBGWeekendHonorTicks; m_ReputationTics = (isBGWeekend) ? BG_AB_ABBGWeekendReputationTicks : BG_AB_NotABBGWeekendReputationTicks; + m_TeamScores500disadvantage[BG_TEAM_ALLIANCE] = false; + m_TeamScores500disadvantage[BG_TEAM_HORDE] = false; for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) { @@ -621,7 +635,7 @@ void BattleGroundAB::EndBattleGround(uint32 winner) WorldSafeLocsEntry const* BattleGroundAB::GetClosestGraveYard(Player* player) { - uint8 teamIndex = GetTeamIndexByTeamId(player->GetTeam()); + BattleGroundTeamId teamIndex = GetTeamIndexByTeamId(player->GetTeam()); // Is there any occupied node for this team? std::vector<uint8> nodes; @@ -660,8 +674,7 @@ WorldSafeLocsEntry const* BattleGroundAB::GetClosestGraveYard(Player* player) void BattleGroundAB::UpdatePlayerScore(Player *Source, uint32 type, uint32 value) { - std::map<uint64, BattleGroundScore*>::iterator itr = m_PlayerScores.find(Source->GetGUID()); - + BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); if( itr == m_PlayerScores.end() ) // player not found... return; @@ -679,3 +692,13 @@ void BattleGroundAB::UpdatePlayerScore(Player *Source, uint32 type, uint32 value } } +bool BattleGroundAB::IsAllNodesConrolledByTeam(uint32 team) const +{ + uint32 count = 0; + for(int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + if (team == ALLIANCE && m_Nodes[i] == BG_AB_NODE_STATUS_ALLY_OCCUPIED || + team == HORDE && m_Nodes[i] == BG_AB_NODE_STATUS_HORDE_OCCUPIED) + ++count; + + return count == BG_AB_DYNAMIC_NODES_COUNT; +} diff --git a/src/game/BattleGroundAB.h b/src/game/BattleGroundAB.h index 1c4a6d3b738..1a940a041d4 100644 --- a/src/game/BattleGroundAB.h +++ b/src/game/BattleGroundAB.h @@ -262,6 +262,9 @@ class BattleGroundAB : public BattleGround /* Nodes occupying */ virtual void EventPlayerClickedOnFlag(Player *source, GameObject* target_obj); + /* achievement req. */ + bool IsAllNodesConrolledByTeam(uint32 team) const; // overwrited + bool IsTeamScores500disadvantage(uint32 team) const { return m_TeamScores500disadvantage[GetTeamIndexByTeamId(team)]; } private: /* Gameobject spawning/despawning */ void _CreateBanner(uint8 node, uint8 type, uint8 teamIndex, bool delay); @@ -285,15 +288,14 @@ class BattleGroundAB : public BattleGround uint8 m_prevNodes[BG_AB_DYNAMIC_NODES_COUNT]; BG_AB_BannerTimer m_BannerTimers[BG_AB_DYNAMIC_NODES_COUNT]; uint32 m_NodeTimers[BG_AB_DYNAMIC_NODES_COUNT]; - uint32 m_TeamScores[BG_TEAMS_COUNT]; uint32 m_lastTick[BG_TEAMS_COUNT]; uint32 m_HonorScoreTics[BG_TEAMS_COUNT]; uint32 m_ReputationScoreTics[BG_TEAMS_COUNT]; bool m_IsInformedNearVictory; uint32 m_HonorTics; uint32 m_ReputationTics; - - + // need for achievements + bool m_TeamScores500disadvantage[BG_TEAMS_COUNT]; }; #endif diff --git a/src/game/BattleGroundAV.cpp b/src/game/BattleGroundAV.cpp index a93b304e2b9..4778bdb2384 100644 --- a/src/game/BattleGroundAV.cpp +++ b/src/game/BattleGroundAV.cpp @@ -34,11 +34,10 @@ BattleGroundAV::BattleGroundAV() m_BgObjects.resize(BG_AV_OBJECT_MAX); m_BgCreatures.resize(AV_CPLACE_MAX+AV_STATICCPLACE_MAX); - //TODO FIX ME! - m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_EY_START_TWO_MINUTES; - m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_EY_START_ONE_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_EY_START_HALF_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_EY_HAS_BEGUN; + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_AV_START_TWO_MINUTES; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_AV_START_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_AV_START_HALF_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_AV_HAS_BEGUN; } BattleGroundAV::~BattleGroundAV() @@ -300,117 +299,8 @@ Creature* BattleGroundAV::AddAVCreature(uint16 cinfoid, uint16 type ) void BattleGroundAV::Update(uint32 diff) { BattleGround::Update(diff); - if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize()) - { - ModifyStartDelayTime(diff); - - if (!(m_Events & 0x01)) - { - m_Events |= 0x01; - - if(!SetupBattleGround()) - { - EndNow(); - return; - } - uint16 i; - sLog.outDebug("Alterac Valley: entering state STATUS_WAIT_JOIN ..."); - // Initial Nodes - for(i = 0; i < BG_AV_OBJECT_MAX; i++) - SpawnBGObject(i, RESPAWN_ONE_DAY); - for(i = BG_AV_OBJECT_FLAG_A_FIRSTAID_STATION; i <= BG_AV_OBJECT_FLAG_A_STONEHEART_GRAVE ; i++){ - SpawnBGObject(BG_AV_OBJECT_AURA_A_FIRSTAID_STATION+3*i,RESPAWN_IMMEDIATELY); - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - } - for(i = BG_AV_OBJECT_FLAG_A_DUNBALDAR_SOUTH; i <= BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER ; i++) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - for(i = BG_AV_OBJECT_FLAG_H_ICEBLOOD_GRAVE; i <= BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER ; i++){ - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - if(i<=BG_AV_OBJECT_FLAG_H_FROSTWOLF_HUT) - SpawnBGObject(BG_AV_OBJECT_AURA_H_FIRSTAID_STATION+3*GetNodeThroughObject(i),RESPAWN_IMMEDIATELY); - } - for(i = BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH; i <= BG_AV_OBJECT_TFLAG_A_STONEHEART_BUNKER; i+=2) - { - SpawnBGObject(i, RESPAWN_IMMEDIATELY); //flag - SpawnBGObject(i+16, RESPAWN_IMMEDIATELY); //aura - } - for(i = BG_AV_OBJECT_TFLAG_H_ICEBLOOD_TOWER; i <= BG_AV_OBJECT_TFLAG_H_FROSTWOLF_WTOWER; i+=2) - { - SpawnBGObject(i, RESPAWN_IMMEDIATELY); //flag - SpawnBGObject(i+16, RESPAWN_IMMEDIATELY); //aura - } - //snowfall and the doors - for(i = BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE; i <= BG_AV_OBJECT_DOOR_A; i++) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - SpawnBGObject(BG_AV_OBJECT_AURA_N_SNOWFALL_GRAVE,RESPAWN_IMMEDIATELY); - - //creatures - sLog.outDebug("BG_AV start poputlating nodes"); - for(BG_AV_Nodes i= BG_AV_NODES_FIRSTAID_STATION; i < BG_AV_NODES_MAX; ++i ) - { - if(m_Nodes[i].Owner) - PopulateNode(i); - } - //all creatures which don't get despawned through the script are static - sLog.outDebug("BG_AV: start spawning static creatures"); - for(i=0; i < AV_STATICCPLACE_MAX; i++ ) - AddAVCreature(0,i+AV_CPLACE_MAX); - //mainspiritguides: - sLog.outDebug("BG_AV: start spawning spiritguides creatures"); - AddSpiritGuide(7, BG_AV_CreaturePos[7][0], BG_AV_CreaturePos[7][1], BG_AV_CreaturePos[7][2], BG_AV_CreaturePos[7][3], ALLIANCE); - AddSpiritGuide(8, BG_AV_CreaturePos[8][0], BG_AV_CreaturePos[8][1], BG_AV_CreaturePos[8][2], BG_AV_CreaturePos[8][3], HORDE); - //spawn the marshals (those who get deleted, if a tower gets destroyed) - sLog.outDebug("BG_AV: start spawning marshal creatures"); - for(i=AV_NPC_A_MARSHAL_SOUTH; i<= AV_NPC_H_MARSHAL_WTOWER; i++) - AddAVCreature(i,AV_CPLACE_A_MARSHAL_SOUTH+(i-AV_NPC_A_MARSHAL_SOUTH)); - - AddAVCreature(AV_NPC_HERALD,AV_CPLACE_HERALD); - DoorClose(BG_AV_OBJECT_DOOR_A); - DoorClose(BG_AV_OBJECT_DOOR_H); - - SetStartDelayTime(BG_START_DELAY_2M); - } - // After 1 minute, warning is signalled - else if (GetStartDelayTime() <= BG_START_DELAY_1M && !(m_Events & 0x04)) - { - m_Events |= 0x04; - SendMessageToAll(LANG_BG_AV_ONEMINTOSTART, CHAT_MSG_BG_SYSTEM_NEUTRAL); - } - // After 1,5 minute, warning is signalled - else if (GetStartDelayTime() <= BG_START_DELAY_1M + BG_START_DELAY_30S && !(m_Events & 0x08)) - { - m_Events |= 0x08; - SendMessageToAll(LANG_BG_AV_HALFMINTOSTART, CHAT_MSG_BG_SYSTEM_NEUTRAL); - } - // After 2 minutes, gates OPEN ! x) - else if (GetStartDelayTime() <= 0 && !(m_Events & 0x10)) - { - UpdateWorldState(AV_SHOW_H_SCORE, 1); - UpdateWorldState(AV_SHOW_A_SCORE, 1); - m_Events |= 0x10; - - SendMessageToAll(LANG_BG_AV_STARTED, CHAT_MSG_BG_SYSTEM_NEUTRAL); - PlaySoundToAll(SOUND_BG_START); - SetStatus(STATUS_IN_PROGRESS); - - sLog.outDebug("BG_AV: start spawning mine stuff"); - for(uint16 i= BG_AV_OBJECT_MINE_SUPPLY_N_MIN; i<=BG_AV_OBJECT_MINE_SUPPLY_N_MAX;i++) - SpawnBGObject(i,RESPAWN_IMMEDIATELY); - for(uint16 i= BG_AV_OBJECT_MINE_SUPPLY_S_MIN; i<=BG_AV_OBJECT_MINE_SUPPLY_S_MAX;i++) - SpawnBGObject(i,RESPAWN_IMMEDIATELY); - for(uint8 mine = AV_NORTH_MINE; mine <= AV_SOUTH_MINE; mine++) //mine population - ChangeMineOwner(mine, AV_NEUTRAL_TEAM,true); - DoorOpen(BG_AV_OBJECT_DOOR_H); - DoorOpen(BG_AV_OBJECT_DOOR_A); - - - for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) - if(Player* plr = objmgr.GetPlayer(itr->first)) - plr->RemoveAurasDueToSpell(SPELL_PREPARATION); - } - } - else if(GetStatus() == STATUS_IN_PROGRESS) + if(GetStatus() == STATUS_IN_PROGRESS) { for(uint8 i=0; i<=1;i++)//0=alliance, 1=horde { @@ -470,10 +360,25 @@ void BattleGroundAV::Update(uint32 diff) void BattleGroundAV::StartingEventCloseDoors() { + DoorClose(BG_AV_OBJECT_DOOR_A); + DoorClose(BG_AV_OBJECT_DOOR_H); } void BattleGroundAV::StartingEventOpenDoors() { + sLog.outDebug("BG_AV: start spawning mine stuff"); + for(uint16 i= BG_AV_OBJECT_MINE_SUPPLY_N_MIN; i<=BG_AV_OBJECT_MINE_SUPPLY_N_MAX;i++) + SpawnBGObject(i,RESPAWN_IMMEDIATELY); + for(uint16 i= BG_AV_OBJECT_MINE_SUPPLY_S_MIN; i<=BG_AV_OBJECT_MINE_SUPPLY_S_MAX;i++) + SpawnBGObject(i,RESPAWN_IMMEDIATELY); + for(uint8 mine = AV_NORTH_MINE; mine <= AV_SOUTH_MINE; mine++) //mine population + ChangeMineOwner(mine, AV_NEUTRAL_TEAM,true); + + UpdateWorldState(AV_SHOW_H_SCORE, 1); + UpdateWorldState(AV_SHOW_A_SCORE, 1); + + DoorOpen(BG_AV_OBJECT_DOOR_H); + DoorOpen(BG_AV_OBJECT_DOOR_A); } void BattleGroundAV::AddPlayer(Player *plr) @@ -584,8 +489,7 @@ void BattleGroundAV::HandleAreaTrigger(Player *Source, uint32 Trigger) void BattleGroundAV::UpdatePlayerScore(Player* Source, uint32 type, uint32 value) { - std::map<uint64, BattleGroundScore*>::iterator itr = m_PlayerScores.find(Source->GetGUID()); - + BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); if(itr == m_PlayerScores.end()) // player not found... return; @@ -1234,7 +1138,7 @@ bool BattleGroundAV::SetupBattleGround() return false; } -//spawn node-objects + //spawn node-objects for (uint8 i = BG_AV_NODES_FIRSTAID_STATION ; i < BG_AV_NODES_MAX; ++i) { if( i <= BG_AV_NODES_FROSTWOLF_HUT ) @@ -1345,6 +1249,58 @@ bool BattleGroundAV::SetupBattleGround() return false; } } + + uint16 i; + sLog.outDebug("Alterac Valley: entering state STATUS_WAIT_JOIN ..."); + // Initial Nodes + for(i = 0; i < BG_AV_OBJECT_MAX; i++) + SpawnBGObject(i, RESPAWN_ONE_DAY); + for(i = BG_AV_OBJECT_FLAG_A_FIRSTAID_STATION; i <= BG_AV_OBJECT_FLAG_A_STONEHEART_GRAVE ; i++){ + SpawnBGObject(BG_AV_OBJECT_AURA_A_FIRSTAID_STATION+3*i,RESPAWN_IMMEDIATELY); + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + } + for(i = BG_AV_OBJECT_FLAG_A_DUNBALDAR_SOUTH; i <= BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER ; i++) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + for(i = BG_AV_OBJECT_FLAG_H_ICEBLOOD_GRAVE; i <= BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER ; i++){ + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + if(i<=BG_AV_OBJECT_FLAG_H_FROSTWOLF_HUT) + SpawnBGObject(BG_AV_OBJECT_AURA_H_FIRSTAID_STATION+3*GetNodeThroughObject(i),RESPAWN_IMMEDIATELY); + } + for(i = BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH; i <= BG_AV_OBJECT_TFLAG_A_STONEHEART_BUNKER; i+=2) + { + SpawnBGObject(i, RESPAWN_IMMEDIATELY); //flag + SpawnBGObject(i+16, RESPAWN_IMMEDIATELY); //aura + } + for(i = BG_AV_OBJECT_TFLAG_H_ICEBLOOD_TOWER; i <= BG_AV_OBJECT_TFLAG_H_FROSTWOLF_WTOWER; i+=2) + { + SpawnBGObject(i, RESPAWN_IMMEDIATELY); //flag + SpawnBGObject(i+16, RESPAWN_IMMEDIATELY); //aura + } + //snowfall and the doors + for(i = BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE; i <= BG_AV_OBJECT_DOOR_A; i++) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + SpawnBGObject(BG_AV_OBJECT_AURA_N_SNOWFALL_GRAVE,RESPAWN_IMMEDIATELY); + + //creatures + sLog.outDebug("BG_AV start poputlating nodes"); + for(BG_AV_Nodes i= BG_AV_NODES_FIRSTAID_STATION; i < BG_AV_NODES_MAX; ++i ) + { + if(m_Nodes[i].Owner) + PopulateNode(i); + } + //all creatures which don't get despawned through the script are static + sLog.outDebug("BG_AV: start spawning static creatures"); + for(i=0; i < AV_STATICCPLACE_MAX; i++ ) + AddAVCreature(0,i+AV_CPLACE_MAX); + //mainspiritguides: + sLog.outDebug("BG_AV: start spawning spiritguides creatures"); + AddSpiritGuide(7, BG_AV_CreaturePos[7][0], BG_AV_CreaturePos[7][1], BG_AV_CreaturePos[7][2], BG_AV_CreaturePos[7][3], ALLIANCE); + AddSpiritGuide(8, BG_AV_CreaturePos[8][0], BG_AV_CreaturePos[8][1], BG_AV_CreaturePos[8][2], BG_AV_CreaturePos[8][3], HORDE); + //spawn the marshals (those who get deleted, if a tower gets destroyed) + sLog.outDebug("BG_AV: start spawning marshal creatures"); + for(i=AV_NPC_A_MARSHAL_SOUTH; i<= AV_NPC_H_MARSHAL_WTOWER; i++) + AddAVCreature(i,AV_CPLACE_A_MARSHAL_SOUTH+(i-AV_NPC_A_MARSHAL_SOUTH)); + AddAVCreature(AV_NPC_HERALD,AV_CPLACE_HERALD); return true; } @@ -1378,10 +1334,26 @@ const char* BattleGroundAV::GetNodeName(BG_AV_Nodes node) void BattleGroundAV::AssaultNode(BG_AV_Nodes node, uint16 team) { - assert(m_Nodes[node].TotalOwner != team); - assert(m_Nodes[node].Owner != team); - assert(m_Nodes[node].State != POINT_DESTROYED); - assert(m_Nodes[node].State != POINT_ASSAULTED || !m_Nodes[node].TotalOwner ); //only assault an assaulted node if no totalowner exists + if (m_Nodes[node].TotalOwner == team) + { + sLog.outCrash("Assaulting team is TotalOwner of node"); + assert (false); + } + if (m_Nodes[node].Owner == team) + { + sLog.outCrash("Assaulting team is owner of node"); + assert (false); + } + if (m_Nodes[node].State == POINT_DESTROYED) + { + sLog.outCrash("Destroyed node is being assaulted"); + assert (false); + } + if (m_Nodes[node].State == POINT_ASSAULTED && m_Nodes[node].TotalOwner) //only assault an assaulted node if no totalowner exists + { + sLog.outCrash("Assault on an not assaulted node with total owner"); + assert (false); + } //the timer gets another time, if the previous owner was 0==Neutral m_Nodes[node].Timer = (m_Nodes[node].PrevOwner)? BG_AV_CAPTIME : BG_AV_SNOWFALL_FIRSTCAP; m_Nodes[node].PrevOwner = m_Nodes[node].Owner; diff --git a/src/game/BattleGroundBE.cpp b/src/game/BattleGroundBE.cpp index d58c2250948..05475328b01 100644 --- a/src/game/BattleGroundBE.cpp +++ b/src/game/BattleGroundBE.cpp @@ -182,8 +182,7 @@ bool BattleGroundBE::SetupBattleGround() void BattleGroundBE::UpdatePlayerScore(Player* Source, uint32 type, uint32 value) { - std::map<uint64, BattleGroundScore*>::iterator itr = m_PlayerScores.find(Source->GetGUID()); - + BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); if(itr == m_PlayerScores.end()) // player not found... return; diff --git a/src/game/BattleGroundEY.cpp b/src/game/BattleGroundEY.cpp index 9b54ecce643..78654677230 100644 --- a/src/game/BattleGroundEY.cpp +++ b/src/game/BattleGroundEY.cpp @@ -128,7 +128,7 @@ void BattleGroundEY::StartingEventOpenDoors() void BattleGroundEY::AddPoints(uint32 Team, uint32 Points) { - uint8 team_index = GetTeamIndexByTeamId(Team); + BattleGroundTeamId team_index = GetTeamIndexByTeamId(Team); m_TeamScores[team_index] += Points; m_HonorScoreTics[team_index] += Points; if (m_HonorScoreTics[team_index] >= m_HonorTics ) @@ -788,8 +788,7 @@ void BattleGroundEY::EventPlayerCapturedFlag(Player *Source, uint32 BgObjectType void BattleGroundEY::UpdatePlayerScore(Player *Source, uint32 type, uint32 value) { - std::map<uint64, BattleGroundScore*>::iterator itr = m_PlayerScores.find(Source->GetGUID()); - + BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); if(itr == m_PlayerScores.end()) // player not found return; @@ -908,3 +907,12 @@ WorldSafeLocsEntry const *BattleGroundEY::GetClosestGraveYard(Player* player) return nearestEntry; } +bool BattleGroundEY::IsAllNodesConrolledByTeam(uint32 team) const +{ + uint32 count = 0; + for(int i = 0; i < EY_POINTS_MAX; ++i) + if (m_PointOwnedByTeam[i] == team && m_PointState[i] == EY_POINT_UNDER_CONTROL) + ++count; + + return count == EY_POINTS_MAX; +} diff --git a/src/game/BattleGroundEY.h b/src/game/BattleGroundEY.h index a22d6ecc6db..3385a06bfb0 100644 --- a/src/game/BattleGroundEY.h +++ b/src/game/BattleGroundEY.h @@ -350,6 +350,8 @@ class BattleGroundEY : public BattleGround virtual void EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj); virtual void EventPlayerDroppedFlag(Player *Source); + /* achievement req. */ + bool IsAllNodesConrolledByTeam(uint32 team) const; private: void EventPlayerCapturedFlag(Player *Source, uint32 BgObjectType); void EventTeamCapturedPoint(Player *Source, uint32 Point); @@ -369,7 +371,6 @@ class BattleGroundEY : public BattleGround void RemovePoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] -= Points; } void SetTeamPoint(uint32 TeamID, uint32 Points = 0) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] = Points; } - uint32 m_TeamScores[2]; uint32 m_HonorScoreTics[2]; uint32 m_TeamPointsCount[2]; diff --git a/src/game/BattleGroundHandler.cpp b/src/game/BattleGroundHandler.cpp index c8bc2bc4ecf..497db2ceadd 100644 --- a/src/game/BattleGroundHandler.cpp +++ b/src/game/BattleGroundHandler.cpp @@ -448,7 +448,8 @@ void WorldSession::HandleBattleFieldPortOpcode( WorldPacket &recv_data ) if (_player->isInFlight()) { _player->GetMotionMaster()->MovementExpired(); - _player->m_taxi.ClearTaxiDestinations(); + _player->CleanupAfterTaxiFlight(); + } sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime(), bg->GetArenaType()); diff --git a/src/game/BattleGroundMgr.cpp b/src/game/BattleGroundMgr.cpp index 371c204717d..d70aa52a018 100644 --- a/src/game/BattleGroundMgr.cpp +++ b/src/game/BattleGroundMgr.cpp @@ -1349,7 +1349,7 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) *data << (int32)(bg->GetPlayerScoresSize()); - for(std::map<uint64, BattleGroundScore*>::const_iterator itr = bg->GetPlayerScoresBegin(); itr != bg->GetPlayerScoresEnd(); ++itr) + for(BattleGround::BattleGroundScoreMap::const_iterator itr = bg->GetPlayerScoresBegin(); itr != bg->GetPlayerScoresEnd(); ++itr) { *data << (uint64)itr->first; *data << (int32)itr->second->KillingBlows; @@ -1474,6 +1474,8 @@ BattleGround * BattleGroundMgr::GetBattleGroundThroughClientInstance(uint32 inst BattleGround * BattleGroundMgr::GetBattleGround(uint32 InstanceID, BattleGroundTypeId bgTypeId) { + if (!InstanceID) + return NULL; //search if needed BattleGroundSet::iterator itr; if (bgTypeId == BATTLEGROUND_TYPE_NONE) diff --git a/src/game/BattleGroundSA.cpp b/src/game/BattleGroundSA.cpp index 5f20950070d..acf16ad3af0 100644 --- a/src/game/BattleGroundSA.cpp +++ b/src/game/BattleGroundSA.cpp @@ -72,8 +72,7 @@ void BattleGroundSA::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/) void BattleGroundSA::UpdatePlayerScore(Player* Source, uint32 type, uint32 value) { - std::map<uint64, BattleGroundScore*>::iterator itr = m_PlayerScores.find(Source->GetGUID()); - + BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); if(itr == m_PlayerScores.end()) // player not found... return; diff --git a/src/game/BattleGroundWS.cpp b/src/game/BattleGroundWS.cpp index c4a2402ad67..81080284b9d 100644 --- a/src/game/BattleGroundWS.cpp +++ b/src/game/BattleGroundWS.cpp @@ -719,8 +719,7 @@ void BattleGroundWS::HandleKillPlayer(Player *player, Player *killer) void BattleGroundWS::UpdatePlayerScore(Player *Source, uint32 type, uint32 value) { - std::map<uint64, BattleGroundScore*>::iterator itr = m_PlayerScores.find(Source->GetGUID()); - + BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); if(itr == m_PlayerScores.end()) // player not found return; diff --git a/src/game/BattleGroundWS.h b/src/game/BattleGroundWS.h index 7395c3a5bb9..16631afecdc 100644 --- a/src/game/BattleGroundWS.h +++ b/src/game/BattleGroundWS.h @@ -200,12 +200,10 @@ class BattleGroundWS : public BattleGround void AddPoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] += Points; } void SetTeamPoint(uint32 TeamID, uint32 Points = 0) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] = Points; } void RemovePoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] -= Points; } - private: uint64 m_FlagKeepers[2]; // 0 - alliance, 1 - horde uint64 m_DroppedFlagGUID[2]; uint8 m_FlagState[2]; // for checking flag state - uint32 m_TeamScores[2]; int32 m_FlagsTimer[2]; int32 m_FlagsDropTimer[2]; diff --git a/src/game/CMakeLists.txt b/src/game/CMakeLists.txt index bb05b4d6076..7132d89d983 100644 --- a/src/game/CMakeLists.txt +++ b/src/game/CMakeLists.txt @@ -56,8 +56,9 @@ SET(game_STAT_SRCS Channel.cpp Channel.h ChannelHandler.cpp - ChannelMgr.h CharacterHandler.cpp + ChannelMgr.h + ChannelMgr.cpp Chat.cpp Chat.h ChatHandler.cpp diff --git a/src/game/Channel.cpp b/src/game/Channel.cpp index 9bb3463dbdb..ae7e4d17352 100644 --- a/src/game/Channel.cpp +++ b/src/game/Channel.cpp @@ -23,9 +23,10 @@ #include "SocialMgr.h" #include "World.h" -Channel::Channel(const std::string& name, uint32 channel_id) -: m_announce(true), m_moderate(false), m_name(name), m_flags(0), m_channelId(channel_id), m_ownerGUID(0) +Channel::Channel(const std::string& name, uint32 channel_id, uint32 Team) + : m_name(name), m_announce(true), m_moderate(false), m_channelId(channel_id), m_ownerGUID(0), m_password(""), m_flags(0) { + m_Team = Team; // set special flags if built-in channel ChatChannelsEntry const* ch = GetChannelEntryFor(channel_id); if(ch) // it's built-in channel @@ -45,10 +46,54 @@ Channel::Channel(const std::string& name, uint32 channel_id) m_flags |= CHANNEL_FLAG_LFG; else // for all other channels m_flags |= CHANNEL_FLAG_NOT_LFG; + m_IsSaved = false; } else // it's custom channel { m_flags |= CHANNEL_FLAG_CUSTOM; + //load not built in channel if saved + QueryResult *result = CharacterDatabase.PQuery("SELECT m_name, m_team, m_ownerGUID, m_announce, m_moderate, m_password, BannedList FROM channels WHERE m_name = '%s' AND m_team = '%u'", name.c_str(), m_Team); + if (result)//load + { + Field *fields = result->Fetch(); + const char* db_name = fields[0].GetString(); + uint32 db_team = fields[1].GetUInt32(); + uint64 db_owner = fields[2].GetUInt64(); + bool db_announce = fields[3].GetBool(); + bool db_moderate = fields[4].GetBool(); + const char* db_password = fields[5].GetString(); + const char* db_BannedList = fields[6].GetString(); + + m_ownerGUID = db_owner; + m_announce = db_announce; + m_moderate = db_moderate; + m_password = db_password; + m_IsSaved = true; + + if(db_BannedList) + { + Tokens tokens = StrSplit(db_BannedList, " "); + Tokens::iterator iter; + for (iter = tokens.begin();iter != tokens.end(); ++iter) + { + uint64 banned_guid = atol((*iter).c_str()); + if(banned_guid) + { + sLog.outDebug("Channel(%s) loaded banned guid: %u",name.c_str(), banned_guid); + banned.insert(banned_guid); + } + } + } + }else{//save + std::ostringstream ss; + ss << "INSERT INTO channels (m_name,m_team,m_ownerGUID,m_announce,m_moderate,m_password) VALUES ('" + << name.c_str() << "','" << m_Team << "','0','1','0','')"; + if(CharacterDatabase.PExecute( ss.str( ).c_str( ) )) + { + sLog.outDebug("New Channel(%s) saved", name.c_str()); + m_IsSaved = true; + } + } } } @@ -120,6 +165,10 @@ void Channel::Join(uint64 p, const char *pass) if(!IsConstant() && !m_ownerGUID) { SetOwner(p, (players.size() > 1 ? true : false)); + players[p].SetModerator(true); + }else if(!IsConstant() && m_ownerGUID && plr && m_ownerGUID == plr->GetGUID()) + { + SetOwner(p, (players.size() > 1 ? true : false)); players[p].SetModerator(true); } } @@ -161,7 +210,7 @@ void Channel::Leave(uint64 p, bool send) LeaveNotify(p); - if(changeowner) + if(changeowner && !m_IsSaved) { uint64 newowner = !players.empty() ? players.begin()->second.player : 0; SetOwner(newowner); @@ -213,6 +262,23 @@ void Channel::KickOrBan(uint64 good, const char *badname, bool ban) { banned.insert(bad->GetGUID()); MakePlayerBanned(&data, bad->GetGUID(), good); + //save banlist + if(m_IsSaved) + { + std::ostringstream banlist; + BannedList::iterator iter; + for (iter = banned.begin();iter != banned.end(); ++iter) + { + banlist << (*iter) << " "; + } + std::ostringstream ss; + ss << "UPDATE channels SET BannedList = '" << banlist.str().c_str() << "' WHERE m_name = '"<<m_name.c_str()<<"' AND m_team = '"<<m_Team<<"'"; + if(CharacterDatabase.PExecute( ss.str( ).c_str( ) )) + { + sLog.outDebug("Channel(%s) BannedList saved", m_name.c_str()); + } + } + } else MakePlayerKicked(&data, bad->GetGUID(), good); @@ -265,6 +331,22 @@ void Channel::UnBan(uint64 good, const char *badname) WorldPacket data; MakePlayerUnbanned(&data, bad->GetGUID(), good); SendToAll(&data); + //save banlist + if(m_IsSaved) + { + std::ostringstream banlist; + BannedList::iterator iter; + for (iter = banned.begin();iter != banned.end(); ++iter) + { + banlist << (*iter) << " "; + } + std::ostringstream ss; + ss << "UPDATE channels SET BannedList = '" << banlist.str().c_str() << "' WHERE m_name = '"<<m_name.c_str()<<"' AND m_team = '"<<m_Team<<"'"; + if(CharacterDatabase.PExecute( ss.str( ).c_str( ) )) + { + sLog.outDebug("Channel(%s) BannedList saved", m_name.c_str()); + } + } } } } @@ -295,6 +377,15 @@ void Channel::Password(uint64 p, const char *pass) WorldPacket data; MakePasswordChanged(&data, p); SendToAll(&data); + if(m_IsSaved) + { + std::ostringstream ss; + ss << "UPDATE channels SET m_password = '" << pass << "' WHERE m_name = '"<<m_name.c_str()<<"' AND m_team = '"<<m_Team<<"'"; + if(CharacterDatabase.PExecute( ss.str( ).c_str( ) )) + { + sLog.outDebug("Channel(%s) password saved", m_name.c_str()); + } + } } } @@ -448,7 +539,7 @@ void Channel::List(Player* player) size_t pos = data.wpos(); data << uint32(0); // size of list, placeholder - bool gmInWhoList = sWorld.getConfig(CONFIG_GM_IN_WHO_LIST) || player->GetSession()->GetSecurity() > SEC_PLAYER; + uint32 gmLevelInWhoList = sWorld.getConfig(CONFIG_GM_LEVEL_IN_WHO_LIST); uint32 count = 0; for(PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) @@ -457,7 +548,8 @@ void Channel::List(Player* player) // PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters // MODERATOR, GAME MASTER, ADMINISTRATOR can see all - if (plr && ( plr->GetSession()->GetSecurity() == SEC_PLAYER || (gmInWhoList && plr->IsVisibleGloballyFor(player)))) + if (plr && (player->GetSession()->GetSecurity() > SEC_PLAYER || plr->GetSession()->GetSecurity() <= gmLevelInWhoList) && + plr->IsVisibleGloballyFor(player)) { data << uint64(i->first); data << uint8(i->second.flags); // flags seems to be changed... @@ -500,6 +592,16 @@ void Channel::Announce(uint64 p) else MakeAnnouncementsOff(&data, p); SendToAll(&data); + if(m_IsSaved) + { + std::ostringstream ss; + ss << "UPDATE channels SET m_announce = '" << m_announce << "' WHERE m_name = '"<<m_name.c_str()<<"' AND m_team = '"<<m_Team<<"'"; + if(CharacterDatabase.PExecute( ss.str( ).c_str( ) )) + { + sLog.outDebug("Channel(%s) announce saved", m_name.c_str()); + } + } + } } @@ -532,6 +634,15 @@ void Channel::Moderate(uint64 p) else MakeModerationOff(&data, p); SendToAll(&data); + if(m_IsSaved) + { + std::ostringstream ss; + ss << "UPDATE channels SET m_moderate = '" << m_moderate << "' WHERE m_name = '"<<m_name.c_str()<<"' AND m_team = '"<<m_Team<<"'"; + if(CharacterDatabase.PExecute( ss.str( ).c_str( ) )) + { + sLog.outDebug("Channel(%s) moderate saved", m_name.c_str()); + } + } } } @@ -659,6 +770,16 @@ void Channel::SetOwner(uint64 guid, bool exclaim) MakeOwnerChanged(&data, m_ownerGUID); SendToAll(&data); } + if(m_IsSaved) + { + std::ostringstream ss; + ss << "UPDATE channels SET m_ownerGUID = '" << guid << "' WHERE m_name = '"<<m_name.c_str()<<"' AND m_team = '"<<m_Team<<"'"; + if(CharacterDatabase.PExecute( ss.str( ).c_str( ) )) + { + sLog.outDebug("Channel(%s) owner saved", m_name.c_str()); + } + } + } } diff --git a/src/game/Channel.h b/src/game/Channel.h index c2dd5ecd3ac..69a1e2f66f6 100644 --- a/src/game/Channel.h +++ b/src/game/Channel.h @@ -158,6 +158,7 @@ class Channel uint8 m_flags; uint32 m_channelId; uint64 m_ownerGUID; + bool m_IsSaved; private: // initial packet data (notify type and channel name) @@ -243,7 +244,8 @@ class Channel } public: - Channel(const std::string& name, uint32 channel_id); + uint32 m_Team; + Channel(const std::string& name, uint32 channel_id, uint32 Team = 0); std::string GetName() const { return m_name; } uint32 GetChannelId() const { return m_channelId; } bool IsConstant() const { return m_channelId != 0; } diff --git a/src/game/ChannelHandler.cpp b/src/game/ChannelHandler.cpp index 45cdf8c20b2..e3c10afe81f 100644 --- a/src/game/ChannelHandler.cpp +++ b/src/game/ChannelHandler.cpp @@ -24,9 +24,6 @@ #include "ObjectMgr.h" // for normalizePlayerName #include "ChannelMgr.h" -INSTANTIATE_SINGLETON_1( AllianceChannelMgr ); -INSTANTIATE_SINGLETON_1( HordeChannelMgr ); - void WorldSession::HandleJoinChannel(WorldPacket& recvPacket) { sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); @@ -48,8 +45,11 @@ void WorldSession::HandleJoinChannel(WorldPacket& recvPacket) recvPacket >> pass; if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + { + cMgr->team = _player->GetTeam(); if(Channel *chn = cMgr->GetJoinChannel(channelname, channel_id)) chn->Join(_player->GetGUID(), pass.c_str()); + } } void WorldSession::HandleLeaveChannel(WorldPacket& recvPacket) diff --git a/src/game/ChannelMgr.cpp b/src/game/ChannelMgr.cpp new file mode 100644 index 00000000000..09d172155cc --- /dev/null +++ b/src/game/ChannelMgr.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ChannelMgr.h" +#include "Policies/SingletonImp.h" +#include "World.h" + +INSTANTIATE_SINGLETON_1( AllianceChannelMgr ); +INSTANTIATE_SINGLETON_1( HordeChannelMgr ); + +ChannelMgr* channelMgr(uint32 team) +{ + if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) + return &MaNGOS::Singleton<AllianceChannelMgr>::Instance(); // cross-faction + + if(team == ALLIANCE) + return &MaNGOS::Singleton<AllianceChannelMgr>::Instance(); + if(team == HORDE) + return &MaNGOS::Singleton<HordeChannelMgr>::Instance(); + + return NULL; +} + +ChannelMgr::~ChannelMgr() +{ + for(ChannelMap::iterator itr = channels.begin();itr!=channels.end(); ++itr) + delete itr->second; + + channels.clear(); +} + +Channel *ChannelMgr::GetJoinChannel(std::string name, uint32 channel_id) +{ + if (channels.find(name) == channels.end()) + { + Channel *nchan = new Channel(name,channel_id, team); + channels[name] = nchan; + } + + return channels[name]; +} + +Channel *ChannelMgr::GetChannel(std::string name, Player *p, bool pkt) +{ + ChannelMap::const_iterator i = channels.find(name); + + if(i == channels.end()) + { + if(pkt) + { + WorldPacket data; + MakeNotOnPacket(&data,name); + p->GetSession()->SendPacket(&data); + } + + return NULL; + } + else + return i->second; +} + +void ChannelMgr::LeftChannel(std::string name) +{ + ChannelMap::const_iterator i = channels.find(name); + + if(i == channels.end()) + return; + + Channel* channel = i->second; + + if(channel->GetNumPlayers() == 0 && !channel->IsConstant()) + { + channels.erase(name); + delete channel; + } +} + +void ChannelMgr::MakeNotOnPacket(WorldPacket *data, std::string name) +{ + data->Initialize(SMSG_CHANNEL_NOTIFY, (1+10)); // we guess size + (*data) << (uint8)0x05 << name; +} diff --git a/src/game/ChannelMgr.h b/src/game/ChannelMgr.h index aeecfbfa541..f65a8520648 100644 --- a/src/game/ChannelMgr.h +++ b/src/game/ChannelMgr.h @@ -17,8 +17,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef TRINITYCORE_CHANNELMGR_H -#define TRINITYCORE_CHANNELMGR_H +#ifndef MANGOSSERVER_CHANNELMGR_H +#define MANGOSSERVER_CHANNELMGR_H + +#include "Common.h" +#include "Channel.h" +#include "Policies/Singleton.h" #include <map> #include <string> @@ -31,75 +35,23 @@ class ChannelMgr { public: + uint32 team; typedef std::map<std::string,Channel *> ChannelMap; - ChannelMgr() {} - ~ChannelMgr() - { - for(ChannelMap::const_iterator itr = channels.begin();itr!=channels.end(); ++itr) - delete itr->second; - channels.clear(); - } - Channel *GetJoinChannel(const std::string& name, uint32 channel_id) - { - if (channels.find(name) == channels.end()) - { - Channel *nchan = new Channel(name,channel_id); - channels[name] = nchan; - } - return channels[name]; - } - Channel *GetChannel(const std::string& name, Player *p) - { - ChannelMap::const_iterator i = channels.find(name); - - if(i == channels.end()) - { - WorldPacket data; - MakeNotOnPacket(&data,name); - p->GetSession()->SendPacket(&data); - return NULL; - } - else - return i->second; - } - void LeftChannel(const std::string& name) - { - ChannelMap::const_iterator i = channels.find(name); + ChannelMgr() {team = 0;} + ~ChannelMgr(); - if(i == channels.end()) - return; - - Channel* channel = i->second; - - if(channel->GetNumPlayers() == 0 && !channel->IsConstant()) - { - channels.erase(name); - delete channel; - } - } + Channel *GetJoinChannel(std::string name, uint32 channel_id); + Channel *GetChannel(std::string name, Player *p, bool pkt = true); + void LeftChannel(std::string name); private: ChannelMap channels; - void MakeNotOnPacket(WorldPacket *data, const std::string& name) - { - data->Initialize(SMSG_CHANNEL_NOTIFY, (1+10)); // we guess size - (*data) << (uint8)0x05 << name; - } + void MakeNotOnPacket(WorldPacket *data, std::string name); }; class AllianceChannelMgr : public ChannelMgr {}; class HordeChannelMgr : public ChannelMgr {}; -inline ChannelMgr* channelMgr(uint32 team) -{ - if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) - //For Test,No Seprate Faction - return &Trinity::Singleton<AllianceChannelMgr>::Instance(); +ChannelMgr* channelMgr(uint32 team); - if(team==ALLIANCE) - return &Trinity::Singleton<AllianceChannelMgr>::Instance(); - if(team==HORDE) - return &Trinity::Singleton<HordeChannelMgr>::Instance(); - return NULL; -} #endif diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index 84b830651e7..056bcd816e0 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -64,7 +64,7 @@ bool LoginQueryHolder::Initialize() // NOTE: all fields in `characters` must be read to prevent lost character data at next save in case wrong DB structure. // !!! NOTE: including unused `zone`,`online` - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADFROM, "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,instance_id FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADFROM, "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,instance_id,bgteam,bgmap,bgx,bgy,bgz,bgo 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_mask,stackcount,amount0, amount1, amount2 ,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid)); @@ -73,7 +73,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,misc FROM character_action WHERE guid = '%u' ORDER BY button", 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_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)); @@ -123,7 +123,7 @@ class CharacterHandler void WorldSession::HandleCharEnum(QueryResult * result) { // keys can be non cleared if player open realm list and close it by 'cancel' - LoginDatabase.PExecute("UPDATE account SET v = '0', s = '0' WHERE id = '%u'", GetAccountId()); + loginDatabase.PExecute("UPDATE account SET v = '0', s = '0' WHERE id = '%u'", GetAccountId()); WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size @@ -133,21 +133,15 @@ void WorldSession::HandleCharEnum(QueryResult * result) if( result ) { - Player *plr = new Player(this); do { uint32 guidlow = (*result)[0].GetUInt32(); sLog.outDetail("Loading char guid %u from account %u.",guidlow,GetAccountId()); - - if(plr->MinimalLoadFromDB( result, guidlow )) - { - plr->BuildEnumData( result, &data ); + if(Player::BuildEnumData(result, &data)) ++num; - } } while( result->NextRow() ); - delete plr; delete result; } @@ -162,19 +156,23 @@ void WorldSession::HandleCharEnumOpcode( WorldPacket & /*recv_data*/ ) CharacterDatabase.AsyncPQuery(&chrHandler, &CharacterHandler::HandleCharEnumCallback, GetAccountId(), !sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) ? // ------- Query Without Declined Names -------- - // 0 1 2 3 4 5 6 7 8 - "SELECT characters.guid, characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, " - // 9 10 11 12 13 14 - "characters.at_login, characters.zone, character_pet.entry, character_pet.modelid, character_pet.level, guild_member.guildid " + // 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 + "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.data " "FROM characters LEFT JOIN character_pet ON characters.guid=character_pet.owner AND character_pet.slot='%u' " "LEFT JOIN guild_member ON characters.guid = guild_member.guid " "WHERE characters.account = '%u' ORDER BY characters.guid" : // --------- Query With Declined Names --------- - // 0 1 2 3 4 5 6 7 8 - "SELECT characters.guid, characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, " - // 9 10 11 12 13 14 15 - "characters.at_login, characters.zone, character_pet.entry, character_pet.modelid, character_pet.level, guild_member.guildid, character_declinedname.genitive " + // 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 " "FROM characters LEFT JOIN character_pet ON characters.guid = character_pet.owner AND character_pet.slot='%u' " "LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid " "LEFT JOIN guild_member ON characters.guid = guild_member.guid " @@ -251,38 +249,39 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) } // prevent character creating with invalid name - if(!normalizePlayerName(name)) + if (!normalizePlayerName(name)) { - data << (uint8)CHAR_NAME_INVALID_CHARACTER; + data << (uint8)CHAR_NAME_NO_NAME; SendPacket( &data ); sLog.outError("Account:[%d] but tried to Create character with empty [name] ",GetAccountId()); return; } // check name limitations - if(!ObjectMgr::IsValidName(name,true)) + uint8 res = ObjectMgr::CheckPlayerName(name,true); + if (res != CHAR_NAME_SUCCESS) { - data << (uint8)CHAR_NAME_INVALID_CHARACTER; + data << uint8(res); SendPacket( &data ); return; } - if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(name)) + if (GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(name)) { data << (uint8)CHAR_NAME_RESERVED; SendPacket( &data ); return; } - if(objmgr.GetPlayerGUIDByName(name)) + if (objmgr.GetPlayerGUIDByName(name)) { data << (uint8)CHAR_CREATE_NAME_IN_USE; SendPacket( &data ); return; } - QueryResult *resultacct = LoginDatabase.PQuery("SELECT SUM(numchars) FROM realmcharacters WHERE acctid = '%d'", GetAccountId()); - if ( resultacct ) + QueryResult *resultacct = loginDatabase.PQuery("SELECT SUM(numchars) FROM realmcharacters WHERE acctid = '%d'", GetAccountId()); + if (resultacct) { Field *fields=resultacct->Fetch(); uint32 acctcharcount = fields[0].GetUInt32(); @@ -340,7 +339,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) if(!AllowTwoSideAccounts || skipCinematics == 1 || class_ == CLASS_DEATH_KNIGHT) { - QueryResult *result2 = CharacterDatabase.PQuery("SELECT guid,race,class FROM characters WHERE account = '%u' %s", + QueryResult *result2 = CharacterDatabase.PQuery("SELECT level,race,class FROM characters WHERE account = '%u' %s", GetAccountId(), (skipCinematics == 1 || class_ == CLASS_DEATH_KNIGHT) ? "" : "LIMIT 1"); if(result2) { @@ -367,8 +366,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) if(!have_req_level_for_heroic) { - uint32 acc_guid = field[0].GetUInt32(); - uint32 acc_level = Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL,acc_guid); + uint32 acc_level = field[0].GetUInt32(); if(acc_level >= req_level_for_heroic) have_req_level_for_heroic = true; } @@ -422,8 +420,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) if(!have_req_level_for_heroic) { - uint32 acc_guid = field[0].GetUInt32(); - uint32 acc_level = Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL,acc_guid); + uint32 acc_level = field[0].GetUInt32(); if(acc_level >= req_level_for_heroic) have_req_level_for_heroic = true; } @@ -464,8 +461,8 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) pNewChar->SaveToDB(); charcount+=1; - LoginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", GetAccountId(), realmID); - LoginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charcount, GetAccountId(), realmID); + loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", GetAccountId(), realmID); + loginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charcount, GetAccountId(), realmID); delete pNewChar; // created only to call SaveToDB() @@ -739,7 +736,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) pCurrChar->SendInitialPacketsAfterAddToMap(); CharacterDatabase.PExecute("UPDATE characters SET online = 1 WHERE guid = '%u'", pCurrChar->GetGUIDLow()); - LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = '%u'", GetAccountId()); + loginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = '%u'", GetAccountId()); pCurrChar->SetInGameTime( getMSTime() ); // announce group about member online (must be after add to player list to receive announce to self) @@ -815,6 +812,10 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) SendDoFlight( mountDisplayId, path, startNode ); } + // reset for all pets before pet loading + if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS)) + Pet::resetTalentsForAllPetsOf(pCurrChar); + // Load pet if any (if player not alive and in taxi flight or another then pet will remember as temporary unsummoned) pCurrChar->LoadPet(); @@ -835,6 +836,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_TALENTS)) { pCurrChar->resetTalents(true); + pCurrChar->SendTalentsInfoData(false); // original talents send already in to SendInitialPacketsBeforeAddToMap, resend reset state SendNotification(LANG_RESET_TALENTS); } @@ -852,6 +854,9 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) sLog.outChar("Account: %d (IP: %s) Login Character:[%s] (guid:%u)", GetAccountId(),IP_str.c_str(),pCurrChar->GetName() ,pCurrChar->GetGUIDLow()); + if(!pCurrChar->IsStandState() && !pCurrChar->hasUnitState(UNIT_STAT_STUNNED)) + pCurrChar->SetStandState(UNIT_STAND_STATE_STAND); + m_playerLoading = false; delete holder; } @@ -985,7 +990,7 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data) recv_data >> newname; // prevent character rename to invalid name - if(!normalizePlayerName(newname)) + if (!normalizePlayerName(newname)) { WorldPacket data(SMSG_CHAR_RENAME, 1); data << uint8(CHAR_NAME_NO_NAME); @@ -993,16 +998,17 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data) return; } - if(!ObjectMgr::IsValidName(newname, true)) + uint8 res = ObjectMgr::CheckPlayerName(newname,true); + if (res != CHAR_NAME_SUCCESS) { WorldPacket data(SMSG_CHAR_RENAME, 1); - data << uint8(CHAR_NAME_INVALID_CHARACTER); + data << uint8(res); SendPacket( &data ); return; } // check name limitations - if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) + if (GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) { WorldPacket data(SMSG_CHAR_RENAME, 1); data << uint8(CHAR_NAME_RESERVED); @@ -1258,7 +1264,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data) } // prevent character rename to invalid name - if(!normalizePlayerName(newname)) + if (!normalizePlayerName(newname)) { WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); data << uint8(CHAR_NAME_NO_NAME); @@ -1266,16 +1272,17 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data) return; } - if(!ObjectMgr::IsValidName(newname,true)) + uint8 res = ObjectMgr::CheckPlayerName(newname,true); + if (res != CHAR_NAME_SUCCESS) { WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); - data << uint8(CHAR_NAME_INVALID_CHARACTER); + data << uint8(res); SendPacket( &data ); return; } // check name limitations - if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) + if (GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) { WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); data << uint8(CHAR_NAME_RESERVED); @@ -1284,9 +1291,9 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data) } // character with this name already exist - if(uint64 newguid = objmgr.GetPlayerGUIDByName(newname)) + if (uint64 newguid = objmgr.GetPlayerGUIDByName(newname)) { - if(newguid != guid) + if (newguid != guid) { WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); data << uint8(CHAR_CREATE_NAME_IN_USE); diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index d1e85f6baa0..06822627460 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -154,19 +154,21 @@ ChatCommand * ChatHandler::getCommandTable() { "anim", SEC_GAMEMASTER, false, &ChatHandler::HandleDebugAnimCommand, "", NULL }, { "arena", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugArenaCommand, "", NULL }, { "bg", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugBattlegroundCommand, "", NULL }, - { "getitemstate", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugGetItemState, "", NULL }, - { "lootrecipient", SEC_GAMEMASTER, false, &ChatHandler::HandleDebugGetLootRecipient, "", NULL }, - { "getvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugGetValue, "", NULL }, - { "Mod32Value", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugMod32Value, "", NULL }, + { "getitemstate", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugGetItemStateCommand, "", NULL }, + { "lootrecipient", SEC_GAMEMASTER, false, &ChatHandler::HandleDebugGetLootRecipientCommand, "", NULL }, + { "getvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugGetValueCommand, "", NULL }, + { "Mod32Value", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugMod32ValueCommand, "", NULL }, { "play", SEC_MODERATOR, false, NULL, "", debugPlayCommandTable }, { "send", SEC_ADMINISTRATOR, false, NULL, "", debugSendCommandTable }, + { "setaurastate", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetAuraStateCommand, "", NULL }, { "setitemflag", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetItemFlagCommand, "", NULL }, - { "setvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetValue, "", NULL }, + { "setvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetValueCommand, "", NULL }, { "spawnvehicle", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSpawnVehicle, "", NULL }, { "setvid", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetVehicleId, "", NULL }, { "entervehicle", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugEnterVehicle, "", NULL }, { "uws", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugUpdateWorldStateCommand, "", NULL }, - { "update", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugUpdate, "", NULL }, + { "update", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugUpdateCommand, "", NULL }, + { "itemexpire", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugItemExpireCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; @@ -371,6 +373,7 @@ ChatCommand * ChatHandler::getCommandTable() { "tame", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcTameCommand, "", NULL }, { "setdeathstate", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetDeathStateCommand, "", NULL }, { "addformation", SEC_MODERATOR, false, &ChatHandler::HandleNpcAddFormationCommand, "", NULL }, + { "setlink", SEC_MODERATOR, false, &ChatHandler::HandleNpcSetLinkCommand, "", NULL }, //{ TODO: fix or remove this commands { "addweapon", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcAddWeaponCommand, "", NULL }, @@ -477,7 +480,6 @@ ChatCommand * ChatHandler::getCommandTable() { "skill_extra_item_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSkillExtraItemTemplateCommand, "", NULL }, { "skill_fishing_base_level", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSkillFishingBaseLevelCommand, "", NULL }, { "skinning_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesSkinningCommand, "", NULL }, - { "spell_affect", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellAffectCommand, "", NULL }, { "spell_required", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellRequiredCommand, "", NULL }, { "spell_area", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellAreaCommand, "", NULL }, { "spell_bonus_data", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellBonusesCommand, "", NULL }, @@ -546,7 +548,6 @@ ChatCommand * ChatHandler::getCommandTable() { { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCommand, "", NULL }, - { "setlink", SEC_MODERATOR, false, &ChatHandler::HandleNpcSetLinkCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; @@ -627,7 +628,7 @@ ChatCommand * ChatHandler::getCommandTable() { "go", SEC_MODERATOR, false, NULL, "", goCommandTable }, { "learn", SEC_MODERATOR, false, NULL, "", learnCommandTable }, { "modify", SEC_MODERATOR, false, NULL, "", modifyCommandTable }, - { "debug", SEC_MODERATOR, false, NULL, "", debugCommandTable }, + { "debug", SEC_MODERATOR, true, NULL, "", debugCommandTable }, { "tele", SEC_MODERATOR, true, NULL, "", teleCommandTable }, { "character", SEC_GAMEMASTER, false, NULL, "", characterCommandTable}, { "event", SEC_GAMEMASTER, false, NULL, "", eventCommandTable }, diff --git a/src/game/Chat.h b/src/game/Chat.h index b970628f94f..ee01b7b61f4 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -138,16 +138,19 @@ class ChatHandler bool HandleDebugAnimCommand(const char* args); bool HandleDebugArenaCommand(const char * args); bool HandleDebugBattlegroundCommand(const char * args); - bool HandleDebugGetItemState(const char * args); - bool HandleDebugGetLootRecipient(const char * args); - bool HandleDebugGetValue(const char* args); - bool HandleDebugMod32Value(const char* args); - bool HandleDebugSetValue(const char* args); + bool HandleDebugGetItemStateCommand(const char * args); + bool HandleDebugGetLootRecipientCommand(const char * args); + bool HandleDebugGetValueCommand(const char* args); + bool HandleDebugMod32ValueCommand(const char* args); + bool HandleDebugSetAuraStateCommand(const char * args); bool HandleDebugSetItemFlagCommand(const char * args); + bool HandleDebugItemExpireCommand(const char * args); bool HandleDebugSetVehicleId(const char * args); - bool HandleDebugSpawnVehicle(const char * args); bool HandleDebugEnterVehicle(const char * args); - bool HandleDebugUpdate(const char* args); + bool HandleDebugSetValueCommand(const char* args); + bool HandleDebugSpawnVehicle(const char * args); + bool HandleDebugSpellCheckCommand(const char* args); + bool HandleDebugUpdateCommand(const char* args); bool HandleDebugUpdateWorldStateCommand(const char* args); bool HandleDebugSet32Bit(const char* args); @@ -397,7 +400,6 @@ class ChatHandler bool HandleReloadSkillDiscoveryTemplateCommand(const char* args); bool HandleReloadSkillExtraItemTemplateCommand(const char* args); bool HandleReloadSkillFishingBaseLevelCommand(const char* args); - bool HandleReloadSpellAffectCommand(const char* args); bool HandleReloadSpellRequiredCommand(const char* args); bool HandleReloadSpellAreaCommand(const char* args); bool HandleReloadSpellElixirCommand(const char* args); @@ -562,7 +564,7 @@ class ChatHandler bool HandleQuestRemove(const char * args); bool HandleQuestComplete(const char * args);*/ - bool HandleSet32Bit(const char* args); + //bool HandleSet32Bit(const char* args); bool HandleSaveAllCommand(const char* args); Player* getSelectedPlayer(); diff --git a/src/game/ChatHandler.cpp b/src/game/ChatHandler.cpp index ae9da2d9fe5..6fdf9f0b222 100644 --- a/src/game/ChatHandler.cpp +++ b/src/game/ChatHandler.cpp @@ -155,6 +155,15 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) GetPlayer()->UpdateSpeakTime(); } + if (GetPlayer()->HasAura(1852) && type != CHAT_MSG_WHISPER) + { + std::string msg=""; + recv_data >> msg; + + SendNotification(GetTrinityString(LANG_GM_SILENCE), GetPlayer()->GetName()); + return; + } + switch(type) { case CHAT_MSG_SAY: @@ -231,6 +240,12 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) } } + if (GetPlayer()->HasAura(1852) && !player->isGameMaster()) + { + SendNotification(GetTrinityString(LANG_GM_SILENCE), GetPlayer()->GetName()); + return; + } + GetPlayer()->Whisper(msg, lang,player->GetGUID()); } break; diff --git a/src/game/Corpse.cpp b/src/game/Corpse.cpp index 9fe54be1bec..83d21bb3b14 100644 --- a/src/game/Corpse.cpp +++ b/src/game/Corpse.cpp @@ -39,6 +39,8 @@ Corpse::Corpse(CorpseType type) : WorldObject() m_type = type; + m_mapId = 0; + m_time = time(NULL); lootForBody = false; @@ -74,9 +76,11 @@ bool Corpse::Create( uint32 guidlow ) bool Corpse::Create( uint32 guidlow, Player *owner) { - SetInstanceId(owner->GetInstanceId()); + ASSERT(owner); - WorldObject::_Create(guidlow, HIGHGUID_CORPSE, owner->GetMapId(), owner->GetPhaseMask()); + //we need to assign owner's map for corpse + //in other way we will get a crash in Corpse::SaveToDB() + SetMap(owner->GetMap()); Relocate(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ(), owner->GetOrientation()); @@ -87,6 +91,8 @@ bool Corpse::Create( uint32 guidlow, Player *owner) return false; } + WorldObject::_Create(guidlow, HIGHGUID_CORPSE, owner->GetPhaseMask()); + SetFloatValue( OBJECT_FIELD_SCALE_X, 1 ); SetUInt64Value( CORPSE_FIELD_OWNER, owner->GetGUID() ); @@ -146,6 +152,7 @@ void Corpse::DeleteFromDB() CharacterDatabase.PExecute("DELETE FROM corpse WHERE player = '%d' AND corpse_type <> '0'", GUID_LOPART(GetOwnerGUID())); } +/* bool Corpse::LoadFromDB(uint32 guid, QueryResult *result, uint32 InstanceId) { bool external = (result != NULL); @@ -173,7 +180,7 @@ bool Corpse::LoadFromDB(uint32 guid, QueryResult *result, uint32 InstanceId) delete result; return true; -} +}*/ bool Corpse::LoadFromDB(uint32 guid, Field *fields) { @@ -183,7 +190,6 @@ bool Corpse::LoadFromDB(uint32 guid, Field *fields) float positionY = fields[1].GetFloat(); float positionZ = fields[2].GetFloat(); float ort = fields[3].GetFloat(); - uint32 mapid = fields[4].GetUInt32(); Object::_Create(guid, 0, HIGHGUID_CORPSE); @@ -193,6 +199,9 @@ bool Corpse::LoadFromDB(uint32 guid, Field *fields) return false; } + SetMapId(fields[4].GetUInt32()); + SetInstanceId(fields[8].GetUInt32()); + m_time = time_t(fields[6].GetUInt64()); m_type = CorpseType(fields[7].GetUInt32()); @@ -202,15 +211,11 @@ bool Corpse::LoadFromDB(uint32 guid, Field *fields) return false; } - uint32 instanceid = fields[8].GetUInt32(); uint32 phaseMask = fields[9].GetUInt32(); // overwrite possible wrong/corrupted guid SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_CORPSE)); - // place - SetInstanceId(instanceid); - SetMapId(mapid); SetPhaseMask(phaseMask, false); Relocate(positionX, positionY, positionZ, ort); diff --git a/src/game/Corpse.h b/src/game/Corpse.h index da3511abfbb..ee6556e3436 100644 --- a/src/game/Corpse.h +++ b/src/game/Corpse.h @@ -61,12 +61,19 @@ class Corpse : public WorldObject bool Create( uint32 guidlow, Player *owner ); void SaveToDB(); - bool LoadFromDB(uint32 guid, QueryResult *result, uint32 InstanceId); + //bool LoadFromDB(uint32 guid, QueryResult *result, uint32 InstanceId); bool LoadFromDB(uint32 guid, Field *fields); void DeleteBonesFromWorld(); void DeleteFromDB(); + void SetMap (Map * map) {WorldObject::SetMap(map); m_mapId = map->GetId(); SetInstanceId(map->GetInstanceId());} + // Used to check object existence in unloaded grids + uint32 GetMapId() const {return m_mapId;} + void SetMapId (uint32 id) {m_mapId = id;} + uint32 GetInstanceId() const {return m_instanceId;} + void SetInstanceId (uint32 id) {m_instanceId = id;} + uint64 const& GetOwnerGUID() const { return GetUInt64Value(CORPSE_FIELD_OWNER); } time_t const& GetGhostTime() const { return m_time; } @@ -95,6 +102,8 @@ class Corpse : public WorldObject CorpseType m_type; time_t m_time; GridPair m_grid; // gride for corpse position for fast search + uint32 m_mapId; // map id for fast corpse check at packet requests and in other situations with unloaded map of corpse. + uint32 m_instanceId; }; #endif diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index 50fa95337f6..fc2185bb5a8 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -691,17 +691,18 @@ void Creature::Motion_Initialize() bool Creature::Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 team, float x, float y, float z, float ang, const CreatureData *data) { + ASSERT(map); + SetMap(map); + SetPhaseMask(phaseMask,false); + Relocate(x, y, z, ang); + if(!IsPositionValid()) { sLog.outError("Creature (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)",guidlow,Entry,x,y); return false; } - SetMapId(map->GetId()); - SetInstanceId(map->GetInstanceId()); - SetPhaseMask(phaseMask,false); - //oX = x; oY = y; dX = x; dY = y; m_moveTime = 0; m_startMove = 0; const bool bResult = CreateFromProto(guidlow, Entry, team, data); @@ -1392,7 +1393,7 @@ void Creature::SelectLevel(const CreatureInfo *cinfo) //damage - float damagemod = _GetDamageMod(rank); + float damagemod = 1.0f;//_GetDamageMod(rank); SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, cinfo->mindmg * damagemod); SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, cinfo->maxdmg * damagemod); @@ -2055,7 +2056,7 @@ void Creature::CallAssistance() TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::AnyAssistCreatureInRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher); CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, grid_creature_searcher, *MapManager::Instance().GetMap(GetMapId(), this)); + cell_lock->Visit(cell_lock, grid_creature_searcher, *GetMap()); } if (!assistList.empty()) @@ -2102,6 +2103,10 @@ bool Creature::CanAssistTo(const Unit* u, const Unit* enemy, bool checkfaction / if (!isAlive()) return false; + // we don't need help from non-combatant ;) + if (isCivilian()) + return false; + // skip fighting creature if (isInCombat()) return false; @@ -2262,6 +2267,43 @@ void Creature::SendZoneUnderAttackMessage(Player* attacker) sWorld.SendGlobalMessage(&data,NULL,(enemy_team==ALLIANCE ? HORDE : ALLIANCE)); } +void Creature::SetInCombatWithZone() +{ + if (!CanHaveThreatList()) + { + sLog.outError("Creature entry %u call SetInCombatWithZone but creature cannot have threat list.", GetEntry()); + return; + } + + Map* pMap = GetMap(); + + if (!pMap->IsDungeon()) + { + sLog.outError("Creature entry %u call SetInCombatWithZone for map (id: %u) that isn't an instance.", GetEntry(), pMap->GetId()); + return; + } + + Map::PlayerList const &PlList = pMap->GetPlayers(); + + if (PlList.isEmpty()) + return; + + for(Map::PlayerList::const_iterator i = PlList.begin(); i != PlList.end(); ++i) + { + if (Player* pPlayer = i->getSource()) + { + if (pPlayer->isGameMaster()) + continue; + + if (pPlayer->isAlive()) + { + pPlayer->SetInCombatWith(this); + AddThreat(pPlayer, 0.0f); + } + } + } +} + void Creature::_AddCreatureSpellCooldown(uint32 spell_id, time_t end_time) { m_CreatureSpellCooldowns[spell_id] = end_time; @@ -2540,3 +2582,4 @@ time_t Creature::GetLinkedCreatureRespawnTime() const return 0; } + diff --git a/src/game/Creature.h b/src/game/Creature.h index f29982436d5..d291aaf9db0 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -150,6 +150,7 @@ enum CreatureFlagsExtra CREATURE_FLAG_EXTRA_WORLDEVENT = 0x00004000, // custom flag for world event creatures (left room for merging) //CREATURE_FLAG_EXTRA_CHARM_AI = 0x00008000, // use ai when charmed CREATURE_FLAG_EXTRA_NO_CRIT = 0x00020000, // creature can't do critical strikes + CREATURE_FLAG_EXTRA_NO_SKILLGAIN = 0x00040000, // creature won't increase weapon skills }; enum SummonMask @@ -163,6 +164,7 @@ enum SummonMask SUMMON_MASK_VEHICLE = 0x00000020, SUMMON_MASK_PUPPET = 0x00000040, SUMMON_MASK_HUNTER_PET = 0x00000080, + SUMMON_MASK_CONTROLABLE_GUARDIAN = 0x00000100, }; // GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform @@ -172,13 +174,14 @@ enum SummonMask #pragma pack(push,1) #endif +#define MAX_KILL_CREDIT 2 + // from `creature_template` table struct CreatureInfo { uint32 Entry; uint32 HeroicEntry; - uint32 unk1; - uint32 unk2; + uint32 KillCredit[MAX_KILL_CREDIT]; uint32 DisplayID_A[2]; uint32 DisplayID_H[2]; char* Name; @@ -319,7 +322,7 @@ struct CreatureData struct CreatureDataAddonAura { - uint16 spell_id; + uint32 spell_id; uint8 effect_idx; }; @@ -692,6 +695,8 @@ class TRINITY_DLL_SPEC Creature : public Unit void SendZoneUnderAttackMessage(Player* attacker); + void SetInCombatWithZone(); + bool hasQuest(uint32 quest_id) const; bool hasInvolvedQuest(uint32 quest_id) const; @@ -700,10 +705,10 @@ class TRINITY_DLL_SPEC Creature : public Unit virtual uint8 GetPetAutoSpellSize() const { return CREATURE_MAX_SPELLS; } virtual uint32 GetPetAutoSpellOnPos(uint8 pos) const { - if (pos >= CREATURE_MAX_SPELLS || m_charmInfo->GetCharmSpell(pos)->active != ACT_ENABLED) + if (pos >= CREATURE_MAX_SPELLS || m_charmInfo->GetCharmSpell(pos)->GetType() != ACT_ENABLED) return 0; else - return m_charmInfo->GetCharmSpell(pos)->spellId; + return m_charmInfo->GetCharmSpell(pos)->GetAction(); } void SetHomePosition(float x, float y, float z, float ori) { mHome_X = x; mHome_Y = y; mHome_Z = z; mHome_O = ori;} @@ -734,10 +739,11 @@ class TRINITY_DLL_SPEC Creature : public Unit } void ResetPlayerDamageReq() { m_PlayerDamageReq = GetHealth() / 2; } uint32 m_PlayerDamageReq; - + void SetOriginalEntry(uint32 entry) { m_originalEntry = entry; } static float _GetDamageMod(int32 Rank); + protected: bool CreateFromProto(uint32 guidlow,uint32 Entry,uint32 team, const CreatureData *data = NULL); bool InitEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL); @@ -751,7 +757,6 @@ class TRINITY_DLL_SPEC Creature : public Unit uint32 m_lootMoney; uint64 m_lootRecipient; - uint32 m_unDamageByPlayers; /// Timers uint32 m_deathTimer; // (msecs)timer for death or corpse disappearance diff --git a/src/game/CreatureAI.cpp b/src/game/CreatureAI.cpp index 9f06ca38b56..165bb2411ec 100644 --- a/src/game/CreatureAI.cpp +++ b/src/game/CreatureAI.cpp @@ -91,6 +91,13 @@ void CreatureAI::DoZoneInCombat(Creature* creature) pPlayer->SetInCombatWith(creature); creature->AddThreat(pPlayer, 0.0f); } + + for(Unit::ControlList::const_iterator itr = pPlayer->m_Controlled.begin(); itr != pPlayer->m_Controlled.end(); ++itr) + { + creature->SetInCombatWith(*itr); + (*itr)->SetInCombatWith(creature); + creature->AddThreat(*itr, 0.0f); + } } } } @@ -148,7 +155,6 @@ bool CreatureAI::UpdateVictim() { if(!me->isInCombat()) return false; - if(Unit *victim = me->SelectVictim()) AttackStart(victim); return me->getVictim(); @@ -194,10 +200,16 @@ void CreatureAI::EnterEvadeMode() if(!_EnterEvadeMode()) return; - if(Unit *owner = me->GetCharmerOrOwner()) - me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE, MOTION_SLOT_IDLE); - else - me->GetMotionMaster()->MoveTargetedHome(); + if(!me->m_Vehicle) // otherwise me will be in evade mode forever + { + if(Unit *owner = me->GetCharmerOrOwner()) + { + me->GetMotionMaster()->Clear(false); + me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, m_creature->GetFollowAngle(), MOTION_SLOT_ACTIVE); + } + else + me->GetMotionMaster()->MoveTargetedHome(); + } Reset(); } diff --git a/src/game/CreatureAI.h b/src/game/CreatureAI.h index 91fabe30448..061b3d273c2 100644 --- a/src/game/CreatureAI.h +++ b/src/game/CreatureAI.h @@ -115,6 +115,7 @@ class TRINITY_DLL_SPEC CreatureAI : public UnitAI // Called when the creature is target of hostile action: swing, hostile spell landed, fear/etc) //virtual void AttackedBy(Unit* attacker); + virtual bool IsEscorted () {return false;} // Called when creature is spawned or respawned (for reseting variables) virtual void JustRespawned() { Reset(); } diff --git a/src/game/CreatureAIImpl.h b/src/game/CreatureAIImpl.h index f6653ce40f7..bcedd108d44 100644 --- a/src/game/CreatureAIImpl.h +++ b/src/game/CreatureAIImpl.h @@ -246,10 +246,13 @@ enum AICondition struct AISpellInfoType { - AISpellInfoType() : target(AITARGET_SELF), condition(AICOND_COMBAT), cooldown(AI_DEFAULT_COOLDOWN) {} + AISpellInfoType() : target(AITARGET_SELF), condition(AICOND_COMBAT) + , cooldown(AI_DEFAULT_COOLDOWN), realCooldown(0), maxRange(0.0f){} AITarget target; AICondition condition; uint32 cooldown; + uint32 realCooldown; + float maxRange; }; TRINITY_DLL_SPEC AISpellInfoType * GetAISpellInfo(uint32 i); diff --git a/src/game/CreatureAISelector.cpp b/src/game/CreatureAISelector.cpp index 65e50a2e41b..68055bcddd3 100644 --- a/src/game/CreatureAISelector.cpp +++ b/src/game/CreatureAISelector.cpp @@ -54,13 +54,13 @@ namespace FactorySelector // select by NPC flags if(!ai_factory) { - if(creature->isGuardian() && ((Guardian*)creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER) + if(creature->HasSummonMask(SUMMON_MASK_CONTROLABLE_GUARDIAN) && ((Guardian*)creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER) ai_factory = ai_registry.GetRegistryItem("PetAI"); else if(creature->isVehicle() || creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK)) ai_factory = ai_registry.GetRegistryItem("NullCreatureAI"); else if(creature->isGuard()) ai_factory = ai_registry.GetRegistryItem("GuardAI"); - else if(creature->isGuardian()) + else if(creature->HasSummonMask(SUMMON_MASK_CONTROLABLE_GUARDIAN)) ai_factory = ai_registry.GetRegistryItem("PetAI"); else if(creature->isTotem()) ai_factory = ai_registry.GetRegistryItem("TotemAI"); @@ -71,7 +71,7 @@ namespace FactorySelector else ai_factory = ai_registry.GetRegistryItem("NullCreatureAI"); } - else if(creature->GetCreatureType() == CREATURE_TYPE_CRITTER) + else if(creature->GetCreatureType() == CREATURE_TYPE_CRITTER && !creature->HasSummonMask(SUMMON_MASK_GUARDIAN)) ai_factory = ai_registry.GetRegistryItem("CritterAI"); } diff --git a/src/game/CreatureEventAI.cpp b/src/game/CreatureEventAI.cpp index 0393c30e7d3..5c425217844 100644 --- a/src/game/CreatureEventAI.cpp +++ b/src/game/CreatureEventAI.cpp @@ -30,6 +30,7 @@ #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "InstanceData.h" +#include "SpellMgr.h" bool CreatureEventAIHolder::UpdateRepeatTimer( Creature* creature, uint32 repeatMin, uint32 repeatMax ) { @@ -95,6 +96,8 @@ CreatureEventAI::CreatureEventAI(Creature *c ) : CreatureAI(c) AttackDistance = 0.0f; AttackAngle = 0.0f; + InvinceabilityHpLevel = 0; + //Handle Spawned Events if (!bEmptyList) { @@ -113,13 +116,6 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction if (pHolder.Event.event_inverse_phase_mask & (1 << Phase)) return false; - //Store random here so that all random actions match up - uint32 rnd = rand(); - - //Return if chance for event is not met - if (pHolder.Event.event_chance <= rnd % 100) - return false; - CreatureEventAI_Event const& event = pHolder.Event; //Check event conditions based on the event type, also reset events @@ -331,6 +327,13 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction if (!(pHolder.Event.event_flags & EFLAG_REPEATABLE)) pHolder.Enabled = false; + //Store random here so that all random actions match up + uint32 rnd = rand(); + + //Return if chance for event is not met + if (pHolder.Event.event_chance <= rnd % 100) + return false; + //Process actions for (uint32 j = 0; j < MAX_ACTIONS; j++) ProcessAction(pHolder.Event.action[j], rnd, pHolder.Event.event_id, pActionInvoker); @@ -746,15 +749,10 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(),NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); break; case ACTION_T_ZONE_COMBAT_PULSE: - if (!m_creature->isInCombat() || !m_creature->GetMap()->IsDungeon()) - { - - sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_ZONE_COMBAT_PULSE on creature out of combat or in non-dungeon map. Creature %d", EventId, m_creature->GetEntry()); - return; - } - - DoZoneInCombat(m_creature); + { + m_creature->SetInCombatWithZone(); break; + } case ACTION_T_CALL_FOR_HELP: { m_creature->CallForHelp(action.call_for_help.radius); @@ -796,6 +794,14 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 m_creature->ForcedDespawn(); break; } + case ACTION_T_SET_INVINCIBILITY_HP_LEVEL: + { + if(action.invincibility_hp_level.is_percent) + InvinceabilityHpLevel = m_creature->GetMaxHealth()*action.invincibility_hp_level.hp_level/100; + else + InvinceabilityHpLevel = action.invincibility_hp_level.hp_level; + break; + } } } @@ -1302,7 +1308,7 @@ bool CreatureEventAI::CanCast(Unit* Target, SpellEntry const *Spell, bool Trigge return false; //Check for power - if (!Triggered && me->GetPower((Powers)Spell->powerType) < Spell->manaCost) + if (!Triggered && me->GetPower((Powers)Spell->powerType) < CalculatePowerCost(Spell, me, GetSpellSchoolMask(Spell))) return false; SpellRangeEntry const *TempRange = NULL; @@ -1342,6 +1348,17 @@ void CreatureEventAI::ReceiveEmote(Player* pPlayer, uint32 text_emote) } } +void CreatureEventAI::DamageTaken( Unit* done_by, uint32& damage ) +{ + if(InvinceabilityHpLevel > 0 && m_creature->GetHealth() < InvinceabilityHpLevel+damage) + { + if(m_creature->GetHealth() <= InvinceabilityHpLevel) + damage = 0; + else + damage = m_creature->GetHealth() - InvinceabilityHpLevel; + } +} + bool CreatureEventAI::SpawnedEventConditionsCheck(CreatureEventAI_Event const& event) { if(event.event_type != EVENT_T_SPAWNED) diff --git a/src/game/CreatureEventAI.h b/src/game/CreatureEventAI.h index ad3827ef75d..7882e7542ec 100644 --- a/src/game/CreatureEventAI.h +++ b/src/game/CreatureEventAI.h @@ -113,6 +113,7 @@ enum EventAI_ActionType ACTION_T_FORCE_DESPAWN = 41, // No Params ACTION_T_END = 105, + ACTION_T_SET_INVINCIBILITY_HP_LEVEL = 42, // MinHpValue, format(0-flat,1-percent from max health) }; enum Target @@ -379,6 +380,12 @@ struct CreatureEventAI_Action { uint32 sheath; } set_sheath; + // ACTION_T_SET_INVINCIBILITY_HP_LEVEL = 42 + struct + { + uint32 hp_level; + uint32 is_percent; + } invincibility_hp_level; // RAW struct { @@ -581,6 +588,7 @@ class TRINITY_DLL_SPEC CreatureEventAI : public CreatureAI void AttackStart(Unit *who); void MoveInLineOfSight(Unit *who); void SpellHit(Unit* pUnit, const SpellEntry* pSpell); + void DamageTaken(Unit* done_by, uint32& damage); void UpdateAI(const uint32 diff); void ReceiveEmote(Player* pPlayer, uint32 text_emote); static int Permissible(const Creature *); @@ -608,10 +616,11 @@ class TRINITY_DLL_SPEC CreatureEventAI : public CreatureAI bool bEmptyList; //Variables used by Events themselves - uint8 Phase; //Current phase, max 32 phases - bool CombatMovementEnabled; //If we allow targeted movment gen (movement twoards top threat) - bool MeleeEnabled; //If we allow melee auto attack - float AttackDistance; //Distance to attack from - float AttackAngle; //Angle of attack + uint8 Phase; // Current phase, max 32 phases + bool CombatMovementEnabled; // If we allow targeted movment gen (movement twoards top threat) + bool MeleeEnabled; // If we allow melee auto attack + float AttackDistance; // Distance to attack from + float AttackAngle; // Angle of attack + uint32 InvinceabilityHpLevel; // Minimal health level allowed at damage apply }; #endif diff --git a/src/game/CreatureEventAIMgr.cpp b/src/game/CreatureEventAIMgr.cpp index 38cc84177e4..901ec541455 100644 --- a/src/game/CreatureEventAIMgr.cpp +++ b/src/game/CreatureEventAIMgr.cpp @@ -39,7 +39,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Texts() objmgr.LoadTrinityStrings(WorldDatabase,"creature_ai_texts",MIN_CREATURE_AI_TEXT_STRING_ID,MAX_CREATURE_AI_TEXT_STRING_ID); // Gather Additional data from EventAI Texts - QueryResult *result = WorldDatabase.PQuery("SELECT entry, sound, type, language, emote FROM creature_ai_texts"); + QueryResult *result = WorldDatabase.Query("SELECT entry, sound, type, language, emote FROM creature_ai_texts"); sLog.outString("Loading EventAI Texts additional data..."); if (result) @@ -117,8 +117,8 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Summons() //Drop Existing EventSummon Map m_CreatureEventAI_Summon_Map.clear(); - //Gather additional data for EventAI - QueryResult *result = WorldDatabase.PQuery("SELECT id, position_x, position_y, position_z, orientation, spawntimesecs FROM creature_ai_summons"); + // Gather additional data for EventAI + QueryResult *result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, spawntimesecs FROM creature_ai_summons"); if (result) { barGoLink bar(result->GetRowCount()); @@ -169,8 +169,8 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts() //Drop Existing EventAI List m_CreatureEventAI_Event_Map.clear(); - //Gather event data - QueryResult *result = WorldDatabase.PQuery("SELECT id, creature_id, event_type, event_inverse_phase_mask, event_chance, event_flags, " + // Gather event data + QueryResult *result = WorldDatabase.Query("SELECT id, creature_id, event_type, event_inverse_phase_mask, event_chance, event_flags, " "event_param1, event_param2, event_param3, event_param4, " "action1_type, action1_param1, action1_param2, action1_param3, " "action2_type, action2_param1, action2_param2, action2_param3, " @@ -473,9 +473,8 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts() action.morph.modelId = 0; } } - - break; } + break; case ACTION_T_SOUND: if (!sSoundEntriesStore.LookupEntry(action.sound.soundId)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SoundID %u.", i, j+1, action.sound.soundId); @@ -662,6 +661,16 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts() action.set_sheath.sheath = SHEATH_STATE_UNARMED; } break; + case ACTION_T_SET_INVINCIBILITY_HP_LEVEL: + if(action.invincibility_hp_level.is_percent) + { + if(action.invincibility_hp_level.hp_level > 100) + { + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses wrong percent value %u.", i, j+1, action.invincibility_hp_level.hp_level); + action.invincibility_hp_level.hp_level = 100; + } + } + break; case ACTION_T_EVADE: //No Params case ACTION_T_FLEE_FOR_ASSIST: //No Params case ACTION_T_DIE: //No Params diff --git a/src/game/DBCStores.cpp b/src/game/DBCStores.cpp index f618b8cfb8c..42780870499 100644 --- a/src/game/DBCStores.cpp +++ b/src/game/DBCStores.cpp @@ -191,7 +191,7 @@ inline void LoadDBC(uint32& availableDbcLocales,barGoLink& bar, StoreProblemList if(f) { char buf[100]; - snprintf(buf,100," (exist, but have %d fields instead %d) Wrong client version DBC file?",storage.GetFieldCount(),strlen(storage.GetFormat())); + snprintf(buf,100," (exist, but have %d fields instead " SIZEFMTD ") Wrong client version DBC file?",storage.GetFieldCount(),strlen(storage.GetFormat())); errlist.push_back(dbc_filename + buf); fclose(f); } @@ -696,3 +696,4 @@ TRINITY_DLL_SPEC DBCStorage <ItemEntry> const* GetItemDisplayStore() TRINITY_DLL_SPEC DBCStorage <CreatureDisplayInfoEntry> const* GetCreatureDisplayStore() { return &sCreatureDisplayInfoStore; } TRINITY_DLL_SPEC DBCStorage <EmotesEntry> const* GetEmotesStore() { return &sEmotesStore; } TRINITY_DLL_SPEC DBCStorage <EmotesTextEntry> const* GetEmotesTextStore() { return &sEmotesTextStore; } +TRINITY_DLL_SPEC DBCStorage <AchievementEntry> const* GetAchievementStore() { return &sAchievementStore; } diff --git a/src/game/DBCStores.h b/src/game/DBCStores.h index a423cd0bfc8..fc47568e6a5 100644 --- a/src/game/DBCStores.h +++ b/src/game/DBCStores.h @@ -158,4 +158,5 @@ TRINITY_DLL_SPEC DBCStorage <ItemEntry> const* GetItemDisplaySt TRINITY_DLL_SPEC DBCStorage <CreatureDisplayInfoEntry> const* GetCreatureDisplayStore(); TRINITY_DLL_SPEC DBCStorage <EmotesEntry> const* GetEmotesStore(); TRINITY_DLL_SPEC DBCStorage <EmotesTextEntry> const* GetEmotesTextStore(); +TRINITY_DLL_SPEC DBCStorage <AchievementEntry> const* GetAchievementStore(); #endif diff --git a/src/game/DBCStructure.h b/src/game/DBCStructure.h index 686c43fc355..a8b5d3ef177 100644 --- a/src/game/DBCStructure.h +++ b/src/game/DBCStructure.h @@ -84,11 +84,14 @@ struct AchievementCriteriaEntry } kill_creature; // ACHIEVEMENT_CRITERIA_TYPE_WIN_BG = 1 - // TODO: there are further criterias instead just winning struct { uint32 bgMapID; // 3 uint32 winCount; // 4 + uint32 additionalRequirement1_type; // 5 additional requirement 1 type + uint32 additionalRequirement1_value; // 6 additional requirement 1 value + uint32 additionalRequirement2_type; // 7 additional requirement 2 type + uint32 additionalRequirement2_value; // 8 additional requirement 1 value } win_bg; // ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL = 5 @@ -490,7 +493,9 @@ struct AchievementCriteriaEntry //uint32 name_flags; // 25 uint32 completionFlag; // 26 uint32 groupFlag; // 27 - //uint32 unk1; // 28 + //uint32 unk1; // 28 Alway appears with timed events + // for timed spells it is spell id for + // timed kills it is creature id uint32 timeLimit; // 29 time limit in seconds //uint32 showOrder; // 30 show order }; @@ -507,6 +512,14 @@ struct AreaTableEntry char* area_name[16]; // 11-26 // 27, string flags, unused uint32 team; // 28 + + // helpers + bool IsSanctuary() const + { + if (mapid == 609) + return true; + return (flags & AREA_FLAG_SANCTUARY); + } }; struct AreaGroupEntry @@ -1423,7 +1436,7 @@ struct SpellEntry uint32 SchoolMask; // 228 m_schoolMask uint32 runeCostID; // 229 m_runeCostID //uint32 spellMissileID; // 230 m_spellMissileID not used - //uint32 PowerDisplayId; // 231 PowerDisplay.dbc, new in 3.1 + //uint32 PowerDisplayId; // 231 PowerDisplay.dbc, new in 3.1 // helpers int32 CalculateSimpleValue(uint8 eff) const { return EffectBasePoints[eff]+int32(EffectBaseDice[eff]); } @@ -1453,13 +1466,6 @@ struct SpellFocusObjectEntry // 16 string flags, unused }; -// stored in SQL table -struct SpellThreatEntry -{ - uint32 spellId; - int32 threat; -}; - struct SpellRadiusEntry { uint32 ID; diff --git a/src/game/Debugcmds.cpp b/src/game/Debugcmds.cpp index d8b94df4e10..69b3bcb1284 100644 --- a/src/game/Debugcmds.cpp +++ b/src/game/Debugcmds.cpp @@ -36,10 +36,12 @@ #include "CellImpl.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" +#include "SpellMgr.h" +#include "ScriptCalls.h" bool ChatHandler::HandleDebugSendSpellFailCommand(const char* args) { - if(!args) + if (!*args) return false; char* px = strtok((char*)args, " "); @@ -47,7 +49,7 @@ bool ChatHandler::HandleDebugSendSpellFailCommand(const char* args) return false; uint8 failnum = (uint8)atoi(px); - if(failnum==0 && *px!='0') + if (failnum==0 && *px!='0') return false; char* p1 = strtok(NULL, " "); @@ -72,20 +74,20 @@ bool ChatHandler::HandleDebugSendSpellFailCommand(const char* args) bool ChatHandler::HandleDebugSendPoiCommand(const char* args) { + if (!*args) + return false; + Player *pPlayer = m_session->GetPlayer(); Unit* target = getSelectedUnit(); - if(!target) + if (!target) { SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); return true; } - if(!args) - return false; - char* icon_text = strtok((char*)args, " "); char* flags_text = strtok(NULL, " "); - if(!icon_text || !flags_text) + if (!icon_text || !flags_text) return false; uint32 icon = atol(icon_text); @@ -98,7 +100,7 @@ bool ChatHandler::HandleDebugSendPoiCommand(const char* args) bool ChatHandler::HandleDebugSendEquipErrorCommand(const char* args) { - if(!args) + if (!*args) return false; uint8 msg = atoi(args); @@ -108,7 +110,7 @@ bool ChatHandler::HandleDebugSendEquipErrorCommand(const char* args) bool ChatHandler::HandleDebugSendSellErrorCommand(const char* args) { - if(!args) + if (!*args) return false; uint8 msg = atoi(args); @@ -118,7 +120,7 @@ bool ChatHandler::HandleDebugSendSellErrorCommand(const char* args) bool ChatHandler::HandleDebugSendBuyErrorCommand(const char* args) { - if(!args) + if (!*args) return false; uint8 msg = atoi(args); @@ -137,7 +139,7 @@ bool ChatHandler::HandleDebugSendOpcodeCommand(const char* /*args*/) if(!unit) unit = player; std::ifstream ifs("opcode.txt"); - if(ifs.bad()) + if (ifs.bad()) return false; uint32 opcode; @@ -273,7 +275,7 @@ bool ChatHandler::HandleDebugPlayCinematicCommand(const char* args) { // USAGE: .debug play cinematic #cinematicid // #cinematicid - ID decimal number from CinemaicSequences.dbc (1st column) - if( !*args ) + if (!*args) { SendSysMessage(LANG_BAD_VALUE); SetSentErrorMessage(true); @@ -282,7 +284,7 @@ bool ChatHandler::HandleDebugPlayCinematicCommand(const char* args) uint32 dwId = atoi((char*)args); - if(!sCinematicSequencesStore.LookupEntry(dwId)) + if (!sCinematicSequencesStore.LookupEntry(dwId)) { PSendSysMessage(LANG_CINEMATIC_NOT_EXIST, dwId); SetSentErrorMessage(true); @@ -297,7 +299,7 @@ bool ChatHandler::HandleDebugPlayMovieCommand(const char* args) { // USAGE: .debug play movie #movieid // #movieid - ID decimal number from Movie.dbc (1st column) - if( !*args ) + if (!*args) { SendSysMessage(LANG_BAD_VALUE); SetSentErrorMessage(true); @@ -306,7 +308,7 @@ bool ChatHandler::HandleDebugPlayMovieCommand(const char* args) uint32 dwId = atoi((char*)args); - if(!sMovieStore.LookupEntry(dwId)) + if (!sMovieStore.LookupEntry(dwId)) { PSendSysMessage(LANG_MOVIE_NOT_EXIST, dwId); SetSentErrorMessage(true); @@ -322,7 +324,7 @@ bool ChatHandler::HandleDebugPlaySoundCommand(const char* args) { // USAGE: .debug playsound #soundid // #soundid - ID decimal number from SoundEntries.dbc (1st column) - if( !*args ) + if (!*args) { SendSysMessage(LANG_BAD_VALUE); SetSentErrorMessage(true); @@ -331,7 +333,7 @@ bool ChatHandler::HandleDebugPlaySoundCommand(const char* args) uint32 dwSoundId = atoi((char*)args); - if(!sSoundEntriesStore.LookupEntry(dwSoundId)) + if (!sSoundEntriesStore.LookupEntry(dwSoundId)) { PSendSysMessage(LANG_SOUND_NOT_EXIST, dwSoundId); SetSentErrorMessage(true); @@ -339,14 +341,14 @@ bool ChatHandler::HandleDebugPlaySoundCommand(const char* args) } Unit* unit = getSelectedUnit(); - if(!unit) + if (!unit) { SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); SetSentErrorMessage(true); return false; } - if(m_session->GetPlayer()->GetSelection()) + if (m_session->GetPlayer()->GetSelection()) unit->PlayDistanceSound(dwSoundId,m_session->GetPlayer()); else unit->PlayDirectSound(dwSoundId,m_session->GetPlayer()); @@ -358,7 +360,7 @@ bool ChatHandler::HandleDebugPlaySoundCommand(const char* args) //Send notification in channel bool ChatHandler::HandleDebugSendChannelNotifyCommand(const char* args) { - if(!args) + if (!*args) return false; const char *name = "test"; @@ -376,7 +378,7 @@ bool ChatHandler::HandleDebugSendChannelNotifyCommand(const char* args) //Send notification in chat bool ChatHandler::HandleDebugSendChatMsgCommand(const char* args) { - if(!args) + if (!*args) return false; const char *msg = "testtest"; @@ -394,10 +396,10 @@ bool ChatHandler::HandleDebugSendQuestPartyMsgCommand(const char* args) return true; } -bool ChatHandler::HandleDebugGetLootRecipient(const char* /*args*/) +bool ChatHandler::HandleDebugGetLootRecipientCommand(const char* /*args*/) { Creature* target = getSelectedCreature(); - if(!target) + if (!target) return false; PSendSysMessage("loot recipient: %s", target->hasLootRecipient()?(target->GetLootRecipient()?target->GetLootRecipient()->GetName():"offline"):"no loot recipient"); @@ -411,9 +413,9 @@ bool ChatHandler::HandleDebugSendQuestInvalidMsgCommand(const char* args) return true; } -bool ChatHandler::HandleDebugGetItemState(const char* args) +bool ChatHandler::HandleDebugGetItemStateCommand(const char* args) { - if (!args) + if (!*args) return false; std::string state_str = args; @@ -613,13 +615,13 @@ bool ChatHandler::HandleDebugGetItemState(const char* args) if (item->GetOwnerGUID() != player->GetGUID()) { - PSendSysMessage("queue(%d): for the an item (guid %d), the owner's guid (%d) and player's guid (%d) don't match!", i, item->GetGUIDLow(), GUID_LOPART(item->GetOwnerGUID()), player->GetGUIDLow()); + PSendSysMessage("queue(" SIZEFMTD "): for the an item (guid %d), the owner's guid (%d) and player's guid (%d) don't match!", i, item->GetGUIDLow(), GUID_LOPART(item->GetOwnerGUID()), player->GetGUIDLow()); error = true; continue; } if (item->GetQueuePos() != i) { - PSendSysMessage("queue(%d): for the an item (guid %d), the queuepos doesn't match it's position in the queue!", i, item->GetGUIDLow()); + PSendSysMessage("queue(" SIZEFMTD "): for the an item (guid %d), the queuepos doesn't match it's position in the queue!", i, item->GetGUIDLow()); error = true; continue; } @@ -628,13 +630,13 @@ bool ChatHandler::HandleDebugGetItemState(const char* args) if (test == NULL) { - PSendSysMessage("queue(%d): the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the player doesn't have an item at that position!", i, item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow()); + PSendSysMessage("queue(" SIZEFMTD "): the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the player doesn't have an item at that position!", i, item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow()); error = true; continue; } if (test != item) { - PSendSysMessage("queue(%d): the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the item with guid %d is there instead!", i, item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow(), test->GetGUIDLow()); + PSendSysMessage("queue(" SIZEFMTD "): the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the item with guid %d is there instead!", i, item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow(), test->GetGUIDLow()); error = true; continue; } } @@ -757,7 +759,7 @@ bool ChatHandler::HandleDebugEnterVehicle(const char * args) bool ChatHandler::HandleDebugSpawnVehicle(const char* args) { - if(!args) + if (!*args) return false; char* e = strtok((char*)args, " "); @@ -778,12 +780,12 @@ bool ChatHandler::HandleDebugSpawnVehicle(const char* args) CreatureInfo const *ci = objmgr.GetCreatureTemplate(entry); - if(!ci) + if (!ci) return false; VehicleEntry const *ve = sVehicleStore.LookupEntry(id); - if(!ve) + if (!ve) return false; Vehicle *v = new Vehicle; @@ -812,7 +814,7 @@ bool ChatHandler::HandleDebugSendLargePacketCommand(const char* /*args*/) bool ChatHandler::HandleDebugSendSetPhaseShiftCommand(const char* args) { - if(!args) + if (!*args) return false; uint32 PhaseShift = atoi(args); @@ -822,7 +824,7 @@ bool ChatHandler::HandleDebugSendSetPhaseShiftCommand(const char* args) bool ChatHandler::HandleDebugSetItemFlagCommand(const char* args) { - if(!args) + if (!*args) return false; char* e = strtok((char*)args, " "); @@ -836,7 +838,7 @@ bool ChatHandler::HandleDebugSetItemFlagCommand(const char* args) Item *i = m_session->GetPlayer()->GetItemByGuid(MAKE_NEW_GUID(guid, 0, HIGHGUID_ITEM)); - if(!i) + if (!i) return false; i->SetUInt32Value(ITEM_FIELD_FLAGS, flag); @@ -844,6 +846,28 @@ bool ChatHandler::HandleDebugSetItemFlagCommand(const char* args) return true; } +bool ChatHandler::HandleDebugItemExpireCommand(const char* args) +{ + if (!*args) + return false; + + char* e = strtok((char*)args, " "); + if (!e) + return false; + + uint32 guid = (uint32)atoi(e); + + Item *i = m_session->GetPlayer()->GetItemByGuid(MAKE_NEW_GUID(guid, 0, HIGHGUID_ITEM)); + + if (!i) + return false; + + m_session->GetPlayer()->DestroyItem( i->GetBagSlot(),i->GetSlot(), true); + Script->ItemExpire(m_session->GetPlayer(),i->GetProto()); + + return true; +} + //show animation bool ChatHandler::HandleDebugAnimCommand(const char* args) { @@ -854,3 +878,216 @@ bool ChatHandler::HandleDebugAnimCommand(const char* args) m_session->GetPlayer()->HandleEmoteCommand(anim_id); return true; } + +bool ChatHandler::HandleDebugSetAuraStateCommand(const char* args) +{ + if (!*args) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Unit* unit = getSelectedUnit(); + if (!unit) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + int32 state = atoi((char*)args); + if (!state) + { + // reset all states + for(int i = 1; i <= 32; ++i) + unit->ModifyAuraState(AuraState(i),false); + return true; + } + + unit->ModifyAuraState(AuraState(abs(state)),state > 0); + return true; +} + +bool ChatHandler::HandleDebugSetValueCommand(const char* args) +{ + if(!*args) + return false; + + char* px = strtok((char*)args, " "); + char* py = strtok(NULL, " "); + char* pz = strtok(NULL, " "); + + if (!px || !py) + return false; + + WorldObject* target = getSelectedObject(); + if(!target) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + uint64 guid = target->GetGUID(); + + uint32 Opcode = (uint32)atoi(px); + if(Opcode >= target->GetValuesCount()) + { + PSendSysMessage(LANG_TOO_BIG_INDEX, Opcode, GUID_LOPART(guid), target->GetValuesCount()); + return false; + } + uint32 iValue; + float fValue; + bool isint32 = true; + if(pz) + isint32 = (bool)atoi(pz); + if(isint32) + { + iValue = (uint32)atoi(py); + sLog.outDebug(GetTrinityString(LANG_SET_UINT), GUID_LOPART(guid), Opcode, iValue); + target->SetUInt32Value( Opcode , iValue ); + PSendSysMessage(LANG_SET_UINT_FIELD, GUID_LOPART(guid), Opcode,iValue); + } + else + { + fValue = (float)atof(py); + sLog.outDebug(GetTrinityString(LANG_SET_FLOAT), GUID_LOPART(guid), Opcode, fValue); + target->SetFloatValue( Opcode , fValue ); + PSendSysMessage(LANG_SET_FLOAT_FIELD, GUID_LOPART(guid), Opcode,fValue); + } + + return true; +} + +bool ChatHandler::HandleDebugGetValueCommand(const char* args) +{ + if(!*args) + return false; + + char* px = strtok((char*)args, " "); + char* pz = strtok(NULL, " "); + + if (!px) + return false; + + Unit* target = getSelectedUnit(); + if(!target) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + uint64 guid = target->GetGUID(); + + uint32 Opcode = (uint32)atoi(px); + if(Opcode >= target->GetValuesCount()) + { + PSendSysMessage(LANG_TOO_BIG_INDEX, Opcode, GUID_LOPART(guid), target->GetValuesCount()); + return false; + } + uint32 iValue; + float fValue; + bool isint32 = true; + if(pz) + isint32 = (bool)atoi(pz); + + if(isint32) + { + iValue = target->GetUInt32Value( Opcode ); + sLog.outDebug(GetTrinityString(LANG_GET_UINT), GUID_LOPART(guid), Opcode, iValue); + PSendSysMessage(LANG_GET_UINT_FIELD, GUID_LOPART(guid), Opcode, iValue); + } + else + { + fValue = target->GetFloatValue( Opcode ); + sLog.outDebug(GetTrinityString(LANG_GET_FLOAT), GUID_LOPART(guid), Opcode, fValue); + PSendSysMessage(LANG_GET_FLOAT_FIELD, GUID_LOPART(guid), Opcode, fValue); + } + + return true; +} + +bool ChatHandler::HandleDebugMod32ValueCommand(const char* args) +{ + if(!*args) + return false; + + char* px = strtok((char*)args, " "); + char* py = strtok(NULL, " "); + + if (!px || !py) + return false; + + uint32 Opcode = (uint32)atoi(px); + int Value = atoi(py); + + if(Opcode >= m_session->GetPlayer()->GetValuesCount()) + { + PSendSysMessage(LANG_TOO_BIG_INDEX, Opcode, m_session->GetPlayer()->GetGUIDLow(), m_session->GetPlayer( )->GetValuesCount()); + return false; + } + + sLog.outDebug(GetTrinityString(LANG_CHANGE_32BIT), Opcode, Value); + + int CurrentValue = (int)m_session->GetPlayer( )->GetUInt32Value( Opcode ); + + CurrentValue += Value; + m_session->GetPlayer( )->SetUInt32Value( Opcode , (uint32)CurrentValue ); + + PSendSysMessage(LANG_CHANGE_32BIT_FIELD, Opcode,CurrentValue); + + return true; +} + +bool ChatHandler::HandleDebugUpdateCommand(const char* args) +{ + if(!*args) + return false; + + uint32 updateIndex; + uint32 value; + + char* pUpdateIndex = strtok((char*)args, " "); + + Unit* chr = getSelectedUnit(); + if (chr == NULL) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + if(!pUpdateIndex) + { + return true; + } + updateIndex = atoi(pUpdateIndex); + //check updateIndex + if(chr->GetTypeId() == TYPEID_PLAYER) + { + if (updateIndex>=PLAYER_END) return true; + } + else + { + if (updateIndex>=UNIT_END) return true; + } + + char* pvalue = strtok(NULL, " "); + if (!pvalue) + { + value=chr->GetUInt32Value(updateIndex); + + PSendSysMessage(LANG_UPDATE, chr->GetGUIDLow(),updateIndex,value); + return true; + } + + value=atoi(pvalue); + + PSendSysMessage(LANG_UPDATE_CHANGE, chr->GetGUIDLow(),updateIndex,value); + + chr->SetUInt32Value(updateIndex,value); + + return true; +} diff --git a/src/game/DestinationHolderImp.h b/src/game/DestinationHolderImp.h index d50d8c3e727..c84ce19a96d 100644 --- a/src/game/DestinationHolderImp.h +++ b/src/game/DestinationHolderImp.h @@ -142,7 +142,7 @@ DestinationHolder<TRAVELLER>::UpdateTraveller(TRAVELLER &traveller, uint32 diff, i_fromZ = z; } - if( traveller.GetTraveller().GetPositionX() != x || traveller.GetTraveller().GetPositionY() != y ) + if( traveller.GetTraveller().GetPositionX() != x || traveller.GetTraveller().GetPositionY() != y || traveller.GetTraveller().GetPositionZ() != z) { float ori = traveller.GetTraveller().GetAngle(x, y); traveller.Relocation(x, y, z, ori); diff --git a/src/game/DynamicObject.cpp b/src/game/DynamicObject.cpp index dc2b05e8d6a..80b0d91a1b8 100644 --- a/src/game/DynamicObject.cpp +++ b/src/game/DynamicObject.cpp @@ -72,9 +72,8 @@ void DynamicObject::RemoveFromWorld() bool DynamicObject::Create( uint32 guidlow, Unit *caster, uint32 spellId, uint32 effIndex, float x, float y, float z, int32 duration, float radius ) { - SetInstanceId(caster->GetInstanceId()); + SetMap(caster->GetMap()); - WorldObject::_Create(guidlow, HIGHGUID_DYNAMICOBJECT, caster->GetMapId(), caster->GetPhaseMask()); Relocate(x, y, z, 0); if(!IsPositionValid()) @@ -83,6 +82,8 @@ bool DynamicObject::Create( uint32 guidlow, Unit *caster, uint32 spellId, uint32 return false; } + WorldObject::_Create(guidlow, HIGHGUID_DYNAMICOBJECT, caster->GetPhaseMask()); + SetEntry(spellId); SetFloatValue( OBJECT_FIELD_SCALE_X, 1 ); SetUInt64Value( DYNAMICOBJECT_CASTER, caster->GetGUID() ); diff --git a/src/game/FleeingMovementGenerator.cpp b/src/game/FleeingMovementGenerator.cpp index 2eddd2a128c..eef98b705cc 100644 --- a/src/game/FleeingMovementGenerator.cpp +++ b/src/game/FleeingMovementGenerator.cpp @@ -413,6 +413,7 @@ template bool FleeingMovementGenerator<Creature>::Update(Creature &, const uint3 void TimedFleeingMovementGenerator::Finalize(Unit &owner) { + owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING); owner.clearUnitState(UNIT_STAT_FLEEING | UNIT_STAT_ROAMING); if (Unit* victim = owner.getVictim()) { diff --git a/src/game/Formulas.h b/src/game/Formulas.h index 041c0621c74..2ec175f95be 100644 --- a/src/game/Formulas.h +++ b/src/game/Formulas.h @@ -117,7 +117,7 @@ namespace Trinity (((Creature*)u)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL) )) return 0; - uint32 xp_gain= BaseGain(pl->getLevel(), u->getLevel(), GetContentLevelsForMapAndZone(pl->GetMapId(),pl->GetZoneId())); + uint32 xp_gain= BaseGain(pl->getLevel(), u->getLevel(), GetContentLevelsForMapAndZone(u->GetMapId(),u->GetZoneId())); if( xp_gain == 0 ) return 0; diff --git a/src/game/GameEventMgr.cpp b/src/game/GameEventMgr.cpp index 0984c896eac..5b7aa737bc0 100644 --- a/src/game/GameEventMgr.cpp +++ b/src/game/GameEventMgr.cpp @@ -1215,7 +1215,7 @@ void GameEventMgr::GameEventSpawn(int16 event_id) if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size()) { - sLog.outError("GameEventMgr::GameEventSpawn attempt access to out of range mGameEventCreatureGuids element %i (size: %u)",internal_event_id,mGameEventCreatureGuids.size()); + sLog.outError("GameEventMgr::GameEventSpawn attempt access to out of range mGameEventCreatureGuids element %i (size: " SIZEFMTD ")",internal_event_id,mGameEventCreatureGuids.size()); return; } @@ -1248,7 +1248,7 @@ void GameEventMgr::GameEventSpawn(int16 event_id) if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size()) { - sLog.outError("GameEventMgr::GameEventSpawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %u)",internal_event_id,mGameEventGameobjectGuids.size()); + sLog.outError("GameEventMgr::GameEventSpawn attempt access to out of range mGameEventGameobjectGuids element %i (size: " SIZEFMTD ")",internal_event_id,mGameEventGameobjectGuids.size()); return; } @@ -1282,7 +1282,7 @@ void GameEventMgr::GameEventSpawn(int16 event_id) if(internal_event_id < 0 || internal_event_id >= mGameEventPoolIds.size()) { - sLog.outError("GameEventMgr::GameEventSpawn attempt access to out of range mGameEventPoolIds element %i (size: %u)",internal_event_id,mGameEventPoolIds.size()); + sLog.outError("GameEventMgr::GameEventSpawn attempt access to out of range mGameEventPoolIds element %i (size: " SIZEFMTD ")",internal_event_id,mGameEventPoolIds.size()); return; } @@ -1298,7 +1298,7 @@ void GameEventMgr::GameEventUnspawn(int16 event_id) if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size()) { - sLog.outError("GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventCreatureGuids element %i (size: %u)",internal_event_id,mGameEventCreatureGuids.size()); + sLog.outError("GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventCreatureGuids element %i (size: " SIZEFMTD ")",internal_event_id,mGameEventCreatureGuids.size()); return; } @@ -1313,16 +1313,13 @@ void GameEventMgr::GameEventUnspawn(int16 event_id) objmgr.RemoveCreatureFromGrid(*itr, data); if( Creature* pCreature = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(*itr, data->id, HIGHGUID_UNIT), (Creature*)NULL) ) - { - pCreature->CleanupsBeforeDelete(); pCreature->AddObjectToRemoveList(); - } } } if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size()) { - sLog.outError("GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %u)",internal_event_id,mGameEventGameobjectGuids.size()); + sLog.outError("GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventGameobjectGuids element %i (size: " SIZEFMTD ")",internal_event_id,mGameEventGameobjectGuids.size()); return; } @@ -1342,7 +1339,7 @@ void GameEventMgr::GameEventUnspawn(int16 event_id) } if(internal_event_id < 0 || internal_event_id >= mGameEventPoolIds.size()) { - sLog.outError("GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventPoolIds element %i (size: %u)",internal_event_id,mGameEventPoolIds.size()); + sLog.outError("GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventPoolIds element %i (size: " SIZEFMTD ")",internal_event_id,mGameEventPoolIds.size()); return; } diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp index d6aa162b50a..c3bfc1d77b9 100644 --- a/src/game/GameObject.cpp +++ b/src/game/GameObject.cpp @@ -54,7 +54,6 @@ GameObject::GameObject() : WorldObject(), m_goValue(new GameObjectValue) m_spawnedByDefault = true; m_usetimes = 0; m_spellId = 0; - m_charges = 5; m_cooldownTime = 0; m_goInfo = NULL; m_goData = NULL; @@ -65,28 +64,39 @@ GameObject::GameObject() : WorldObject(), m_goValue(new GameObjectValue) GameObject::~GameObject() { - /*if(m_uint32Values) // field array can be not exist if GameOBject not loaded + //if(m_uint32Values) // field array can be not exist if GameOBject not loaded + // CleanupsBeforeDelete(); +} + +void GameObject::CleanupsBeforeDelete() +{ + if(m_uint32Values) // field array can be not exist if GameOBject not loaded { // Possible crash at access to deleted GO in Unit::m_gameobj - uint64 owner_guid = GetOwnerGUID(); - if(owner_guid) + if(uint64 owner_guid = GetOwnerGUID()) { - Unit* owner = ObjectAccessor::GetUnit(*this,owner_guid); + Unit* owner = NULL; + // Object may be deleted while player is not in world, skip this check for now. + /*if(IS_PLAYER_GUID(owner_guid)) + owner = ObjectAccessor::GetObjectInWorld(owner_guid, (Player*)NULL); + else*/ + owner = ObjectAccessor::GetUnit(*this,owner_guid); + if(owner) owner->RemoveGameObject(this,false); else { - char * ownerType = "creature"; + const char * ownerType = "creature"; if(IS_PLAYER_GUID(owner_guid)) ownerType = "player"; else if(IS_PET_GUID(owner_guid)) ownerType = "pet"; sLog.outError("Delete GameObject (GUID: %u Entry: %u SpellId %u LinkedGO %u) that lost references to owner (GUID %u Type '%s') GO list. Crash possible later.", - GetGUIDLow(), GetGOInfo()->id, m_spellId, GetLinkedGameObjectEntry(), GUID_LOPART(owner_guid), ownerType); + GetGUIDLow(), GetGOInfo()->id, m_spellId, GetGOInfo()->GetLinkedGameObjectEntry(), GUID_LOPART(owner_guid), ownerType); } } - }*/ + } } void GameObject::AddToWorld() @@ -113,8 +123,7 @@ void GameObject::RemoveFromWorld() // Possible crash at access to deleted GO in Unit::m_gameobj if(uint64 owner_guid = GetOwnerGUID()) { - Unit* owner = ObjectAccessor::GetUnit(*this,owner_guid); - if(owner) + if(Unit * owner = GetOwner(false)) owner->RemoveGameObject(this,false); else if(!IS_PLAYER_GUID(owner_guid)) sLog.outError("Delete GameObject (GUID: %u Entry: %u ) that have references in not found creature %u GO list. Crash possible later.",GetGUIDLow(),GetGOInfo()->id,GUID_LOPART(owner_guid)); @@ -126,17 +135,18 @@ void GameObject::RemoveFromWorld() bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMask, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, GOState go_state, uint32 ArtKit) { - Relocate(x,y,z,ang); - SetMapId(map->GetId()); - SetInstanceId(map->GetInstanceId()); - SetPhaseMask(phaseMask,false); + ASSERT(map); + SetMap(map); + Relocate(x,y,z,ang); if(!IsPositionValid()) { sLog.outError("Gameobject (GUID: %u Entry: %u ) not created. Suggested coordinates isn't valid (X: %f Y: %f)",guidlow,name_id,x,y); return false; } + SetPhaseMask(phaseMask,false); + GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(name_id); if (!goinfo) { @@ -177,9 +187,6 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMa switch(goinfo->type) { - case GAMEOBJECT_TYPE_SPELLCASTER: - m_charges = goinfo->spellcaster.charges; - break; case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING: m_goValue->building.health = goinfo->building.intactNumHits + goinfo->building.damagedNumHits; break; @@ -298,87 +305,94 @@ void GameObject::Update(uint32 /*p_time*/) } } - // traps can have time and can not have - GameObjectInfo const* goInfo = GetGOInfo(); - if(goInfo->type == GAMEOBJECT_TYPE_TRAP) + if(isSpawned()) { - // traps - Unit* owner = GetOwner(); - Unit* ok = NULL; // pointer to appropriate target if found any + // traps can have time and can not have + GameObjectInfo const* goInfo = GetGOInfo(); + if(goInfo->type == GAMEOBJECT_TYPE_TRAP) + { + if(m_cooldownTime >= time(NULL)) + return; - if(m_cooldownTime >= time(NULL)) - return; + // traps + Unit* owner = GetOwner(); + Unit* ok = NULL; // pointer to appropriate target if found any - bool IsBattleGroundTrap = false; - //FIXME: this is activation radius (in different casting radius that must be selected from spell data) - //TODO: move activated state code (cast itself) to GO_ACTIVATED, in this place only check activating and set state - float radius = goInfo->trap.radius; - if(!radius) // i think this is a hack, spell radius is determined by trap radius (spell itself does not have radius) - if(const SpellEntry *spellEntry = sSpellStore.LookupEntry(m_spellId)) - radius = goInfo->trap.radius; - if(!radius) - { - if(goInfo->trap.cooldown == 3) // cast in other case (at some triggering/linked go/etc explicit call) + bool IsBattleGroundTrap = false; + //FIXME: this is activation radius (in different casting radius that must be selected from spell data) + //TODO: move activated state code (cast itself) to GO_ACTIVATED, in this place only check activating and set state + float radius = goInfo->trap.radius; + if(!radius) { - if(m_respawnTime > 0) - break; + if(goInfo->trap.cooldown != 3) // cast in other case (at some triggering/linked go/etc explicit call) + return; + else + { + if(m_respawnTime > 0) + break; + + radius = goInfo->trap.cooldown; // battlegrounds gameobjects has data2 == 0 && data5 == 3 + IsBattleGroundTrap = true; - radius = goInfo->trap.cooldown; // battlegrounds gameobjects has data2 == 0 && data5 == 3 - IsBattleGroundTrap = true; + if(!radius) + return; + } } - if(!radius) - return; - } - // Note: this hack with search required until GO casting not implemented - // search unfriendly creature - if(owner) // hunter trap - { - Trinity::AnyUnfriendlyNoTotemUnitInObjectRangeCheck checker(this, owner, radius); - Trinity::UnitSearcher<Trinity::AnyUnfriendlyNoTotemUnitInObjectRangeCheck> searcher(this, ok, checker); - VisitNearbyGridObject(radius, searcher); - if(!ok) VisitNearbyWorldObject(radius, searcher); - } - else // environmental trap - { - // environmental damage spells already have around enemies targeting but this not help in case not existed GO casting support - // affect only players - Player* player = NULL; - MaNGOS::AnyPlayerInObjectRangeCheck checker(this, radius); - MaNGOS::PlayerSearcher<MaNGOS::AnyPlayerInObjectRangeCheck> searcher(this, player, checker); - VisitNearbyWorldObject(radius, searcher); - ok = player; - } + // Note: this hack with search required until GO casting not implemented + // search unfriendly creature + if(owner) // hunter trap + { + Trinity::AnyUnfriendlyNoTotemUnitInObjectRangeCheck checker(this, owner, radius); + Trinity::UnitSearcher<Trinity::AnyUnfriendlyNoTotemUnitInObjectRangeCheck> searcher(this, ok, checker); + VisitNearbyGridObject(radius, searcher); + if(!ok) VisitNearbyWorldObject(radius, searcher); + } + else // environmental trap + { + // environmental damage spells already have around enemies targeting but this not help in case not existed GO casting support + // affect only players + Player* player = NULL; + MaNGOS::AnyPlayerInObjectRangeCheck checker(this, radius); + MaNGOS::PlayerSearcher<MaNGOS::AnyPlayerInObjectRangeCheck> searcher(this, player, checker); + VisitNearbyWorldObject(radius, searcher); + ok = player; + } + + if (ok) + { + // some traps do not have spell but should be triggered + if(goInfo->trap.spellId) + CastSpell(ok, goInfo->trap.spellId); - if (ok) - { - // some traps do not have spell but should be triggered - if(goInfo->trap.spellId) - CastSpell(ok, goInfo->trap.spellId); - //Unit *caster = owner ? owner : ok; - //caster->CastSpell(ok, goInfo->trap.spellId, true, 0, 0, GetGUID()); - - if(goInfo->trap.cooldown) - m_cooldownTime = time(NULL) + goInfo->trap.cooldown; - else m_cooldownTime = time(NULL) + 4; // 4 seconds - if(owner) - SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed + // count charges + //if(goInfo->trap.charges > 0) + // AddUse(); + + if(owner) + SetLootState(GO_JUST_DEACTIVATED); - if(IsBattleGroundTrap && ok->GetTypeId() == TYPEID_PLAYER) + if(IsBattleGroundTrap && ok->GetTypeId() == TYPEID_PLAYER) + { + //BattleGround gameobjects case + if(((Player*)ok)->InBattleGround()) + if(BattleGround *bg = ((Player*)ok)->GetBattleGround()) + bg->HandleTriggerBuff(GetGUID()); + } + } + } + else if(uint32 max_charges = goInfo->GetCharges()) + { + if (m_usetimes >= max_charges) { - //BattleGround gameobjects case - if(((Player*)ok)->InBattleGround()) - if(BattleGround *bg = ((Player*)ok)->GetBattleGround()) - bg->HandleTriggerBuff(GetGUID()); + m_usetimes = 0; + SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed } } } - if (m_charges && m_usetimes >= m_charges) - SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed - break; } case GO_ACTIVATED: @@ -387,7 +401,7 @@ void GameObject::Update(uint32 /*p_time*/) { case GAMEOBJECT_TYPE_DOOR: case GAMEOBJECT_TYPE_BUTTON: - if (GetAutoCloseTime() && (m_cooldownTime < time(NULL))) + if (GetGOInfo()->GetAutoCloseTime() && (m_cooldownTime < time(NULL))) ResetDoorOrButton(); break; } @@ -420,16 +434,17 @@ void GameObject::Update(uint32 /*p_time*/) if(GetOwnerGUID()) { - if(Unit* owner = GetOwner()) + if(Unit* owner = GetOwner(false)) + { owner->RemoveGameObject(this, false); - - SetRespawnTime(0); - Delete(); + SetRespawnTime(0); + Delete(); + } return; } //burning flags in some battlegrounds, if you find better condition, just add it - if (GetGoAnimProgress() > 0) + if (GetGOInfo()->IsDespawnAtAction() || GetGoAnimProgress() > 0) { SendObjectDeSpawnAnim(GetGUID()); //reset flags @@ -477,8 +492,18 @@ void GameObject::AddUniqueUse(Player* player) m_unique_users.insert(player->GetGUIDLow()); } +void GameObject::DeleteObjectWithOwner() +{ + SetLootState(GO_NOT_READY); + if (GetOwnerGUID()) + if (Unit * owner = GetOwner(false)) + owner->RemoveGameObject(this, false); + Delete(); +} + void GameObject::Delete() { + assert (!GetOwnerGUID()); SendObjectDeSpawnAnim(GetGUID()); SetGoState(GO_STATE_READY); @@ -609,7 +634,7 @@ bool GameObject::LoadFromDB(uint32 guid, Map *map) if (!Create(guid,entry, map, phaseMask, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state, ArtKit) ) return false; - if(!GetDespawnPossibility()) + if(!GetGOInfo()->GetDespawnPossibility() && !GetGOInfo()->IsDespawnAtAction()) { SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN); m_spawnedByDefault = true; @@ -657,22 +682,6 @@ GameObject* GameObject::GetGameObject(WorldObject& object, uint64 guid) return object.GetMap()->GetGameObject(guid); } -uint32 GameObject::GetLootId(GameObjectInfo const* ginfo) -{ - if (!ginfo) - return 0; - - switch(ginfo->type) - { - case GAMEOBJECT_TYPE_CHEST: - return ginfo->chest.lootId; - case GAMEOBJECT_TYPE_FISHINGHOLE: - return ginfo->fishinghole.lootId; - default: - return 0; - } -} - /*********************************************************/ /*** QUEST SYSTEM ***/ /*********************************************************/ @@ -706,9 +715,11 @@ bool GameObject::IsTransport() const return gInfo->type == GAMEOBJECT_TYPE_TRANSPORT || gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT; } -Unit* GameObject::GetOwner() const +Unit* GameObject::GetOwner(bool inWorld) const { - return ObjectAccessor::GetUnit(*this, GetOwnerGUID()); + if (inWorld) + return ObjectAccessor::GetUnit(*this, GetOwnerGUID()); + return ObjectAccessor::GetUnitInOrOutOfWorld(*this, GetOwnerGUID()); } void GameObject::SaveRespawnTime() @@ -788,7 +799,7 @@ bool GameObject::ActivateToQuest( Player *pTarget)const // scan GO chest with loot including quest items case GAMEOBJECT_TYPE_CHEST: { - if(LootTemplates_Gameobject.HaveQuestLootForPlayer(GetLootId(), pTarget)) + if(LootTemplates_Gameobject.HaveQuestLootForPlayer(GetGOInfo()->GetLootId(), pTarget)) { //TODO: fix this hack //look for battlegroundAV for some objects which are only activated after mine gots captured by own team @@ -894,7 +905,7 @@ void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = f return; if(!time_to_restore) - time_to_restore = GetAutoCloseTime(); + time_to_restore = GetGOInfo()->GetAutoCloseTime(); SwitchDoorOrButton(true,alternative); SetLootState(GO_ACTIVATED); @@ -938,7 +949,7 @@ void GameObject::Use(Unit* user) UseDoorOrButton(); // activate script - sWorld.ScriptsStart(sGameObjectScripts, GetDBTableGUIDLow(), spellCaster, this); + GetMap()->ScriptsStart(sGameObjectScripts, GetDBTableGUIDLow(), spellCaster, this); return; case GAMEOBJECT_TYPE_QUESTGIVER: //2 @@ -1034,7 +1045,7 @@ void GameObject::Use(Unit* user) player->CastedCreatureOrGO(info->id, GetGUID(), 0); if (info->goober.eventId) - sWorld.ScriptsStart(sEventScripts, info->goober.eventId, player, this); + GetMap()->ScriptsStart(sEventScripts, info->goober.eventId, player, this); } // cast this spell later if provided @@ -1057,7 +1068,7 @@ void GameObject::Use(Unit* user) player->SendCinematicStart(info->camera.cinematicId); if (info->camera.eventID) - sWorld.ScriptsStart(sEventScripts, info->camera.eventID, player, this); + GetMap()->ScriptsStart(sEventScripts, info->camera.eventID, player, this); return; } @@ -1358,6 +1369,7 @@ void GameObject::Use(Unit* user) } Spell *spell = new Spell(spellCaster, spellInfo, triggered); + //Spell *spell = new Spell(spellCaster, spellInfo, triggered,GetGUID()); // spell target is user of GO SpellCastTargets targets; diff --git a/src/game/GameObject.h b/src/game/GameObject.h index 43f8fb0ea5f..43441e2fb9c 100644 --- a/src/game/GameObject.h +++ b/src/game/GameObject.h @@ -399,6 +399,98 @@ struct GameObjectInfo } raw; }; uint32 ScriptId; + + // helpers + bool IsDespawnAtAction() const + { + switch(type) + { + case GAMEOBJECT_TYPE_CHEST: return chest.consumable; + case GAMEOBJECT_TYPE_GOOBER: return goober.consumable; + default: return false; + } + } + + uint32 GetLockId() const + { + switch(type) + { + case GAMEOBJECT_TYPE_DOOR: return door.lockId; + case GAMEOBJECT_TYPE_BUTTON: return button.lockId; + case GAMEOBJECT_TYPE_QUESTGIVER: return questgiver.lockId; + case GAMEOBJECT_TYPE_CHEST: return chest.lockId; + case GAMEOBJECT_TYPE_TRAP: return trap.lockId; + case GAMEOBJECT_TYPE_GOOBER: return goober.lockId; + case GAMEOBJECT_TYPE_AREADAMAGE: return areadamage.lockId; + case GAMEOBJECT_TYPE_CAMERA: return camera.lockId; + case GAMEOBJECT_TYPE_FLAGSTAND: return flagstand.lockId; + case GAMEOBJECT_TYPE_FISHINGHOLE:return fishinghole.lockId; + case GAMEOBJECT_TYPE_FLAGDROP: return flagdrop.lockId; + default: return 0; + } + } + + bool GetDespawnPossibility() const // despawn at targeting of cast? + { + switch(type) + { + case GAMEOBJECT_TYPE_DOOR: return door.noDamageImmune; + case GAMEOBJECT_TYPE_BUTTON: return button.noDamageImmune; + case GAMEOBJECT_TYPE_QUESTGIVER: return questgiver.noDamageImmune; + case GAMEOBJECT_TYPE_GOOBER: return goober.noDamageImmune; + case GAMEOBJECT_TYPE_FLAGSTAND: return flagstand.noDamageImmune; + case GAMEOBJECT_TYPE_FLAGDROP: return flagdrop.noDamageImmune; + default: return true; + } + } + + uint32 GetCharges() const // despawn at uses amount + { + switch(type) + { + //case GAMEOBJECT_TYPE_TRAP: return trap.charges; + case GAMEOBJECT_TYPE_GUARDPOST: return guardpost.charges; + case GAMEOBJECT_TYPE_SPELLCASTER: return spellcaster.charges; + default: return 0; + } + } + + uint32 GetLinkedGameObjectEntry() const + { + switch(type) + { + case GAMEOBJECT_TYPE_CHEST: return chest.linkedTrapId; + case GAMEOBJECT_TYPE_SPELL_FOCUS: return spellFocus.linkedTrapId; + case GAMEOBJECT_TYPE_GOOBER: return goober.linkedTrapId; + default: return 0; + } + } + + uint32 GetAutoCloseTime() const + { + uint32 autoCloseTime = 0; + switch(type) + { + case GAMEOBJECT_TYPE_DOOR: autoCloseTime = door.autoCloseTime; break; + case GAMEOBJECT_TYPE_BUTTON: autoCloseTime = button.autoCloseTime; break; + case GAMEOBJECT_TYPE_TRAP: autoCloseTime = trap.autoCloseTime; break; + case GAMEOBJECT_TYPE_GOOBER: autoCloseTime = goober.autoCloseTime; break; + case GAMEOBJECT_TYPE_TRANSPORT: autoCloseTime = transport.autoCloseTime; break; + case GAMEOBJECT_TYPE_AREADAMAGE: autoCloseTime = areadamage.autoCloseTime; break; + default: break; + } + return autoCloseTime / 0x10000; + } + + uint32 GetLootId() const + { + switch(type) + { + case GAMEOBJECT_TYPE_CHEST: return chest.lootId; + case GAMEOBJECT_TYPE_FISHINGHOLE: return fishinghole.lootId; + default: return 0; + } + } }; class OPvPCapturePoint; @@ -488,6 +580,7 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject void AddToWorld(); void RemoveFromWorld(); + void CleanupsBeforeDelete(); bool Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMask, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, GOState go_state, uint32 ArtKit = 0); void Update(uint32 p_time); @@ -498,14 +591,6 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject bool IsTransport() const; - void SetOwnerGUID(uint64 owner) - { - m_spawnedByDefault = false; // all object with owner is despawned after delay - SetUInt64Value(OBJECT_FIELD_CREATED_BY, owner); - } - uint64 GetOwnerGUID() const { return GetUInt64Value(OBJECT_FIELD_CREATED_BY); } - Unit* GetOwner() const; - uint32 GetDBTableGUIDLow() const { return m_DBTableGuid; } void UpdateRotationFields(float rotation2 = 0.0f, float rotation3 = 0.0f); @@ -523,40 +608,26 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask); bool LoadFromDB(uint32 guid, Map *map); void DeleteFromDB(); - static uint32 GetLootId(GameObjectInfo const* info); - uint32 GetLootId() const { return GetLootId(GetGOInfo()); } - uint32 GetLockId() const + + void SetOwnerGUID(uint64 owner) { - switch(GetGoType()) + // Owner already found and different than expected owner - remove object from old owner + if (owner && GetOwnerGUID() && GetOwnerGUID() != owner) { - case GAMEOBJECT_TYPE_DOOR: return GetGOInfo()->door.lockId; - case GAMEOBJECT_TYPE_BUTTON: return GetGOInfo()->button.lockId; - case GAMEOBJECT_TYPE_QUESTGIVER: return GetGOInfo()->questgiver.lockId; - case GAMEOBJECT_TYPE_CHEST: return GetGOInfo()->chest.lockId; - case GAMEOBJECT_TYPE_TRAP: return GetGOInfo()->trap.lockId; - case GAMEOBJECT_TYPE_GOOBER: return GetGOInfo()->goober.lockId; - case GAMEOBJECT_TYPE_AREADAMAGE: return GetGOInfo()->areadamage.lockId; - case GAMEOBJECT_TYPE_CAMERA: return GetGOInfo()->camera.lockId; - case GAMEOBJECT_TYPE_FLAGSTAND: return GetGOInfo()->flagstand.lockId; - case GAMEOBJECT_TYPE_FISHINGHOLE:return GetGOInfo()->fishinghole.lockId; - case GAMEOBJECT_TYPE_FLAGDROP: return GetGOInfo()->flagdrop.lockId; - default: return 0; + assert(false); } + m_spawnedByDefault = false; // all object with owner is despawned after delay + SetUInt64Value(OBJECT_FIELD_CREATED_BY, owner); } + uint64 GetOwnerGUID() const { return GetUInt64Value(OBJECT_FIELD_CREATED_BY); } + Unit* GetOwner(bool inWorld = true) const; - bool GetDespawnPossibility() const + void SetSpellId(uint32 id) { - switch(GetGoType()) - { - case GAMEOBJECT_TYPE_DOOR: return GetGOInfo()->door.noDamageImmune; - case GAMEOBJECT_TYPE_BUTTON: return GetGOInfo()->button.noDamageImmune; - case GAMEOBJECT_TYPE_QUESTGIVER: return GetGOInfo()->questgiver.noDamageImmune; - case GAMEOBJECT_TYPE_GOOBER: return GetGOInfo()->goober.noDamageImmune; - case GAMEOBJECT_TYPE_FLAGSTAND: return GetGOInfo()->flagstand.noDamageImmune; - case GAMEOBJECT_TYPE_FLAGDROP: return GetGOInfo()->flagdrop.noDamageImmune; - default: return true; - } + m_spawnedByDefault = false; // all summoned object is despawned after delay + m_spellId = id; } + uint32 GetSpellId() const { return m_spellId;} time_t GetRespawnTime() const { return m_respawnTime; } time_t GetRespawnTimeEx() const @@ -585,8 +656,7 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject uint32 GetRespawnDelay() const { return m_respawnDelayTime; } void Refresh(); void Delete(); - void SetSpellId(uint32 id) { m_spellId = id;} - uint32 GetSpellId() const { return m_spellId;} + void DeleteObjectWithOwner(); void getFishLoot(Loot *loot, Player* loot_owner); GameobjectTypes GetGoType() const { return GameobjectTypes(GetByteValue(GAMEOBJECT_BYTES_1, 1)); } void SetGoType(GameobjectTypes type) { SetByteValue(GAMEOBJECT_BYTES_1, 1, type); } @@ -627,34 +697,7 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject void UseDoorOrButton(uint32 time_to_restore = 0, bool alternative = false); // 0 = use `gameobject`.`spawntimesecs` void ResetDoorOrButton(); - // 0 = use `gameobject`.`spawntimesecs` - - uint32 GetLinkedGameObjectEntry() const - { - switch(GetGoType()) - { - case GAMEOBJECT_TYPE_CHEST: return GetGOInfo()->chest.linkedTrapId; - case GAMEOBJECT_TYPE_SPELL_FOCUS: return GetGOInfo()->spellFocus.linkedTrapId; - case GAMEOBJECT_TYPE_GOOBER: return GetGOInfo()->goober.linkedTrapId; - default: return 0; - } - } - uint32 GetAutoCloseTime() const - { - uint32 autoCloseTime = 0; - switch(GetGoType()) - { - case GAMEOBJECT_TYPE_DOOR: autoCloseTime = GetGOInfo()->door.autoCloseTime; break; - case GAMEOBJECT_TYPE_BUTTON: autoCloseTime = GetGOInfo()->button.autoCloseTime; break; - case GAMEOBJECT_TYPE_TRAP: autoCloseTime = GetGOInfo()->trap.autoCloseTime; break; - case GAMEOBJECT_TYPE_GOOBER: autoCloseTime = GetGOInfo()->goober.autoCloseTime; break; - case GAMEOBJECT_TYPE_TRANSPORT: autoCloseTime = GetGOInfo()->transport.autoCloseTime; break; - case GAMEOBJECT_TYPE_AREADAMAGE: autoCloseTime = GetGOInfo()->areadamage.autoCloseTime; break; - default: break; - } - return autoCloseTime / 0x10000; - } void TriggeringLinkedGameObject( uint32 trapEntry, Unit* target); @@ -675,7 +718,6 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject uint64 GetRotation() const { return m_rotation; } protected: - uint32 m_charges; // Spell charges for GAMEOBJECT_TYPE_SPELLCASTER (22) uint32 m_spellId; time_t m_respawnTime; // (secs) time of next respawn (or despawn if GO have owner()), uint32 m_respawnDelayTime; // (secs) if 0 then current GO state no dependent from timer diff --git a/src/game/GridDefines.h b/src/game/GridDefines.h index a3fe011f402..05080521f58 100644 --- a/src/game/GridDefines.h +++ b/src/game/GridDefines.h @@ -35,10 +35,8 @@ class Player; #ifdef LARGE_CELL #define MAX_NUMBER_OF_CELLS 4 -#define CENTER_GRID_CELL_ID 128 #else #define MAX_NUMBER_OF_CELLS 8 -#define CENTER_GRID_CELL_ID 256 #endif #define MAX_NUMBER_OF_GRIDS 64 @@ -53,6 +51,7 @@ class Player; #define SIZE_OF_GRID_CELL (SIZE_OF_GRIDS/MAX_NUMBER_OF_CELLS) +#define CENTER_GRID_CELL_ID (MAX_NUMBER_OF_CELLS*MAX_NUMBER_OF_GRIDS/2) #define CENTER_GRID_CELL_OFFSET (SIZE_OF_GRID_CELL/2) #define TOTAL_NUMBER_OF_CELLS_PER_MAP (MAX_NUMBER_OF_GRIDS*MAX_NUMBER_OF_CELLS) @@ -73,7 +72,7 @@ typedef GridRefManager<GameObject> GameObjectMapType; typedef GridRefManager<Player> PlayerMapType; typedef Grid<Player, AllWorldObjectTypes,AllGridObjectTypes> GridType; -typedef NGrid<8, Player, AllWorldObjectTypes, AllGridObjectTypes> NGridType; +typedef NGrid<MAX_NUMBER_OF_CELLS, Player, AllWorldObjectTypes, AllGridObjectTypes> NGridType; typedef TypeMapContainer<AllGridObjectTypes> GridTypeMapContainer; typedef TypeMapContainer<AllWorldObjectTypes> WorldTypeMapContainer; diff --git a/src/game/GridNotifiers.h b/src/game/GridNotifiers.h index 64deb0d3123..997f0806369 100644 --- a/src/game/GridNotifiers.h +++ b/src/game/GridNotifiers.h @@ -1135,48 +1135,69 @@ namespace Trinity Unit const* pUnit; }; - class AllGameObjectsWithEntryInGrid + class AllGameObjectsWithEntryInRange { public: - AllGameObjectsWithEntryInGrid(uint32 ent) : entry(ent) {} - bool operator() (GameObject* g) + AllGameObjectsWithEntryInRange(const WorldObject* pObject, uint32 uiEntry, float fMaxRange) : m_pObject(pObject), m_uiEntry(uiEntry), m_fRange(fMaxRange) {} + bool operator() (GameObject* pGo) { - if(g->GetEntry() == entry) + if (pGo->GetEntry() == m_uiEntry && m_pObject->IsWithinDist(pGo,m_fRange,false)) return true; return false; } private: - uint32 entry; + const WorldObject* m_pObject; + uint32 m_uiEntry; + float m_fRange; }; - class GameObjectInRangeCheck + class AllCreaturesOfEntryInRange { - public: - GameObjectInRangeCheck(float _x, float _y, float _z, float _range) : x(_x), y(_y), z(_z), range(_range) {} - bool operator() (GameObject* go) - { - return go->IsInRange(x, y, z, range); - } - private: - float x, y, z, range; + public: + AllCreaturesOfEntryInRange(const WorldObject* pObject, uint32 uiEntry, float fMaxRange) : m_pObject(pObject), m_uiEntry(uiEntry), m_fRange(fMaxRange) {} + bool operator() (Unit* pUnit) + { + if (pUnit->GetEntry() == m_uiEntry && m_pObject->IsWithinDist(pUnit,m_fRange,false)) + return true; + + return false; + } + + private: + const WorldObject* m_pObject; + uint32 m_uiEntry; + float m_fRange; }; - class AllCreaturesOfEntryInRange + class PlayerAtMinimumRangeAway { public: - AllCreaturesOfEntryInRange(Unit const* obj, uint32 ent, float ran) : pUnit(obj), entry(ent), range(ran) {} - bool operator() (Unit* u) + PlayerAtMinimumRangeAway(Unit const* unit, float fMinRange) : pUnit(unit), fRange(fMinRange) {} + bool operator() (Player* pPlayer) { - if(u->GetEntry() == entry && pUnit->IsWithinDistInMap(u, range)) + //No threat list check, must be done explicit if expected to be in combat with creature + if (!pPlayer->isGameMaster() && pPlayer->isAlive() && !pUnit->IsWithinDist(pPlayer,fRange,false)) return true; return false; } + private: Unit const* pUnit; - uint32 entry; - float range; + float fRange; + }; + + class GameObjectInRangeCheck + { + public: + GameObjectInRangeCheck(float _x, float _y, float _z, float _range) : x(_x), y(_y), z(_z), range(_range) {} + bool operator() (GameObject* go) + { + return go->IsInRange(x, y, z, range); + } + private: + float x, y, z, range; }; // Player checks and do diff --git a/src/game/GridNotifiersImpl.h b/src/game/GridNotifiersImpl.h index 6502c2ea7c8..211bca9cd3c 100644 --- a/src/game/GridNotifiersImpl.h +++ b/src/game/GridNotifiersImpl.h @@ -631,4 +631,28 @@ void MaNGOS::LocalizedPacketListDo<Builder>::operator()( Player* p ) p->SendDirectMessage((*data_list)[i]); } +struct ObjectDistanceOrder : public std::binary_function<const WorldObject, const WorldObject, bool> +{ + const Unit* m_pSource; + + ObjectDistanceOrder(const Unit* pSource) : m_pSource(pSource) {}; + + bool operator()(const WorldObject* pLeft, const WorldObject* pRight) const + { + return m_pSource->GetDistanceOrder(pLeft, pRight); + } +}; + +struct ObjectDistanceOrderReversed : public std::binary_function<const WorldObject, const WorldObject, bool> +{ + const Unit* m_pSource; + + ObjectDistanceOrderReversed(const Unit* pSource) : m_pSource(pSource) {}; + + bool operator()(const WorldObject* pLeft, const WorldObject* pRight) const + { + return !m_pSource->GetDistanceOrder(pLeft, pRight); + } +}; + #endif // MANGOS_GRIDNOTIFIERSIMPL_H diff --git a/src/game/Group.cpp b/src/game/Group.cpp index c08d1236c32..8a02afa25d2 100644 --- a/src/game/Group.cpp +++ b/src/game/Group.cpp @@ -967,7 +967,7 @@ void Group::SendUpdate() void Group::UpdatePlayerOutOfRange(Player* pPlayer) { - if(!pPlayer) + if(!pPlayer || !pPlayer->IsInWorld()) return; Player *player; @@ -1510,7 +1510,7 @@ void Group::ResetInstances(uint8 method, Player* SendMsgTo) bool isEmpty = true; // if the map is loaded, reset it Map *map = MapManager::Instance().FindMap(p->GetMapId(), p->GetInstanceId()); - if(map && map->IsDungeon()) + if(map && map->IsDungeon() && !(method == INSTANCE_RESET_GROUP_DISBAND && !p->CanReset())) { if(p->CanReset()) isEmpty = ((InstanceMap*)map)->Reset(method); diff --git a/src/game/Guild.cpp b/src/game/Guild.cpp index 84271f08d04..7efc92c2fe0 100644 --- a/src/game/Guild.cpp +++ b/src/game/Guild.cpp @@ -283,7 +283,7 @@ bool Guild::LoadRanksFromDB(uint32 GuildId) std::string name = m_ranks[i].name; uint32 rights = m_ranks[i].rights; CharacterDatabase.escape_string(name); - CharacterDatabase.PExecute( "INSERT INTO guild_rank (guildid,rid,rname,rights) VALUES ('%u', '%u', '%s', '%u')", GuildId, i+1, name.c_str(), rights); + CharacterDatabase.PExecute( "INSERT INTO guild_rank (guildid,rid,rname,rights) VALUES ('%u', '%u', '%s', '%u')", GuildId, uint32(i+1), name.c_str(), rights); } CharacterDatabase.CommitTransaction(); } @@ -369,20 +369,17 @@ bool Guild::FillPlayerData(uint64 guid, MemberSlot* memslot) } else { - QueryResult *result = CharacterDatabase.PQuery("SELECT name,data,zone,class FROM characters WHERE guid = '%u'", GUID_LOPART(guid)); - if(!result) - return false; // player doesn't exist + QueryResult *result = CharacterDatabase.PQuery("SELECT name,level,zone,class FROM characters WHERE guid = '%u'", GUID_LOPART(guid)); + if(!result) + return false; // player doesn't exist Field *fields = result->Fetch(); - plName = fields[0].GetCppString(); - - Tokens data = StrSplit(fields[1].GetCppString(), " "); - plLevel = Player::GetUInt32ValueFromArray(data,UNIT_FIELD_LEVEL); - - plZone = fields[2].GetUInt32(); - plClass = fields[3].GetUInt32(); - delete result; + plName = fields[0].GetCppString(); + plLevel = fields[1].GetUInt32(); + plZone = fields[2].GetUInt32(); + plClass = fields[3].GetUInt32(); + delete result; if(plLevel<1||plLevel>STRONG_MAX_LEVEL) // can be at broken `data` field { @@ -716,40 +713,40 @@ void Guild::Roster(WorldSession *session) data << (uint32)m_ranks.size(); for (RankList::const_iterator ritr = m_ranks.begin(); ritr != m_ranks.end(); ++ritr) { - data << (uint32)ritr->rights; - data << (uint32)ritr->BankMoneyPerDay; // count of: withdraw gold(gold/day) Note: in game set gold, in packet set bronze. - for (uint8 i = 0; i < GUILD_BANK_MAX_TABS; ++i) + data << uint32(ritr->rights); + data << uint32(ritr->BankMoneyPerDay); // count of: withdraw gold(gold/day) Note: in game set gold, in packet set bronze. + for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i) { - data << (uint32)ritr->TabRight[i]; // for TAB_i rights: view tabs = 0x01, deposit items =0x02 - data << (uint32)ritr->TabSlotPerDay[i]; // for TAB_i count of: withdraw items(stack/day) + data << uint32(ritr->TabRight[i]); // for TAB_i rights: view tabs = 0x01, deposit items =0x02 + data << uint32(ritr->TabSlotPerDay[i]); // for TAB_i count of: withdraw items(stack/day) } } for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr) { if (Player *pl = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER))) { - data << (uint64)pl->GetGUID(); - data << (uint8)1; - data << (std::string)pl->GetName(); - data << (uint32)itr->second.RankId; - data << (uint8)pl->getLevel(); - data << (uint8)pl->getClass(); - data << (uint8)0; // new 2.4.0 - data << (uint32)pl->GetZoneId(); + data << uint64(pl->GetGUID()); + data << uint8(1); + data << pl->GetName(); + data << uint32(itr->second.RankId); + data << uint8(pl->getLevel()); + data << uint8(pl->getClass()); + data << uint8(0); // new 2.4.0 + data << uint32(pl->GetZoneId()); data << itr->second.Pnote; data << itr->second.OFFnote; } else { data << uint64(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER)); - data << (uint8)0; + data << uint8(0); data << itr->second.name; - data << (uint32)itr->second.RankId; - data << (uint8)itr->second.level; - data << (uint8)itr->second.Class; - data << (uint8)0; // new 2.4.0 - data << (uint32)itr->second.zoneId; - data << (float(time(NULL)-itr->second.logout_time) / DAY); + data << uint32(itr->second.RankId); + data << uint8(itr->second.level); + data << uint8(itr->second.Class); + data << uint8(0); // new 2.4.0 + data << uint32(itr->second.zoneId); + data << float(float(time(NULL)-itr->second.logout_time) / DAY); data << itr->second.Pnote; data << itr->second.OFFnote; } @@ -829,17 +826,17 @@ void Guild::DisplayGuildEventlog(WorldSession *session) for (GuildEventlog::const_iterator itr = m_GuildEventlog.begin(); itr != m_GuildEventlog.end(); ++itr) { // Event type - data << uint8((*itr)->EventType); + data << uint8(itr->EventType); // Player 1 - data << uint64((*itr)->PlayerGuid1); + data << uint64(itr->PlayerGuid1); // Player 2 not for left/join guild events - if( (*itr)->EventType != GUILD_EVENT_LOG_JOIN_GUILD && (*itr)->EventType != GUILD_EVENT_LOG_LEAVE_GUILD ) - data << uint64((*itr)->PlayerGuid2); + if (itr->EventType != GUILD_EVENT_LOG_JOIN_GUILD && itr->EventType != GUILD_EVENT_LOG_LEAVE_GUILD) + data << uint64(itr->PlayerGuid2); // New Rank - only for promote/demote guild events - if( (*itr)->EventType == GUILD_EVENT_LOG_PROMOTE_PLAYER || (*itr)->EventType == GUILD_EVENT_LOG_DEMOTE_PLAYER ) - data << uint8((*itr)->NewRank); + if (itr->EventType == GUILD_EVENT_LOG_PROMOTE_PLAYER || itr->EventType == GUILD_EVENT_LOG_DEMOTE_PLAYER) + data << uint8(itr->NewRank); // Event timestamp - data << uint32(time(NULL)-(*itr)->TimeStamp); + data << uint32(time(NULL)-itr->TimeStamp); } session->SendPacket(&data); sLog.outDebug("WORLD: Sent (MSG_GUILD_EVENT_LOG_QUERY)"); @@ -858,14 +855,14 @@ void Guild::LoadGuildEventLogFromDB() do { Field *fields = result->Fetch(); - GuildEventlogEntry *NewEvent = new GuildEventlogEntry; + GuildEventlogEntry NewEvent; // Fill entry - NewEvent->LogGuid = fields[0].GetUInt32(); - NewEvent->EventType = fields[1].GetUInt8(); - NewEvent->PlayerGuid1 = fields[2].GetUInt32(); - NewEvent->PlayerGuid2 = fields[3].GetUInt32(); - NewEvent->NewRank = fields[4].GetUInt8(); - NewEvent->TimeStamp = fields[5].GetUInt64(); + NewEvent.LogGuid = fields[0].GetUInt32(); + NewEvent.EventType = fields[1].GetUInt8(); + NewEvent.PlayerGuid1 = fields[2].GetUInt32(); + NewEvent.PlayerGuid2 = fields[3].GetUInt32(); + NewEvent.NewRank = fields[4].GetUInt8(); + NewEvent.TimeStamp = fields[5].GetUInt64(); // Add entry to map m_GuildEventlog.push_front(NewEvent); @@ -875,9 +872,8 @@ void Guild::LoadGuildEventLogFromDB() // Check lists size in case to many event entries in db // This cases can happen only if a crash occured somewhere and table has too many log entries if (!m_GuildEventlog.empty()) - { - CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid=%u AND LogGuid < %u", Id, m_GuildEventlog.front()->LogGuid); - } + CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid=%u AND LogGuid < %u", Id, m_GuildEventlog.front().LogGuid); + m_eventlogloaded = true; } @@ -886,16 +882,8 @@ void Guild::UnloadGuildEventlog() { if (!m_eventlogloaded) return; - GuildEventlogEntry *EventLogEntry; - if( !m_GuildEventlog.empty() ) - { - do - { - EventLogEntry = *(m_GuildEventlog.begin()); - m_GuildEventlog.pop_front(); - delete EventLogEntry; - }while( !m_GuildEventlog.empty() ); - } + + m_GuildEventlog.clear(); m_eventlogloaded = false; } @@ -915,27 +903,25 @@ void Guild::RenumGuildEventlog() // Add entry to guild eventlog void Guild::LogGuildEvent(uint8 EventType, uint32 PlayerGuid1, uint32 PlayerGuid2, uint8 NewRank) { - GuildEventlogEntry *NewEvent = new GuildEventlogEntry; + GuildEventlogEntry NewEvent; // Fill entry - NewEvent->LogGuid = GuildEventlogMaxGuid++; - NewEvent->EventType = EventType; - NewEvent->PlayerGuid1 = PlayerGuid1; - NewEvent->PlayerGuid2 = PlayerGuid2; - NewEvent->NewRank = NewRank; - NewEvent->TimeStamp = uint32(time(NULL)); + NewEvent.LogGuid = GuildEventlogMaxGuid++; + NewEvent.EventType = EventType; + NewEvent.PlayerGuid1 = PlayerGuid1; + NewEvent.PlayerGuid2 = PlayerGuid2; + NewEvent.NewRank = NewRank; + NewEvent.TimeStamp = uint32(time(NULL)); // Check max entry limit and delete from db if needed if (m_GuildEventlog.size() > GUILD_EVENTLOG_MAX_ENTRIES) { - GuildEventlogEntry *OldEvent = *(m_GuildEventlog.begin()); + CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid='%u' AND LogGuid='%u'", Id, m_GuildEventlog.front().LogGuid); m_GuildEventlog.pop_front(); - CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid='%u' AND LogGuid='%u'", Id, OldEvent->LogGuid); - delete OldEvent; } // Add entry to map m_GuildEventlog.push_back(NewEvent); // Add new eventlog entry into DB CharacterDatabase.PExecute("INSERT INTO guild_eventlog (guildid, LogGuid, EventType, PlayerGuid1, PlayerGuid2, NewRank, TimeStamp) VALUES ('%u','%u','%u','%u','%u','%u','" UI64FMTD "')", - Id, NewEvent->LogGuid, uint32(NewEvent->EventType), NewEvent->PlayerGuid1, NewEvent->PlayerGuid2, uint32(NewEvent->NewRank), NewEvent->TimeStamp); + Id, NewEvent.LogGuid, uint32(NewEvent.EventType), NewEvent.PlayerGuid1, NewEvent.PlayerGuid2, uint32(NewEvent.NewRank), NewEvent.TimeStamp); } // ************************************************* @@ -1507,30 +1493,28 @@ void Guild::LoadGuildBankEventLogFromDB() do { Field *fields = result->Fetch(); - GuildBankEvent *NewEvent = new GuildBankEvent; + GuildBankEvent NewEvent; - NewEvent->LogGuid = fields[0].GetUInt32(); - NewEvent->LogEntry = fields[1].GetUInt8(); + NewEvent.LogGuid = fields[0].GetUInt32(); + NewEvent.LogEntry = fields[1].GetUInt8(); uint8 TabId = fields[2].GetUInt8(); - NewEvent->PlayerGuid = fields[3].GetUInt32(); - NewEvent->ItemOrMoney = fields[4].GetUInt32(); - NewEvent->ItemStackCount = fields[5].GetUInt8(); - NewEvent->DestTabId = fields[6].GetUInt8(); - NewEvent->TimeStamp = fields[7].GetUInt64(); + NewEvent.PlayerGuid = fields[3].GetUInt32(); + NewEvent.ItemOrMoney = fields[4].GetUInt32(); + NewEvent.ItemStackCount = fields[5].GetUInt8(); + NewEvent.DestTabId = fields[6].GetUInt8(); + NewEvent.TimeStamp = fields[7].GetUInt64(); if (TabId >= GUILD_BANK_MAX_TABS) { - sLog.outError( "Guild::LoadGuildBankEventLogFromDB: Invalid tabid '%u' for guild bank log entry (guild: '%s', LogGuid: %u), skipped.", TabId, GetName().c_str(), NewEvent->LogGuid); - delete NewEvent; + sLog.outError( "Guild::LoadGuildBankEventLogFromDB: Invalid tabid '%u' for guild bank log entry (guild: '%s', LogGuid: %u), skipped.", TabId, GetName().c_str(), NewEvent.LogGuid); continue; } - if (NewEvent->isMoneyEvent() && m_GuildBankEventLog_Money.size() >= GUILD_BANK_MAX_LOGS - || m_GuildBankEventLog_Item[TabId].size() >= GUILD_BANK_MAX_LOGS) - { - delete NewEvent; + + if (NewEvent.isMoneyEvent() && m_GuildBankEventLog_Money.size() >= GUILD_BANK_MAX_LOGS || + m_GuildBankEventLog_Item[TabId].size() >= GUILD_BANK_MAX_LOGS) continue; - } - if (NewEvent->isMoneyEvent()) + + if (NewEvent.isMoneyEvent()) m_GuildBankEventLog_Money.push_front(NewEvent); else m_GuildBankEventLog_Item[TabId].push_front(NewEvent); @@ -1543,43 +1527,24 @@ void Guild::LoadGuildBankEventLogFromDB() if (!m_GuildBankEventLog_Money.empty()) { CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid=%u AND LogGuid < %u", - Id, m_GuildBankEventLog_Money.front()->LogGuid); + Id, m_GuildBankEventLog_Money.front().LogGuid); } for (uint8 i = 0; i < GUILD_BANK_MAX_TABS; ++i) { if (!m_GuildBankEventLog_Item[i].empty()) { CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid=%u AND LogGuid < %u", - Id, m_GuildBankEventLog_Item[i].front()->LogGuid); + Id, m_GuildBankEventLog_Item[i].front().LogGuid); } } } void Guild::UnloadGuildBankEventLog() { - GuildBankEvent *EventLogEntry; - if( !m_GuildBankEventLog_Money.empty() ) - { - do - { - EventLogEntry = *(m_GuildBankEventLog_Money.begin()); - m_GuildBankEventLog_Money.pop_front(); - delete EventLogEntry; - }while( !m_GuildBankEventLog_Money.empty() ); - } + m_GuildBankEventLog_Money.clear(); - for (uint8 i = 0; i < GUILD_BANK_MAX_TABS; ++i) - { - if( !m_GuildBankEventLog_Item[i].empty() ) - { - do - { - EventLogEntry = *(m_GuildBankEventLog_Item[i].begin()); - m_GuildBankEventLog_Item[i].pop_front(); - delete EventLogEntry; - }while( !m_GuildBankEventLog_Item[i].empty() ); - } - } + for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i) + m_GuildBankEventLog_Item[i].clear(); } void Guild::DisplayGuildBankLogs(WorldSession *session, uint8 TabId) @@ -1595,24 +1560,24 @@ void Guild::DisplayGuildBankLogs(WorldSession *session, uint8 TabId) data << uint8(m_GuildBankEventLog_Money.size()); // number of log entries for (GuildBankEventLog::const_iterator itr = m_GuildBankEventLog_Money.begin(); itr != m_GuildBankEventLog_Money.end(); ++itr) { - data << uint8((*itr)->LogEntry); - data << uint64(MAKE_NEW_GUID((*itr)->PlayerGuid,0,HIGHGUID_PLAYER)); - if ((*itr)->LogEntry == GUILD_BANK_LOG_DEPOSIT_MONEY || - (*itr)->LogEntry == GUILD_BANK_LOG_WITHDRAW_MONEY || - (*itr)->LogEntry == GUILD_BANK_LOG_REPAIR_MONEY || - (*itr)->LogEntry == GUILD_BANK_LOG_UNK1 || - (*itr)->LogEntry == GUILD_BANK_LOG_UNK2) + data << uint8(itr->LogEntry); + data << uint64(MAKE_NEW_GUID(itr->PlayerGuid,0,HIGHGUID_PLAYER)); + if (itr->LogEntry == GUILD_BANK_LOG_DEPOSIT_MONEY || + itr->LogEntry == GUILD_BANK_LOG_WITHDRAW_MONEY || + itr->LogEntry == GUILD_BANK_LOG_REPAIR_MONEY || + itr->LogEntry == GUILD_BANK_LOG_UNK1 || + itr->LogEntry == GUILD_BANK_LOG_UNK2) { - data << uint32((*itr)->ItemOrMoney); + data << uint32(itr->ItemOrMoney); } else { - data << uint32((*itr)->ItemOrMoney); - data << uint32((*itr)->ItemStackCount); - if ((*itr)->LogEntry == GUILD_BANK_LOG_MOVE_ITEM || (*itr)->LogEntry == GUILD_BANK_LOG_MOVE_ITEM2) - data << uint8((*itr)->DestTabId); // moved tab + data << uint32(itr->ItemOrMoney); + data << uint32(itr->ItemStackCount); + if (itr->LogEntry == GUILD_BANK_LOG_MOVE_ITEM || itr->LogEntry == GUILD_BANK_LOG_MOVE_ITEM2) + data << uint8(itr->DestTabId); // moved tab } - data << uint32(time(NULL)-(*itr)->TimeStamp); + data << uint32(time(NULL) - itr->TimeStamp); } session->SendPacket(&data); } @@ -1625,24 +1590,24 @@ void Guild::DisplayGuildBankLogs(WorldSession *session, uint8 TabId) data << uint8(m_GuildBankEventLog_Item[TabId].size()); for (GuildBankEventLog::const_iterator itr = m_GuildBankEventLog_Item[TabId].begin(); itr != m_GuildBankEventLog_Item[TabId].end(); ++itr) { - data << uint8((*itr)->LogEntry); - data << uint64(MAKE_NEW_GUID((*itr)->PlayerGuid,0,HIGHGUID_PLAYER)); - if ((*itr)->LogEntry == GUILD_BANK_LOG_DEPOSIT_MONEY || - (*itr)->LogEntry == GUILD_BANK_LOG_WITHDRAW_MONEY || - (*itr)->LogEntry == GUILD_BANK_LOG_REPAIR_MONEY || - (*itr)->LogEntry == GUILD_BANK_LOG_UNK1 || - (*itr)->LogEntry == GUILD_BANK_LOG_UNK2) + data << uint8(itr->LogEntry); + data << uint64(MAKE_NEW_GUID(itr->PlayerGuid,0,HIGHGUID_PLAYER)); + if (itr->LogEntry == GUILD_BANK_LOG_DEPOSIT_MONEY || + itr->LogEntry == GUILD_BANK_LOG_WITHDRAW_MONEY || + itr->LogEntry == GUILD_BANK_LOG_REPAIR_MONEY || + itr->LogEntry == GUILD_BANK_LOG_UNK1 || + itr->LogEntry == GUILD_BANK_LOG_UNK2) { - data << uint32((*itr)->ItemOrMoney); + data << uint32(itr->ItemOrMoney); } else { - data << uint32((*itr)->ItemOrMoney); - data << uint32((*itr)->ItemStackCount); - if ((*itr)->LogEntry == GUILD_BANK_LOG_MOVE_ITEM || (*itr)->LogEntry == GUILD_BANK_LOG_MOVE_ITEM2) - data << uint8((*itr)->DestTabId); // moved tab + data << uint32(itr->ItemOrMoney); + data << uint32(itr->ItemStackCount); + if (itr->LogEntry == GUILD_BANK_LOG_MOVE_ITEM || itr->LogEntry == GUILD_BANK_LOG_MOVE_ITEM2) + data << uint8(itr->DestTabId); // moved tab } - data << uint32(time(NULL)-(*itr)->TimeStamp); + data << uint32(time(NULL) - itr->TimeStamp); } session->SendPacket(&data); } @@ -1651,24 +1616,23 @@ void Guild::DisplayGuildBankLogs(WorldSession *session, uint8 TabId) void Guild::LogBankEvent(uint8 LogEntry, uint8 TabId, uint32 PlayerGuidLow, uint32 ItemOrMoney, uint8 ItemStackCount, uint8 DestTabId) { - GuildBankEvent *NewEvent = new GuildBankEvent; + GuildBankEvent NewEvent; - NewEvent->LogGuid = LogMaxGuid++; - NewEvent->LogEntry = LogEntry; - NewEvent->PlayerGuid = PlayerGuidLow; - NewEvent->ItemOrMoney = ItemOrMoney; - NewEvent->ItemStackCount = ItemStackCount; - NewEvent->DestTabId = DestTabId; - NewEvent->TimeStamp = uint32(time(NULL)); + NewEvent.LogGuid = LogMaxGuid++; + NewEvent.LogEntry = LogEntry; + NewEvent.PlayerGuid = PlayerGuidLow; + NewEvent.ItemOrMoney = ItemOrMoney; + NewEvent.ItemStackCount = ItemStackCount; + NewEvent.DestTabId = DestTabId; + NewEvent.TimeStamp = uint32(time(NULL)); - if (NewEvent->isMoneyEvent()) + if (NewEvent.isMoneyEvent()) { if (m_GuildBankEventLog_Money.size() > GUILD_BANK_MAX_LOGS) { - GuildBankEvent *OldEvent = *(m_GuildBankEventLog_Money.begin()); + CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid='%u' AND LogGuid='%u'", + Id, m_GuildBankEventLog_Money.front().LogGuid); m_GuildBankEventLog_Money.pop_front(); - CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid='%u' AND LogGuid='%u'", Id, OldEvent->LogGuid); - delete OldEvent; } m_GuildBankEventLog_Money.push_back(NewEvent); } @@ -1676,15 +1640,14 @@ void Guild::LogBankEvent(uint8 LogEntry, uint8 TabId, uint32 PlayerGuidLow, uint { if (m_GuildBankEventLog_Item[TabId].size() > GUILD_BANK_MAX_LOGS) { - GuildBankEvent *OldEvent = *(m_GuildBankEventLog_Item[TabId].begin()); + CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid='%u' AND LogGuid='%u'", + Id, m_GuildBankEventLog_Item[TabId].front().LogGuid); m_GuildBankEventLog_Item[TabId].pop_front(); - CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid='%u' AND LogGuid='%u'", Id, OldEvent->LogGuid); - delete OldEvent; } m_GuildBankEventLog_Item[TabId].push_back(NewEvent); } CharacterDatabase.PExecute("INSERT INTO guild_bank_eventlog (guildid,LogGuid,LogEntry,TabId,PlayerGuid,ItemOrMoney,ItemStackCount,DestTabId,TimeStamp) VALUES ('%u','%u','%u','%u','%u','%u','%u','%u','" UI64FMTD "')", - Id, NewEvent->LogGuid, uint32(NewEvent->LogEntry), uint32(TabId), NewEvent->PlayerGuid, NewEvent->ItemOrMoney, uint32(NewEvent->ItemStackCount), uint32(NewEvent->DestTabId), NewEvent->TimeStamp); + Id, NewEvent.LogGuid, uint32(NewEvent.LogEntry), uint32(TabId), NewEvent.PlayerGuid, NewEvent.ItemOrMoney, uint32(NewEvent.ItemStackCount), uint32(NewEvent.DestTabId), NewEvent.TimeStamp); } // This will renum guids used at load to prevent always going up until infinit @@ -2013,4 +1976,3 @@ bool GuildItemPosCount::isContainedIn(GuildItemPosCountVec const &vec) const return false; } - diff --git a/src/game/Guild.h b/src/game/Guild.h index 9f832260f14..6e2441416c6 100644 --- a/src/game/Guild.h +++ b/src/game/Guild.h @@ -460,8 +460,8 @@ class Guild TabListMap m_TabListMap; /** These are actually ordered lists. The first element is the oldest entry.*/ - typedef std::list<GuildEventlogEntry*> GuildEventlog; - typedef std::list<GuildBankEvent*> GuildBankEventLog; + typedef std::list<GuildEventlogEntry> GuildEventlog; + typedef std::list<GuildBankEvent> GuildBankEventLog; GuildEventlog m_GuildEventlog; GuildBankEventLog m_GuildBankEventLog_Money; GuildBankEventLog m_GuildBankEventLog_Item[GUILD_BANK_MAX_TABS]; diff --git a/src/game/GuildHandler.cpp b/src/game/GuildHandler.cpp index f3f9c6c6758..a8eba52dee0 100644 --- a/src/game/GuildHandler.cpp +++ b/src/game/GuildHandler.cpp @@ -976,7 +976,7 @@ void WorldSession::HandleGuildBankDepositMoney( WorldPacket & recv_data ) pGuild->SetBankMoney(pGuild->GetGuildBankMoney()+money); GetPlayer()->ModifyMoney(-int(money)); - GetPlayer()->SaveDataFieldToDB(); //contains money + GetPlayer()->SaveGoldToDB(); CharacterDatabase.CommitTransaction(); @@ -1032,7 +1032,7 @@ void WorldSession::HandleGuildBankWithdrawMoney( WorldPacket & recv_data ) } GetPlayer()->ModifyMoney(money); - GetPlayer()->SaveDataFieldToDB(); // contains money + GetPlayer()->SaveGoldToDB(); CharacterDatabase.CommitTransaction(); diff --git a/src/game/Item.cpp b/src/game/Item.cpp index bc1c1eeeaab..484fa766da0 100644 --- a/src/game/Item.cpp +++ b/src/game/Item.cpp @@ -25,6 +25,7 @@ #include "Database/DatabaseEnv.h" #include "ItemEnchantmentMgr.h" #include "SpellMgr.h" +#include "ScriptCalls.h" void AddItemsSetItem(Player*player,Item *item) { @@ -285,6 +286,7 @@ void Item::UpdateDuration(Player* owner, uint32 diff) if (GetUInt32Value(ITEM_FIELD_DURATION)<=diff) { owner->DestroyItem(GetBagSlot(), GetSlot(), true); + Script->ItemExpire(owner, GetProto()); return; } @@ -380,6 +382,16 @@ bool Item::LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result) if(!proto) return false; + // update max durability (and durability) if need + if(proto->MaxDurability!= GetUInt32Value(ITEM_FIELD_MAXDURABILITY)) + { + SetUInt32Value(ITEM_FIELD_MAXDURABILITY,proto->MaxDurability); + if(GetUInt32Value(ITEM_FIELD_DURABILITY) > proto->MaxDurability) + SetUInt32Value(ITEM_FIELD_DURABILITY,proto->MaxDurability); + + need_save = true; + } + // recalculate suffix factor if(GetItemRandomPropertyId() < 0) { diff --git a/src/game/ItemHandler.cpp b/src/game/ItemHandler.cpp index cf762dd0cb2..114393f4287 100644 --- a/src/game/ItemHandler.cpp +++ b/src/game/ItemHandler.cpp @@ -674,7 +674,31 @@ void WorldSession::HandleBuyItemInSlotOpcode( WorldPacket & recv_data ) recv_data >> vendorguid >> item >> slot >> bagguid >> bagslot >> count; - GetPlayer()->BuyItemFromVendor(vendorguid,item,count,bagguid,bagslot); + uint8 bag = NULL_BAG; // init for case invalid bagGUID + + // find bag slot by bag guid + if (bagguid == _player->GetGUID()) + bag = INVENTORY_SLOT_BAG_0; + else + { + for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END;++i) + { + if (Bag *pBag = (Bag*)_player->GetItemByPos(INVENTORY_SLOT_BAG_0,i)) + { + if (bagguid == pBag->GetGUID()) + { + bag = i; + break; + } + } + } + } + + // bag not found, cheating? + if (bag == NULL_BAG) + return; + + GetPlayer()->BuyItemFromVendor(vendorguid,item,count,bag,bagslot); } void WorldSession::HandleBuyItemOpcode( WorldPacket & recv_data ) @@ -838,12 +862,14 @@ void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& recvPacket) recvPacket >> guid; // cheating protection + /* not critical if "cheated", and check skip allow by slots in bank windows open by .bank command. Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_BANKER); if(!pCreature) { sLog.outDebug( "WORLD: HandleBuyBankSlotOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) ); return; } + */ uint32 slot = _player->GetBankBagSlotCount(); diff --git a/src/game/LFGHandler.cpp b/src/game/LFGHandler.cpp index 51735c01e02..09653ccb0bc 100644 --- a/src/game/LFGHandler.cpp +++ b/src/game/LFGHandler.cpp @@ -42,6 +42,10 @@ static void AttemptJoin(Player* _player) if(!plr || plr==_player || plr->GetTeam() != _player->GetTeam()) continue; + //skip players not in world + if(!plr->IsInWorld()) + continue; + // skip not auto add, not group leader cases if(!plr->GetSession()->LookingForGroup_auto_add || plr->GetGroup() && plr->GetGroup()->GetLeaderGUID()!=plr->GetGUID()) continue; @@ -98,6 +102,9 @@ static void AttemptAddMore(Player* _player) if(!plr || plr==_player || plr->GetTeam() != _player->GetTeam()) continue; + if(!plr->IsInWorld()) + continue; + // skip not auto join or in group if(!plr->GetSession()->LookingForGroup_auto_join || plr->GetGroup() ) continue; @@ -311,6 +318,9 @@ void WorldSession::SendLfgResult(uint32 type, uint32 entry, uint8 lfg_type) if(!plr || plr->GetTeam() != _player->GetTeam()) continue; + if(!plr->IsInWorld()) + continue; + if(!plr->m_lookingForGroup.HaveInSlot(entry, type)) continue; diff --git a/src/game/Language.h b/src/game/Language.h index c16393eba1c..bd74333c45a 100644 --- a/src/game/Language.h +++ b/src/game/Language.h @@ -85,7 +85,8 @@ enum TrinityStrings LANG_USING_WORLD_DB = 57, LANG_USING_SCRIPT_LIB = 58, LANG_USING_EVENT_AI = 59, - // Room for more level 0 60-99 not used + LANG_CONNECTED_PLAYERS = 60, + // Room for more level 0 61-99 not used // level 1 chat LANG_GLOBAL_NOTIFY = 100, @@ -804,14 +805,15 @@ enum TrinityStrings LANG_BG_AV_NODE_TOWER_FROST_W = 1324, LANG_BG_AV_NODE_GRAVE_FROST_HUT = 1325, - LANG_BG_AV_ONEMINTOSTART = 1326, - LANG_BG_AV_HALFMINTOSTART = 1327, - LANG_BG_AV_STARTED = 1328, + LANG_BG_AV_START_ONE_MINUTE = 1326, + LANG_BG_AV_START_HALF_MINUTE = 1327, + LANG_BG_AV_HAS_BEGUN = 1328, LANG_BG_AV_A_NEAR_LOSE = 1329, LANG_BG_AV_H_NEAR_LOSE = 1330, LANG_BG_AV_H_CAPTAIN_DEAD = 1331, LANG_BG_AV_A_CAPTAIN_DEAD = 1332, - // FREE IDS 1333-1999 + LANG_BG_AV_START_TWO_MINUTES = 1333, + // FREE IDS 1334-1999 // Ticket Strings 2000-2029 LANG_COMMAND_TICKETNEW = 2000, @@ -868,7 +870,8 @@ enum TrinityStrings LANG_GM_BROADCAST = 6613, LANG_GM_NOTIFY = 6614, LANG_GM_ANNOUNCE_COLOR = 6615, - LANG_RESETALL_PET_SPELLS = 6616, + + LANG_GM_SILENCE = 6616, // "Silence is ON for %s" - Spell 1852 LANG_WORLD_CLOSED = 7523, LANG_WORLD_OPENED = 7524, @@ -940,6 +943,7 @@ enum TrinityStrings LANG_OPVP_ZM_GOSSIP_HORDE = 10055, // Use for custom patches 11000-11999 + LANG_AUTO_BROADCAST = 11000, // NOT RESERVED IDS 12000-1999999999 // `db_script_string` table index 2000000000-2000009999 (MIN_DB_SCRIPT_STRING_ID-MAX_DB_SCRIPT_STRING_ID) diff --git a/src/game/Level0.cpp b/src/game/Level0.cpp index c4f6935bced..efc09494d1d 100644 --- a/src/game/Level0.cpp +++ b/src/game/Level0.cpp @@ -86,11 +86,13 @@ bool ChatHandler::HandleStartCommand(const char* /*args*/) bool ChatHandler::HandleServerInfoCommand(const char* /*args*/) { + uint32 PlayersNum = sWorld.GetPlayerCount(); + uint32 MaxPlayersNum = sWorld.GetMaxPlayerCount(); uint32 activeClientsNum = sWorld.GetActiveSessionCount(); uint32 queuedClientsNum = sWorld.GetQueuedSessionCount(); uint32 maxActiveClientsNum = sWorld.GetMaxActiveSessionCount(); uint32 maxQueuedClientsNum = sWorld.GetMaxQueuedSessionCount(); - std::string str = secsToTimeString(sWorld.GetUptime()); + std::string uptime = secsToTimeString(sWorld.GetUptime()); uint32 updateTime = sWorld.GetUpdateTime(); PSendSysMessage(_FULLVERSION); @@ -103,8 +105,9 @@ bool ChatHandler::HandleServerInfoCommand(const char* /*args*/) //PSendSysMessage(LANG_USING_SCRIPT_LIB,sWorld.GetScriptsVersion()); //PSendSysMessage(LANG_USING_WORLD_DB,sWorld.GetDBVersion()); //PSendSysMessage(LANG_USING_EVENT_AI,sWorld.GetCreatureEventAIVersion()); + PSendSysMessage(LANG_CONNECTED_PLAYERS, PlayersNum, MaxPlayersNum); PSendSysMessage(LANG_CONNECTED_USERS, activeClientsNum, maxActiveClientsNum, queuedClientsNum, maxQueuedClientsNum); - PSendSysMessage(LANG_UPTIME, str.c_str()); + PSendSysMessage(LANG_UPTIME, uptime.c_str()); PSendSysMessage("Update time diff: %u.", updateTime); return true; @@ -160,9 +163,9 @@ bool ChatHandler::HandleGMListIngameCommand(const char* /*args*/) HashMapHolder<Player>::MapType::const_iterator itr = m.begin(); for(; itr != m.end(); ++itr) { - if (itr->second->GetSession()->GetSecurity() > SEC_PLAYER && - (itr->second->isGameMaster() || sWorld.getConfig(CONFIG_GM_IN_GM_LIST)) && - (!m_session || itr->second->IsVisibleGloballyFor(m_session->GetPlayer())) ) + AccountTypes itr_sec = itr->second->GetSession()->GetSecurity(); + if ((itr->second->isGameMaster() || itr_sec > SEC_PLAYER && itr_sec <= sWorld.getConfig(CONFIG_GM_LEVEL_IN_GM_LIST)) && + (!m_session || itr->second->IsVisibleGloballyFor(m_session->GetPlayer()))) { if(first) { @@ -242,14 +245,14 @@ bool ChatHandler::HandleAccountLockCommand(const char* args) std::string argstr = (char*)args; if (argstr == "on") { - LoginDatabase.PExecute( "UPDATE account SET locked = '1' WHERE id = '%d'",m_session->GetAccountId()); + loginDatabase.PExecute( "UPDATE account SET locked = '1' WHERE id = '%d'",m_session->GetAccountId()); PSendSysMessage(LANG_COMMAND_ACCLOCKLOCKED); return true; } if (argstr == "off") { - LoginDatabase.PExecute( "UPDATE account SET locked = '0' WHERE id = '%d'",m_session->GetAccountId()); + loginDatabase.PExecute( "UPDATE account SET locked = '0' WHERE id = '%d'",m_session->GetAccountId()); PSendSysMessage(LANG_COMMAND_ACCLOCKUNLOCKED); return true; } diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp index cb06b2fe273..275dd5b1e8c 100644 --- a/src/game/Level1.cpp +++ b/src/game/Level1.cpp @@ -370,7 +370,7 @@ bool ChatHandler::HandleGMTicketGetByIdCommand(const char* args) uint64 tguid = atoi(args); GM_Ticket *ticket = objmgr.GetGMTicket(tguid); - if(!ticket) + if(!ticket || ticket->closed != 0) { SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); return true; @@ -400,7 +400,10 @@ bool ChatHandler::HandleGMTicketGetByNameCommand(const char* args) if(!*args) return false; - Player *plr = objmgr.GetPlayer(args); + std::string name = (char*)args; + normalizePlayerName(name); + + Player *plr = objmgr.GetPlayer(name.c_str()); if(!plr) { SendSysMessage(LANG_NO_PLAYERS_FOUND); @@ -496,7 +499,7 @@ bool ChatHandler::HandleGMTicketAssignToCommand(const char* args) } uint64 tarGUID = objmgr.GetPlayerGUIDByName(targm.c_str()); uint64 accid = objmgr.GetPlayerAccountIdByGUID(tarGUID); - QueryResult *result = LoginDatabase.PQuery("SELECT `gmlevel` FROM `account` WHERE `id` = '%u'", accid); + QueryResult *result = loginDatabase.PQuery("SELECT `gmlevel` FROM `account` WHERE `id` = '%u'", accid); if(!tarGUID|| !result || result->Fetch()->GetUInt32() < SEC_MODERATOR) { SendSysMessage(LANG_COMMAND_TICKETASSIGNERROR_A); @@ -846,13 +849,13 @@ bool ChatHandler::HandleNamegoCommand(const char* args) PSendSysMessage(LANG_SUMMONING, nameLink.c_str(),""); if (needReportToTarget(target)) - ChatHandler(target).PSendSysMessage(LANG_SUMMONED_BY, nameLink.c_str()); + ChatHandler(target).PSendSysMessage(LANG_SUMMONED_BY, _player->GetName()); // stop flight if need if (target->isInFlight()) { target->GetMotionMaster()->MovementExpired(); - target->m_taxi.ClearTaxiDestinations(); + target->CleanupAfterTaxiFlight(); } // save only in non-flight case else @@ -937,7 +940,7 @@ bool ChatHandler::HandleGonameCommand(const char* args) } else if(cMap->IsDungeon()) { - Map* pMap = MapManager::Instance().GetMap(_player->GetMapId(),_player); + Map* pMap = _player->GetMap(); // we have to go to instance, and can go to player only if: // 1) we are in his group (either as leader or as member) @@ -988,7 +991,7 @@ bool ChatHandler::HandleGonameCommand(const char* args) if (_player->isInFlight()) { _player->GetMotionMaster()->MovementExpired(); - _player->m_taxi.ClearTaxiDestinations(); + _player->CleanupAfterTaxiFlight(); } // save only in non-flight case else @@ -1021,7 +1024,7 @@ bool ChatHandler::HandleGonameCommand(const char* args) if (_player->isInFlight()) { _player->GetMotionMaster()->MovementExpired(); - _player->m_taxi.ClearTaxiDestinations(); + _player->CleanupAfterTaxiFlight(); } // save only in non-flight case else @@ -1055,7 +1058,7 @@ bool ChatHandler::HandleRecallCommand(const char* args) if(target->isInFlight()) { target->GetMotionMaster()->MovementExpired(); - target->m_taxi.ClearTaxiDestinations(); + target->CleanupAfterTaxiFlight(); } target->TeleportTo(target->m_recallMap, target->m_recallX, target->m_recallY, target->m_recallZ, target->m_recallO); @@ -1468,23 +1471,42 @@ bool ChatHandler::HandleModifyTalentCommand (const char* args) return false; int tp = atoi((char*)args); - if (tp>0) + if (tp < 0) + return false; + + Unit* target = getSelectedUnit(); + if(!target) { - Player* player = getSelectedPlayer(); - if(!player) - { - SendSysMessage(LANG_NO_CHAR_SELECTED); - SetSentErrorMessage(true); - return false; - } + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + if(target->GetTypeId()==TYPEID_PLAYER) + { // check online security - if (HasLowerSecurity(player, 0)) + if (HasLowerSecurity((Player*)target, 0)) return false; - - player->SetFreeTalentPoints(tp); + ((Player*)target)->SetFreeTalentPoints(tp); + ((Player*)target)->SendTalentsInfoData(false); return true; } + else if(((Creature*)target)->isPet()) + { + Unit *owner = target->GetOwner(); + if(owner && owner->GetTypeId() == TYPEID_PLAYER && ((Pet *)target)->IsPermanentPetFor((Player*)owner)) + { + // check online security + if (HasLowerSecurity((Player*)owner, 0)) + return false; + ((Pet *)target)->SetFreeTalentPoints(tp); + ((Player*)owner)->SendTalentsInfoData(true); + return true; + } + } + + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); return false; } @@ -2222,7 +2244,7 @@ bool ChatHandler::HandleTeleCommand(const char * args) if(_player->isInFlight()) { _player->GetMotionMaster()->MovementExpired(); - _player->m_taxi.ClearTaxiDestinations(); + _player->CleanupAfterTaxiFlight(); } // save only in non-flight case else @@ -2243,7 +2265,7 @@ bool ChatHandler::HandleLookupAreaCommand(const char* args) if (!Utf8toWStr (namepart,wnamepart)) return false; - uint32 counter = 0; // Counter for figure out that we found smth. + bool found = false; // converting string that we try to find to lower case wstrToLower (wnamepart); @@ -2287,12 +2309,13 @@ bool ChatHandler::HandleLookupAreaCommand(const char* args) SendSysMessage (ss.str ().c_str()); - ++counter; + if(!found) + found = true; } } } - if (counter == 0) // if counter == 0 then we found nth + if (!found) SendSysMessage (LANG_COMMAND_NOAREAFOUND); return true; @@ -2485,7 +2508,7 @@ bool ChatHandler::HandleTeleNameCommand(const char * args) if(target->isInFlight()) { target->GetMotionMaster()->MovementExpired(); - target->m_taxi.ClearTaxiDestinations(); + target->CleanupAfterTaxiFlight(); } // save only in non-flight case else @@ -2581,7 +2604,7 @@ bool ChatHandler::HandleTeleGroupCommand(const char * args) if(pl->isInFlight()) { pl->GetMotionMaster()->MovementExpired(); - pl->m_taxi.ClearTaxiDestinations(); + pl->CleanupAfterTaxiFlight(); } // save only in non-flight case else @@ -2670,7 +2693,7 @@ bool ChatHandler::HandleGroupgoCommand(const char* args) if(pl->isInFlight()) { pl->GetMotionMaster()->MovementExpired(); - pl->m_taxi.ClearTaxiDestinations(); + pl->CleanupAfterTaxiFlight(); } // save only in non-flight case else @@ -2720,7 +2743,7 @@ bool ChatHandler::HandleGoTaxinodeCommand(const char* args) if (_player->isInFlight()) { _player->GetMotionMaster()->MovementExpired(); - _player->m_taxi.ClearTaxiDestinations(); + _player->CleanupAfterTaxiFlight(); } // save only in non-flight case else @@ -2763,7 +2786,7 @@ bool ChatHandler::HandleGoXYCommand(const char* args) if(_player->isInFlight()) { _player->GetMotionMaster()->MovementExpired(); - _player->m_taxi.ClearTaxiDestinations(); + _player->CleanupAfterTaxiFlight(); } // save only in non-flight case else @@ -2813,7 +2836,7 @@ bool ChatHandler::HandleGoXYZCommand(const char* args) if(_player->isInFlight()) { _player->GetMotionMaster()->MovementExpired(); - _player->m_taxi.ClearTaxiDestinations(); + _player->CleanupAfterTaxiFlight(); } // save only in non-flight case else @@ -2884,7 +2907,7 @@ bool ChatHandler::HandleGoZoneXYCommand(const char* args) if(_player->isInFlight()) { _player->GetMotionMaster()->MovementExpired(); - _player->m_taxi.ClearTaxiDestinations(); + _player->CleanupAfterTaxiFlight(); } // save only in non-flight case else @@ -2931,7 +2954,7 @@ bool ChatHandler::HandleGoGridCommand(const char* args) if(_player->isInFlight()) { _player->GetMotionMaster()->MovementExpired(); - _player->m_taxi.ClearTaxiDestinations(); + _player->CleanupAfterTaxiFlight(); } // save only in non-flight case else diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp index 63d8943469d..e3f3bbce6e6 100644 --- a/src/game/Level2.cpp +++ b/src/game/Level2.cpp @@ -44,12 +44,6 @@ #include "TargetedMovementGenerator.h" // for HandleNpcUnFollowCommand #include "CreatureGroups.h" -static uint32 ReputationRankStrIndex[MAX_REPUTATION_RANK] = -{ - LANG_REP_HATED, LANG_REP_HOSTILE, LANG_REP_UNFRIENDLY, LANG_REP_NEUTRAL, - LANG_REP_FRIENDLY, LANG_REP_HONORED, LANG_REP_REVERED, LANG_REP_EXALTED -}; - //mute player for some times bool ChatHandler::HandleMuteCommand(const char* args) { @@ -59,6 +53,11 @@ bool ChatHandler::HandleMuteCommand(const char* args) if(!delayStr) return false; + char *mutereason = strtok(NULL, "\r"); + std::string mutereasonstr = "No reason"; + if(mutereason != NULL) + mutereasonstr = mutereason; + Player* target; uint64 target_guid; std::string target_name; @@ -85,14 +84,15 @@ bool ChatHandler::HandleMuteCommand(const char* args) if (target) target->GetSession()->m_muteTime = mutetime; - LoginDatabase.PExecute("UPDATE account SET mutetime = " UI64FMTD " WHERE id = '%u'",uint64(mutetime), account_id ); + loginDatabase.PExecute("UPDATE account SET mutetime = " UI64FMTD " WHERE id = '%u'",uint64(mutetime), account_id ); if(target) - ChatHandler(target).PSendSysMessage(LANG_YOUR_CHAT_DISABLED, notspeaktime); + ChatHandler(target).PSendSysMessage(LANG_YOUR_CHAT_DISABLED, notspeaktime, mutereasonstr.c_str()); std::string nameLink = playerLink(target_name); - PSendSysMessage(LANG_YOU_DISABLE_CHAT, nameLink.c_str(), notspeaktime); + PSendSysMessage(LANG_YOU_DISABLE_CHAT, nameLink.c_str(), notspeaktime, mutereasonstr.c_str()); + return true; } @@ -130,7 +130,7 @@ bool ChatHandler::HandleUnmuteCommand(const char* args) target->GetSession()->m_muteTime = 0; } - LoginDatabase.PExecute("UPDATE account SET mutetime = '0' WHERE id = '%u'", account_id ); + loginDatabase.PExecute("UPDATE account SET mutetime = '0' WHERE id = '%u'", account_id ); if(target) ChatHandler(target).PSendSysMessage(LANG_YOUR_CHAT_ENABLED); @@ -891,98 +891,6 @@ bool ChatHandler::HandleGUIDCommand(const char* /*args*/) return true; } -bool ChatHandler::HandleLookupFactionCommand(const char* args) -{ - if (!*args) - return false; - - // Can be NULL at console call - Player *target = getSelectedPlayer (); - - std::string namepart = args; - std::wstring wnamepart; - - if (!Utf8toWStr (namepart,wnamepart)) - return false; - - // converting string that we try to find to lower case - wstrToLower (wnamepart); - - uint32 counter = 0; // Counter for figure out that we found smth. - - for (uint32 id = 0; id < sFactionStore.GetNumRows(); ++id) - { - FactionEntry const *factionEntry = sFactionStore.LookupEntry (id); - if (factionEntry) - { - FactionState const* repState = target ? target->GetReputationMgr().GetState(factionEntry) : NULL; - - int loc = GetSessionDbcLocale(); - std::string name = factionEntry->name[loc]; - if(name.empty()) - continue; - - if (!Utf8FitTo(name, wnamepart)) - { - loc = 0; - for(; loc < MAX_LOCALE; ++loc) - { - if(loc==GetSessionDbcLocale()) - continue; - - name = factionEntry->name[loc]; - if(name.empty()) - continue; - - if (Utf8FitTo(name, wnamepart)) - break; - } - } - - if(loc < MAX_LOCALE) - { - // send faction in "id - [faction] rank reputation [visible] [at war] [own team] [unknown] [invisible] [inactive]" format - // or "id - [faction] [no reputation]" format - std::ostringstream ss; - if (m_session) - ss << id << " - |cffffffff|Hfaction:" << id << "|h[" << name << " " << localeNames[loc] << "]|h|r"; - else - ss << id << " - " << name << " " << localeNames[loc]; - - if (repState) // and then target!=NULL also - { - ReputationRank rank = target->GetReputationMgr().GetRank(factionEntry); - std::string rankName = GetMangosString(ReputationRankStrIndex[rank]); - - ss << " " << rankName << "|h|r (" << target->GetReputationMgr().GetReputation(factionEntry) << ")"; - - if(repState->Flags & FACTION_FLAG_VISIBLE) - ss << GetTrinityString(LANG_FACTION_VISIBLE); - if(repState->Flags & FACTION_FLAG_AT_WAR) - ss << GetTrinityString(LANG_FACTION_ATWAR); - if(repState->Flags & FACTION_FLAG_PEACE_FORCED) - ss << GetTrinityString(LANG_FACTION_PEACE_FORCED); - if(repState->Flags & FACTION_FLAG_HIDDEN) - ss << GetTrinityString(LANG_FACTION_HIDDEN); - if(repState->Flags & FACTION_FLAG_INVISIBLE_FORCED) - ss << GetTrinityString(LANG_FACTION_INVISIBLE_FORCED); - if(repState->Flags & FACTION_FLAG_INACTIVE) - ss << GetTrinityString(LANG_FACTION_INACTIVE); - } - else - ss << GetTrinityString(LANG_FACTION_NOREPUTATION); - - SendSysMessage(ss.str().c_str()); - counter++; - } - } - } - - if (counter == 0) // if counter == 0 then we found nth - SendSysMessage(LANG_COMMAND_FACTION_NOTFOUND); - return true; -} - bool ChatHandler::HandleModifyRepCommand(const char * args) { if (!*args) return false; @@ -1371,7 +1279,6 @@ bool ChatHandler::HandleNpcDeleteCommand(const char* args) // Delete the creature unit->CombatStop(); unit->DeleteFromDB(); - unit->CleanupsBeforeDelete(); unit->AddObjectToRemoveList(); SendSysMessage(LANG_COMMAND_DELCREATMESSAGE); @@ -1754,7 +1661,7 @@ bool ChatHandler::HandleNpcFollowCommand(const char* /*args*/) } // Follow player - Using pet's default dist and angle - creature->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + creature->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, creature->GetFollowAngle()); PSendSysMessage(LANG_CREATURE_FOLLOW_YOU_NOW, creature->GetName()); return true; @@ -2263,40 +2170,40 @@ bool ChatHandler::HandlePInfoCommand(const char* args) if (HasLowerSecurity(NULL, target_guid)) return false; - // 0 - QueryResult *result = CharacterDatabase.PQuery("SELECT totaltime FROM characters WHERE guid = '%u'", GUID_LOPART(target_guid)); + // 0 1 2 3 + QueryResult *result = CharacterDatabase.PQuery("SELECT totaltime, level, money, account FROM characters WHERE guid = '%u'", GUID_LOPART(target_guid)); if (!result) return false; Field *fields = result->Fetch(); total_player_time = fields[0].GetUInt32(); + level = fields[1].GetUInt32(); + money = fields[2].GetUInt32(); + accId = fields[3].GetUInt32(); delete result; - - Tokens data; - if (!Player::LoadValuesArrayFromDB(data,target_guid)) - return false; - - money = Player::GetUInt32ValueFromArray(data, PLAYER_FIELD_COINAGE); - level = Player::GetUInt32ValueFromArray(data, UNIT_FIELD_LEVEL); - accId = objmgr.GetPlayerAccountIdByGUID(target_guid); } std::string username = GetTrinityString(LANG_ERROR); + std::string email = GetTrinityString(LANG_ERROR); std::string last_ip = GetTrinityString(LANG_ERROR); uint32 security = 0; std::string last_login = GetTrinityString(LANG_ERROR); - QueryResult* result = LoginDatabase.PQuery("SELECT username,gmlevel,last_ip,last_login FROM account WHERE id = '%u'",accId); + QueryResult* result = loginDatabase.PQuery("SELECT username,gmlevel,email,last_ip,last_login FROM account WHERE id = '%u'",accId); if(result) { Field* fields = result->Fetch(); username = fields[0].GetCppString(); security = fields[1].GetUInt32(); + email = fields[2].GetCppString(); + + if(email.empty()) + email = "-"; if(!m_session || m_session->GetSecurity() >= security) { - last_ip = fields[2].GetCppString(); - last_login = fields[3].GetCppString(); + last_ip = fields[3].GetCppString(); + last_login = fields[4].GetCppString(); } else { @@ -2309,7 +2216,7 @@ bool ChatHandler::HandlePInfoCommand(const char* args) std::string nameLink = playerLink(target_name); - PSendSysMessage(LANG_PINFO_ACCOUNT, (target?"":GetMangosString(LANG_OFFLINE)), nameLink.c_str(), GUID_LOPART(target_guid), username.c_str(), accId, security, last_ip.c_str(), last_login.c_str(), latency); + PSendSysMessage(LANG_PINFO_ACCOUNT, (target?"":GetTrinityString(LANG_OFFLINE)), nameLink.c_str(), GUID_LOPART(target_guid), username.c_str(), accId, email.c_str(), security, last_ip.c_str(), last_login.c_str(), latency); std::string timeStr = secsToTimeString(total_player_time,true,true); uint32 gold = money /GOLD; @@ -3448,7 +3355,6 @@ bool ChatHandler::HandleWpShowCommand(const char* args) Field *fields = result->Fetch(); uint32 guid = fields[0].GetUInt32(); Creature* pCreature = m_session->GetPlayer()->GetMap()->GetCreature(MAKE_NEW_GUID(guid,VISUAL_WAYPOINT,HIGHGUID_UNIT)); - if(!pCreature) { PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, guid); @@ -3673,7 +3579,7 @@ bool ChatHandler::HandleLookupEventCommand(const char* args) wstrToLower(wnamepart); - uint32 counter = 0; + bool found = false; GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); GameEventMgr::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); @@ -3695,11 +3601,12 @@ bool ChatHandler::HandleLookupEventCommand(const char* args) else PSendSysMessage(LANG_EVENT_ENTRY_LIST_CONSOLE,id,eventData.description.c_str(),active ); - ++counter; + if(!found) + found = true; } } - if (counter==0) + if (!found) SendSysMessage(LANG_NOEVENTFOUND); return true; @@ -4026,9 +3933,9 @@ bool ChatHandler::HandleLookupPlayerIpCommand(const char* args) char* limit_str = strtok (NULL, " "); int32 limit = limit_str ? atoi (limit_str) : -1; - LoginDatabase.escape_string (ip); + loginDatabase.escape_string (ip); - QueryResult* result = LoginDatabase.PQuery ("SELECT id,username FROM account WHERE last_ip = '%s'", ip.c_str ()); + QueryResult* result = loginDatabase.PQuery ("SELECT id,username FROM account WHERE last_ip = '%s'", ip.c_str ()); return LookupPlayerSearchCommand (result,limit); } @@ -4045,9 +3952,9 @@ bool ChatHandler::HandleLookupPlayerAccountCommand(const char* args) if (!AccountMgr::normalizeString (account)) return false; - LoginDatabase.escape_string (account); + loginDatabase.escape_string (account); - QueryResult* result = LoginDatabase.PQuery ("SELECT id,username FROM account WHERE username = '%s'", account.c_str ()); + QueryResult* result = loginDatabase.PQuery ("SELECT id,username FROM account WHERE username = '%s'", account.c_str ()); return LookupPlayerSearchCommand (result,limit); } @@ -4062,9 +3969,9 @@ bool ChatHandler::HandleLookupPlayerEmailCommand(const char* args) char* limit_str = strtok (NULL, " "); int32 limit = limit_str ? atoi (limit_str) : -1; - LoginDatabase.escape_string (email); + loginDatabase.escape_string (email); - QueryResult* result = LoginDatabase.PQuery ("SELECT id,username FROM account WHERE email = '%s'", email.c_str ()); + QueryResult* result = loginDatabase.PQuery ("SELECT id,username FROM account WHERE email = '%s'", email.c_str ()); return LookupPlayerSearchCommand (result,limit); } diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index adb904dbbbe..c1b8b56f043 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -108,7 +108,7 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args) PSendSysMessage("Syntax is: ahbotoptions ahexpire $ahMapID (2, 6 or 7)"); return false; } - AuctionHouseBotCommands(0, ahMapID, NULL, NULL); + auctionbot.Commands(0, ahMapID, NULL, NULL); } else if (strncmp(opt,"minitems",l) == 0) { @@ -118,21 +118,17 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args) PSendSysMessage("Syntax is: ahbotoptions minitems $ahMapID (2, 6 or 7) $minItems"); return false; } - AuctionHouseBotCommands(1, ahMapID, NULL, param1); + auctionbot.Commands(1, ahMapID, NULL, param1); } else if (strncmp(opt,"maxitems",l) == 0) { - PSendSysMessage("ahbotoptions mintime has been deprecated"); - return false; - /* char * param1 = strtok(NULL, " "); if ((!ahMapIdStr) || (!param1)) { PSendSysMessage("Syntax is: ahbotoptions maxitems $ahMapID (2, 6 or 7) $maxItems"); return false; } - AuctionHouseBotCommands(2, ahMapID, NULL, param1); - */ + auctionbot.Commands(2, ahMapID, NULL, param1); } else if (strncmp(opt,"mintime",l) == 0) { @@ -145,7 +141,7 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args) PSendSysMessage("Syntax is: ahbotoptions mintime $ahMapID (2, 6 or 7) $mintime"); return false; } - AuctionHouseBotCommands(3, ahMapID, NULL, param1); + auctionbot.Commands(3, ahMapID, NULL, param1); */ } else if (strncmp(opt,"maxtime",l) == 0) @@ -159,7 +155,7 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args) PSendSysMessage("Syntax is: ahbotoptions maxtime $ahMapID (2, 6 or 7) $maxtime"); return false; } - AuctionHouseBotCommands(4, ahMapID, NULL, param1); + auctionbot.Commands(4, ahMapID, NULL, param1); */ } else if (strncmp(opt,"percentages",l) == 0) @@ -240,7 +236,7 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args) strcat(param, param13); strcat(param, " "); strcat(param, param14); - AuctionHouseBotCommands(5, ahMapID, NULL, param); + auctionbot.Commands(5, ahMapID, NULL, param); } else if (strncmp(opt,"minprice",l) == 0) { @@ -253,31 +249,31 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args) } if (strncmp(param1,"grey",l) == 0) { - AuctionHouseBotCommands(6, ahMapID, AHB_GREY, param2); + auctionbot.Commands(6, ahMapID, AHB_GREY, param2); } else if (strncmp(param1,"white",l) == 0) { - AuctionHouseBotCommands(6, ahMapID, AHB_WHITE, param2); + auctionbot.Commands(6, ahMapID, AHB_WHITE, param2); } else if (strncmp(param1,"green",l) == 0) { - AuctionHouseBotCommands(6, ahMapID, AHB_GREEN, param2); + auctionbot.Commands(6, ahMapID, AHB_GREEN, param2); } else if (strncmp(param1,"blue",l) == 0) { - AuctionHouseBotCommands(6, ahMapID, AHB_BLUE, param2); + auctionbot.Commands(6, ahMapID, AHB_BLUE, param2); } else if (strncmp(param1,"purple",l) == 0) { - AuctionHouseBotCommands(6, ahMapID, AHB_PURPLE, param2); + auctionbot.Commands(6, ahMapID, AHB_PURPLE, param2); } else if (strncmp(param1,"orange",l) == 0) { - AuctionHouseBotCommands(6, ahMapID, AHB_ORANGE, param2); + auctionbot.Commands(6, ahMapID, AHB_ORANGE, param2); } else if (strncmp(param1,"yellow",l) == 0) { - AuctionHouseBotCommands(6, ahMapID, AHB_YELLOW, param2); + auctionbot.Commands(6, ahMapID, AHB_YELLOW, param2); } else { @@ -296,31 +292,31 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args) } if (strncmp(param1,"grey",l) == 0) { - AuctionHouseBotCommands(7, ahMapID, AHB_GREY, param2); + auctionbot.Commands(7, ahMapID, AHB_GREY, param2); } else if (strncmp(param1,"white",l) == 0) { - AuctionHouseBotCommands(7, ahMapID, AHB_WHITE, param2); + auctionbot.Commands(7, ahMapID, AHB_WHITE, param2); } else if (strncmp(param1,"green",l) == 0) { - AuctionHouseBotCommands(7, ahMapID, AHB_GREEN, param2); + auctionbot.Commands(7, ahMapID, AHB_GREEN, param2); } else if (strncmp(param1,"blue",l) == 0) { - AuctionHouseBotCommands(7, ahMapID, AHB_BLUE, param2); + auctionbot.Commands(7, ahMapID, AHB_BLUE, param2); } else if (strncmp(param1,"purple",l) == 0) { - AuctionHouseBotCommands(7, ahMapID, AHB_PURPLE, param2); + auctionbot.Commands(7, ahMapID, AHB_PURPLE, param2); } else if (strncmp(param1,"orange",l) == 0) { - AuctionHouseBotCommands(7, ahMapID, AHB_ORANGE, param2); + auctionbot.Commands(7, ahMapID, AHB_ORANGE, param2); } else if (strncmp(param1,"yellow",l) == 0) { - AuctionHouseBotCommands(7, ahMapID, AHB_YELLOW, param2); + auctionbot.Commands(7, ahMapID, AHB_YELLOW, param2); } else { @@ -345,31 +341,31 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args) } if (strncmp(param1,"grey",l) == 0) { - AuctionHouseBotCommands(8, ahMapID, AHB_GREY, param2); + auctionbot.Commands(8, ahMapID, AHB_GREY, param2); } else if (strncmp(param1,"white",l) == 0) { - AuctionHouseBotCommands(8, ahMapID, AHB_WHITE, param2); + auctionbot.Commands(8, ahMapID, AHB_WHITE, param2); } else if (strncmp(param1,"green",l) == 0) { - AuctionHouseBotCommands(8, ahMapID, AHB_GREEN, param2); + auctionbot.Commands(8, ahMapID, AHB_GREEN, param2); } else if (strncmp(param1,"blue",l) == 0) { - AuctionHouseBotCommands(8, ahMapID, AHB_BLUE, param2); + auctionbot.Commands(8, ahMapID, AHB_BLUE, param2); } else if (strncmp(param1,"purple",l) == 0) { - AuctionHouseBotCommands(8, ahMapID, AHB_PURPLE, param2); + auctionbot.Commands(8, ahMapID, AHB_PURPLE, param2); } else if (strncmp(param1,"orange",l) == 0) { - AuctionHouseBotCommands(8, ahMapID, AHB_ORANGE, param2); + auctionbot.Commands(8, ahMapID, AHB_ORANGE, param2); } else if (strncmp(param1,"yellow",l) == 0) { - AuctionHouseBotCommands(8, ahMapID, AHB_YELLOW, param2); + auctionbot.Commands(8, ahMapID, AHB_YELLOW, param2); } else { @@ -394,31 +390,31 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args) } if (strncmp(param1,"grey",l) == 0) { - AuctionHouseBotCommands(9, ahMapID, AHB_GREY, param2); + auctionbot.Commands(9, ahMapID, AHB_GREY, param2); } else if (strncmp(param1,"white",l) == 0) { - AuctionHouseBotCommands(9, ahMapID, AHB_WHITE, param2); + auctionbot.Commands(9, ahMapID, AHB_WHITE, param2); } else if (strncmp(param1,"green",l) == 0) { - AuctionHouseBotCommands(9, ahMapID, AHB_GREEN, param2); + auctionbot.Commands(9, ahMapID, AHB_GREEN, param2); } else if (strncmp(param1,"blue",l) == 0) { - AuctionHouseBotCommands(9, ahMapID, AHB_BLUE, param2); + auctionbot.Commands(9, ahMapID, AHB_BLUE, param2); } else if (strncmp(param1,"purple",l) == 0) { - AuctionHouseBotCommands(9, ahMapID, AHB_PURPLE, param2); + auctionbot.Commands(9, ahMapID, AHB_PURPLE, param2); } else if (strncmp(param1,"orange",l) == 0) { - AuctionHouseBotCommands(9, ahMapID, AHB_ORANGE, param2); + auctionbot.Commands(9, ahMapID, AHB_ORANGE, param2); } else if (strncmp(param1,"yellow",l) == 0) { - AuctionHouseBotCommands(9, ahMapID, AHB_YELLOW, param2); + auctionbot.Commands(9, ahMapID, AHB_YELLOW, param2); } else { @@ -443,31 +439,31 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args) } if (strncmp(param1,"grey",l) == 0) { - AuctionHouseBotCommands(10, ahMapID, AHB_GREY, param2); + auctionbot.Commands(10, ahMapID, AHB_GREY, param2); } else if (strncmp(param1,"white",l) == 0) { - AuctionHouseBotCommands(10, ahMapID, AHB_WHITE, param2); + auctionbot.Commands(10, ahMapID, AHB_WHITE, param2); } else if (strncmp(param1,"green",l) == 0) { - AuctionHouseBotCommands(10, ahMapID, AHB_GREEN, param2); + auctionbot.Commands(10, ahMapID, AHB_GREEN, param2); } else if (strncmp(param1,"blue",l) == 0) { - AuctionHouseBotCommands(10, ahMapID, AHB_BLUE, param2); + auctionbot.Commands(10, ahMapID, AHB_BLUE, param2); } else if (strncmp(param1,"purple",l) == 0) { - AuctionHouseBotCommands(10, ahMapID, AHB_PURPLE, param2); + auctionbot.Commands(10, ahMapID, AHB_PURPLE, param2); } else if (strncmp(param1,"orange",l) == 0) { - AuctionHouseBotCommands(10, ahMapID, AHB_ORANGE, param2); + auctionbot.Commands(10, ahMapID, AHB_ORANGE, param2); } else if (strncmp(param1,"yellow",l) == 0) { - AuctionHouseBotCommands(10, ahMapID, AHB_YELLOW, param2); + auctionbot.Commands(10, ahMapID, AHB_YELLOW, param2); } else { @@ -486,31 +482,31 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args) } if (strncmp(param1,"grey",l) == 0) { - AuctionHouseBotCommands(11, ahMapID, AHB_GREY, param2); + auctionbot.Commands(11, ahMapID, AHB_GREY, param2); } else if (strncmp(param1,"white",l) == 0) { - AuctionHouseBotCommands(11, ahMapID, AHB_WHITE, param2); + auctionbot.Commands(11, ahMapID, AHB_WHITE, param2); } else if (strncmp(param1,"green",l) == 0) { - AuctionHouseBotCommands(11, ahMapID, AHB_GREEN, param2); + auctionbot.Commands(11, ahMapID, AHB_GREEN, param2); } else if (strncmp(param1,"blue",l) == 0) { - AuctionHouseBotCommands(11, ahMapID, AHB_BLUE, param2); + auctionbot.Commands(11, ahMapID, AHB_BLUE, param2); } else if (strncmp(param1,"purple",l) == 0) { - AuctionHouseBotCommands(11, ahMapID, AHB_PURPLE, param2); + auctionbot.Commands(11, ahMapID, AHB_PURPLE, param2); } else if (strncmp(param1,"orange",l) == 0) { - AuctionHouseBotCommands(11, ahMapID, AHB_ORANGE, param2); + auctionbot.Commands(11, ahMapID, AHB_ORANGE, param2); } else if (strncmp(param1,"yellow",l) == 0) { - AuctionHouseBotCommands(11, ahMapID, AHB_YELLOW, param2); + auctionbot.Commands(11, ahMapID, AHB_YELLOW, param2); } else { @@ -526,7 +522,7 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args) PSendSysMessage("Syntax is: ahbotoptions bidinterval $ahMapID (2, 6 or 7) $interval(in minutes)"); return false; } - AuctionHouseBotCommands(12, ahMapID, NULL, param1); + auctionbot.Commands(12, ahMapID, NULL, param1); } else if (strncmp(opt,"bidsperinterval",l) == 0) { @@ -536,7 +532,7 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args) PSendSysMessage("Syntax is: ahbotoptions bidsperinterval $ahMapID (2, 6 or 7) $bids"); return false; } - AuctionHouseBotCommands(13, ahMapID, NULL, param1); + auctionbot.Commands(13, ahMapID, NULL, param1); } else { @@ -648,7 +644,6 @@ bool ChatHandler::HandleReloadAllSpellCommand(const char*) { HandleReloadSkillDiscoveryTemplateCommand("a"); HandleReloadSkillExtraItemTemplateCommand("a"); - HandleReloadSpellAffectCommand("a"); HandleReloadSpellRequiredCommand("a"); HandleReloadSpellAreaCommand("a"); HandleReloadSpellElixirCommand("a"); @@ -996,14 +991,6 @@ bool ChatHandler::HandleReloadSkillFishingBaseLevelCommand(const char* /*args*/) return true; } -bool ChatHandler::HandleReloadSpellAffectCommand(const char*) -{ - sLog.outString( "Re-Loading SpellAffect definitions..." ); - spellmgr.LoadSpellAffects(); - SendGlobalGMSysMessage("DB table `spell_affect` (spell mods apply requirements) reloaded."); - return true; -} - bool ChatHandler::HandleReloadSpellAreaCommand(const char*) { sLog.outString( "Re-Loading SpellArea Data..." ); @@ -1428,7 +1415,7 @@ bool ChatHandler::HandleAccountSetGmLevelCommand(const char* args) PSendSysMessage(LANG_YOURS_SECURITY_CHANGED, m_session->GetPlayer()->GetName(), gm); } - LoginDatabase.PExecute("UPDATE account SET gmlevel = '%d' WHERE id = '%u'", gm, targetAccountId); + loginDatabase.PExecute("UPDATE account SET gmlevel = '%d' WHERE id = '%u'", gm, targetAccountId); return true; }else { @@ -1469,7 +1456,7 @@ bool ChatHandler::HandleAccountSetGmLevelCommand(const char* args) } PSendSysMessage(LANG_YOU_CHANGE_SECURITY, targetAccountName.c_str(), gm); - LoginDatabase.PExecute("UPDATE account SET gmlevel = '%d' WHERE id = '%u'", gm, targetAccountId); + loginDatabase.PExecute("UPDATE account SET gmlevel = '%d' WHERE id = '%u'", gm, targetAccountId); return true; } } @@ -1644,6 +1631,9 @@ bool ChatHandler::HandleUnLearnCommand(const char* args) else SendSysMessage(LANG_FORGET_SPELL); + if(GetTalentSpellCost(spell_id)) + target->SendTalentsInfoData(false); + return true; } @@ -2134,7 +2124,6 @@ bool ChatHandler::HandleLearnAllCommand(const char* /*args*/) "1180", "201", "12593", - "12842", "16770", "6057", "12051", @@ -2161,9 +2150,7 @@ bool ChatHandler::HandleLearnAllCommand(const char* /*args*/) "11129", "16766", "12573", - "15053", "12580", - "12475", "12472", "12953", "12488", @@ -2586,6 +2573,10 @@ bool ChatHandler::HandleLearnCommand(const char* args) else targetPlayer->learnSpell(spell,false); + uint32 first_spell = spellmgr.GetFirstSpellInChain(spell); + if(GetTalentSpellCost(first_spell)) + targetPlayer->SendTalentsInfoData(false); + return true; } @@ -3215,7 +3206,7 @@ bool ChatHandler::HandleLookupItemCommand(const char* args) wstrToLower(wnamepart); - uint32 counter = 0; + bool found = false; // Search in `item_template` for (uint32 id = 0; id < sItemStorage.MaxEntry; id++) @@ -3240,7 +3231,10 @@ bool ChatHandler::HandleLookupItemCommand(const char* args) PSendSysMessage(LANG_ITEM_LIST_CHAT, id, id, name.c_str()); else PSendSysMessage(LANG_ITEM_LIST_CONSOLE, id, name.c_str()); - ++counter; + + if(!found) + found = true; + continue; } } @@ -3257,11 +3251,13 @@ bool ChatHandler::HandleLookupItemCommand(const char* args) PSendSysMessage(LANG_ITEM_LIST_CHAT, id, id, name.c_str()); else PSendSysMessage(LANG_ITEM_LIST_CONSOLE, id, name.c_str()); - ++counter; + + if(!found) + found = true; } } - if (counter==0) + if (!found) SendSysMessage(LANG_COMMAND_NOITEMFOUND); return true; @@ -3281,7 +3277,7 @@ bool ChatHandler::HandleLookupItemSetCommand(const char* args) // converting string that we try to find to lower case wstrToLower( wnamepart ); - uint32 counter = 0; // Counter for figure out that we found smth. + bool found = false; // Search in ItemSet.dbc for (uint32 id = 0; id < sItemSetStore.GetNumRows(); id++) @@ -3318,11 +3314,13 @@ bool ChatHandler::HandleLookupItemSetCommand(const char* args) PSendSysMessage(LANG_ITEMSET_LIST_CHAT,id,id,name.c_str(),localeNames[loc]); else PSendSysMessage(LANG_ITEMSET_LIST_CONSOLE,id,name.c_str(),localeNames[loc]); - ++counter; + + if(!found) + found = true; } } } - if (counter == 0) // if counter == 0 then we found nth + if (!found) SendSysMessage(LANG_COMMAND_NOITEMSETFOUND); return true; } @@ -3344,7 +3342,7 @@ bool ChatHandler::HandleLookupSkillCommand(const char* args) // converting string that we try to find to lower case wstrToLower( wnamepart ); - uint32 counter = 0; // Counter for figure out that we found smth. + bool found = false; // Search in SkillLine.dbc for (uint32 id = 0; id < sSkillLineStore.GetNumRows(); id++) @@ -3396,11 +3394,12 @@ bool ChatHandler::HandleLookupSkillCommand(const char* args) else PSendSysMessage(LANG_SKILL_LIST_CONSOLE,id,name.c_str(),localeNames[loc],knownStr,valStr); - ++counter; + if(!found) + found = true; } } } - if (counter == 0) // if counter == 0 then we found nth + if (!found) SendSysMessage(LANG_COMMAND_NOSKILLFOUND); return true; } @@ -3422,7 +3421,7 @@ bool ChatHandler::HandleLookupSpellCommand(const char* args) // converting string that we try to find to lower case wstrToLower( wnamepart ); - uint32 counter = 0; // Counter for figure out that we found smth. + bool found = false; // Search in Spell.dbc for (uint32 id = 0; id < sSpellStore.GetNumRows(); id++) @@ -3496,11 +3495,12 @@ bool ChatHandler::HandleLookupSpellCommand(const char* args) SendSysMessage(ss.str().c_str()); - ++counter; + if(!found) + found = true; } } } - if (counter == 0) // if counter == 0 then we found nth + if (!found) SendSysMessage(LANG_COMMAND_NOSPELLFOUND); return true; } @@ -3522,7 +3522,7 @@ bool ChatHandler::HandleLookupQuestCommand(const char* args) wstrToLower(wnamepart); - uint32 counter = 0 ; + bool found = false; ObjectMgr::QuestMap const& qTemplates = objmgr.GetQuestTemplates(); for (ObjectMgr::QuestMap::const_iterator iter = qTemplates.begin(); iter != qTemplates.end(); ++iter) @@ -3562,7 +3562,10 @@ bool ChatHandler::HandleLookupQuestCommand(const char* args) PSendSysMessage(LANG_QUEST_LIST_CHAT,qinfo->GetQuestId(),qinfo->GetQuestId(),title.c_str(),statusStr); else PSendSysMessage(LANG_QUEST_LIST_CONSOLE,qinfo->GetQuestId(),title.c_str(),statusStr); - ++counter; + + if(!found) + found = true; + continue; } } @@ -3597,11 +3600,12 @@ bool ChatHandler::HandleLookupQuestCommand(const char* args) else PSendSysMessage(LANG_QUEST_LIST_CONSOLE,qinfo->GetQuestId(),title.c_str(),statusStr); - ++counter; + if(!found) + found = true; } } - if (counter==0) + if (!found) SendSysMessage(LANG_COMMAND_NOQUESTFOUND); return true; @@ -3621,7 +3625,7 @@ bool ChatHandler::HandleLookupCreatureCommand(const char* args) wstrToLower (wnamepart); - uint32 counter = 0; + bool found = false; for (uint32 id = 0; id< sCreatureStorage.MaxEntry; ++id) { @@ -3645,7 +3649,10 @@ bool ChatHandler::HandleLookupCreatureCommand(const char* args) PSendSysMessage (LANG_CREATURE_ENTRY_LIST_CHAT, id, id, name.c_str ()); else PSendSysMessage (LANG_CREATURE_ENTRY_LIST_CONSOLE, id, name.c_str ()); - ++counter; + + if(!found) + found = true; + continue; } } @@ -3662,11 +3669,13 @@ bool ChatHandler::HandleLookupCreatureCommand(const char* args) PSendSysMessage (LANG_CREATURE_ENTRY_LIST_CHAT, id, id, name.c_str ()); else PSendSysMessage (LANG_CREATURE_ENTRY_LIST_CONSOLE, id, name.c_str ()); - ++counter; + + if(!found) + found = true; } } - if (counter==0) + if (!found) SendSysMessage (LANG_COMMAND_NOCREATUREFOUND); return true; @@ -3686,7 +3695,7 @@ bool ChatHandler::HandleLookupObjectCommand(const char* args) wstrToLower(wnamepart); - uint32 counter = 0; + bool found = false; for (uint32 id = 0; id< sGOStorage.MaxEntry; id++ ) { @@ -3710,7 +3719,10 @@ bool ChatHandler::HandleLookupObjectCommand(const char* args) PSendSysMessage(LANG_GO_ENTRY_LIST_CHAT, id, id, name.c_str()); else PSendSysMessage(LANG_GO_ENTRY_LIST_CONSOLE, id, name.c_str()); - ++counter; + + if(!found) + found = true; + continue; } } @@ -3727,16 +3739,112 @@ bool ChatHandler::HandleLookupObjectCommand(const char* args) PSendSysMessage(LANG_GO_ENTRY_LIST_CHAT, id, id, name.c_str()); else PSendSysMessage(LANG_GO_ENTRY_LIST_CONSOLE, id, name.c_str()); - ++counter; + + if(!found) + found = true; } } - if(counter==0) + if(!found) SendSysMessage(LANG_COMMAND_NOGAMEOBJECTFOUND); return true; } +bool ChatHandler::HandleLookupFactionCommand(const char* args) +{ + if (!*args) + return false; + + // Can be NULL at console call + Player *target = getSelectedPlayer (); + + std::string namepart = args; + std::wstring wnamepart; + + if (!Utf8toWStr (namepart,wnamepart)) + return false; + + // converting string that we try to find to lower case + wstrToLower (wnamepart); + + bool found = false; + + for (uint32 id = 0; id < sFactionStore.GetNumRows(); ++id) + { + FactionEntry const *factionEntry = sFactionStore.LookupEntry (id); + if (factionEntry) + { + FactionState const* repState = target ? target->GetReputationMgr().GetState(factionEntry) : NULL; + + int loc = GetSessionDbcLocale(); + std::string name = factionEntry->name[loc]; + if(name.empty()) + continue; + + if (!Utf8FitTo(name, wnamepart)) + { + loc = 0; + for(; loc < MAX_LOCALE; ++loc) + { + if(loc==GetSessionDbcLocale()) + continue; + + name = factionEntry->name[loc]; + if(name.empty()) + continue; + + if (Utf8FitTo(name, wnamepart)) + break; + } + } + + if(loc < MAX_LOCALE) + { + // send faction in "id - [faction] rank reputation [visible] [at war] [own team] [unknown] [invisible] [inactive]" format + // or "id - [faction] [no reputation]" format + std::ostringstream ss; + if (m_session) + ss << id << " - |cffffffff|Hfaction:" << id << "|h[" << name << " " << localeNames[loc] << "]|h|r"; + else + ss << id << " - " << name << " " << localeNames[loc]; + + if (repState) // and then target!=NULL also + { + ReputationRank rank = target->GetReputationMgr().GetRank(factionEntry); + std::string rankName = GetMangosString(ReputationRankStrIndex[rank]); + + ss << " " << rankName << "|h|r (" << target->GetReputationMgr().GetReputation(factionEntry) << ")"; + + if(repState->Flags & FACTION_FLAG_VISIBLE) + ss << GetTrinityString(LANG_FACTION_VISIBLE); + if(repState->Flags & FACTION_FLAG_AT_WAR) + ss << GetTrinityString(LANG_FACTION_ATWAR); + if(repState->Flags & FACTION_FLAG_PEACE_FORCED) + ss << GetTrinityString(LANG_FACTION_PEACE_FORCED); + if(repState->Flags & FACTION_FLAG_HIDDEN) + ss << GetTrinityString(LANG_FACTION_HIDDEN); + if(repState->Flags & FACTION_FLAG_INVISIBLE_FORCED) + ss << GetTrinityString(LANG_FACTION_INVISIBLE_FORCED); + if(repState->Flags & FACTION_FLAG_INACTIVE) + ss << GetTrinityString(LANG_FACTION_INACTIVE); + } + else + ss << GetTrinityString(LANG_FACTION_NOREPUTATION); + + SendSysMessage(ss.str().c_str()); + + if(!found) + found = true; + } + } + } + + if (!found) + SendSysMessage(LANG_COMMAND_FACTION_NOTFOUND); + return true; +} + bool ChatHandler::HandleLookupTaxiNodeCommand(const char * args) { if(!*args) @@ -3751,7 +3859,7 @@ bool ChatHandler::HandleLookupTaxiNodeCommand(const char * args) // converting string that we try to find to lower case wstrToLower( wnamepart ); - uint32 counter = 0; // Counter for figure out that we found smth. + bool found = false; // Search in TaxiNodes.dbc for (uint32 id = 0; id < sTaxiNodesStore.GetNumRows(); id++) @@ -3790,12 +3898,14 @@ bool ChatHandler::HandleLookupTaxiNodeCommand(const char * args) else PSendSysMessage (LANG_TAXINODE_ENTRY_LIST_CONSOLE, id, name.c_str(), localeNames[loc], nodeEntry->map_id,nodeEntry->x,nodeEntry->y,nodeEntry->z); - ++counter; + + if(!found) + found = true; } } } - if (counter == 0) // if counter == 0 then we found nth - SendSysMessage(LANG_COMMAND_NOSPELLFOUND); + if (!found) + SendSysMessage(LANG_COMMAND_NOTAXINODEFOUND); return true; } @@ -3813,7 +3923,7 @@ bool ChatHandler::HandleLookupMapCommand(const char* args) wstrToLower(wnamepart); - uint32 counter = 0; + bool found = false; // search in Map.dbc for(uint32 id = 0; id < sMapStore.GetNumRows(); id++) @@ -3892,12 +4002,13 @@ bool ChatHandler::HandleLookupMapCommand(const char* args) else SendSysMessage(ss.str().c_str()); - counter++; + if(!found) + found = true; } } } - if(!counter) + if(!found) SendSysMessage(LANG_COMMAND_NOMAPFOUND); return true; @@ -4672,11 +4783,7 @@ void ChatHandler::HandleCharacterLevel(Player* player, uint64 player_guid, uint3 else { // update level and XP at level, all other will be updated at loading - Tokens values; - Player::LoadValuesArrayFromDB(values,player_guid); - Player::SetUInt32ValueInArray(values,UNIT_FIELD_LEVEL,newlevel); - Player::SetUInt32ValueInArray(values,PLAYER_XP,0); - Player::SaveValuesArrayInDB(values,player_guid); + CharacterDatabase.PExecute("UPDATE characters SET level = '%u', xp = 0 WHERE guid = '%u'", newlevel, GUID_LOPART(player_guid)); } } @@ -4701,7 +4808,7 @@ bool ChatHandler::HandleCharacterLevelCommand(const char* args) if(!extractPlayerTarget(nameStr,&target,&target_guid,&target_name)) return false; - int32 oldlevel = target ? target->getLevel() : Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL,target_guid); + int32 oldlevel = target ? target->getLevel() : Player::GetLevelFromDB(target_guid); int32 newlevel = levelStr ? atoi(levelStr) : oldlevel; if(newlevel < 1) @@ -4740,7 +4847,7 @@ bool ChatHandler::HandleLevelUpCommand(const char* args) if(!extractPlayerTarget(nameStr,&target,&target_guid,&target_name)) return false; - int32 oldlevel = target ? target->getLevel() : Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL,target_guid); + int32 oldlevel = target ? target->getLevel() : Player::GetLevelFromDB(target_guid); int32 addlevel = levelStr ? atoi(levelStr) : 1; int32 newlevel = oldlevel + addlevel; @@ -4823,57 +4930,6 @@ bool ChatHandler::HandleHideAreaCommand(const char* args) return true; } -bool ChatHandler::HandleDebugUpdate(const char* args) -{ - if(!*args) - return false; - - uint32 updateIndex; - uint32 value; - - char* pUpdateIndex = strtok((char*)args, " "); - - Unit* chr = getSelectedUnit(); - if (chr == NULL) - { - SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); - SetSentErrorMessage(true); - return false; - } - - if(!pUpdateIndex) - { - return true; - } - updateIndex = atoi(pUpdateIndex); - //check updateIndex - if(chr->GetTypeId() == TYPEID_PLAYER) - { - if (updateIndex>=PLAYER_END) return true; - } - else - { - if (updateIndex>=UNIT_END) return true; - } - - char* pvalue = strtok(NULL, " "); - if (!pvalue) - { - value=chr->GetUInt32Value(updateIndex); - - PSendSysMessage(LANG_UPDATE, chr->GetGUIDLow(),updateIndex,value); - return true; - } - - value=atoi(pvalue); - - PSendSysMessage(LANG_UPDATE_CHANGE, chr->GetGUIDLow(),updateIndex,value); - - chr->SetUInt32Value(updateIndex,value); - - return true; -} - bool ChatHandler::HandleBankCommand(const char* /*args*/) { m_session->SendShowBank( m_session->GetPlayer()->GetGUID() ); @@ -4923,106 +4979,6 @@ bool ChatHandler::HandleChangeWeather(const char* args) return true; } -bool ChatHandler::HandleDebugSetValue(const char* args) -{ - if(!*args) - return false; - - char* px = strtok((char*)args, " "); - char* py = strtok(NULL, " "); - char* pz = strtok(NULL, " "); - - if (!px || !py) - return false; - - WorldObject* target = getSelectedObject(); - if(!target) - { - SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); - SetSentErrorMessage(true); - return false; - } - - uint64 guid = target->GetGUID(); - - uint32 Opcode = (uint32)atoi(px); - if(Opcode >= target->GetValuesCount()) - { - PSendSysMessage(LANG_TOO_BIG_INDEX, Opcode, GUID_LOPART(guid), target->GetValuesCount()); - return false; - } - uint32 iValue; - float fValue; - bool isint32 = true; - if(pz) - isint32 = (bool)atoi(pz); - if(isint32) - { - iValue = (uint32)atoi(py); - sLog.outDebug(GetTrinityString(LANG_SET_UINT), GUID_LOPART(guid), Opcode, iValue); - target->SetUInt32Value( Opcode , iValue ); - PSendSysMessage(LANG_SET_UINT_FIELD, GUID_LOPART(guid), Opcode,iValue); - } - else - { - fValue = (float)atof(py); - sLog.outDebug(GetTrinityString(LANG_SET_FLOAT), GUID_LOPART(guid), Opcode, fValue); - target->SetFloatValue( Opcode , fValue ); - PSendSysMessage(LANG_SET_FLOAT_FIELD, GUID_LOPART(guid), Opcode,fValue); - } - - return true; -} - -bool ChatHandler::HandleDebugGetValue(const char* args) -{ - if(!*args) - return false; - - char* px = strtok((char*)args, " "); - char* pz = strtok(NULL, " "); - - if (!px) - return false; - - Unit* target = getSelectedUnit(); - if(!target) - { - SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); - SetSentErrorMessage(true); - return false; - } - - uint64 guid = target->GetGUID(); - - uint32 Opcode = (uint32)atoi(px); - if(Opcode >= target->GetValuesCount()) - { - PSendSysMessage(LANG_TOO_BIG_INDEX, Opcode, GUID_LOPART(guid), target->GetValuesCount()); - return false; - } - uint32 iValue; - float fValue; - bool isint32 = true; - if(pz) - isint32 = (bool)atoi(pz); - - if(isint32) - { - iValue = target->GetUInt32Value( Opcode ); - sLog.outDebug(GetTrinityString(LANG_GET_UINT), GUID_LOPART(guid), Opcode, iValue); - PSendSysMessage(LANG_GET_UINT_FIELD, GUID_LOPART(guid), Opcode, iValue); - } - else - { - fValue = target->GetFloatValue( Opcode ); - sLog.outDebug(GetTrinityString(LANG_GET_FLOAT), GUID_LOPART(guid), Opcode, fValue); - PSendSysMessage(LANG_GET_FLOAT_FIELD, GUID_LOPART(guid), Opcode, fValue); - } - - return true; -} - bool ChatHandler::HandleDebugSet32Bit(const char* args) { if(!*args) @@ -5056,38 +5012,6 @@ bool ChatHandler::HandleDebugSet32Bit(const char* args) return true; } -bool ChatHandler::HandleDebugMod32Value(const char* args) -{ - if(!*args) - return false; - - char* px = strtok((char*)args, " "); - char* py = strtok(NULL, " "); - - if (!px || !py) - return false; - - uint32 Opcode = (uint32)atoi(px); - int Value = atoi(py); - - if(Opcode >= m_session->GetPlayer()->GetValuesCount()) - { - PSendSysMessage(LANG_TOO_BIG_INDEX, Opcode, m_session->GetPlayer()->GetGUIDLow(), m_session->GetPlayer( )->GetValuesCount()); - return false; - } - - sLog.outDebug(GetTrinityString(LANG_CHANGE_32BIT), Opcode, Value); - - int CurrentValue = (int)m_session->GetPlayer( )->GetUInt32Value( Opcode ); - - CurrentValue += Value; - m_session->GetPlayer( )->SetUInt32Value( Opcode , (uint32)CurrentValue ); - - PSendSysMessage(LANG_CHANGE_32BIT_FIELD, Opcode,CurrentValue); - - return true; -} - bool ChatHandler::HandleTeleAddCommand(const char * args) { if(!*args) @@ -5243,9 +5167,6 @@ bool ChatHandler::HandleResetHonorCommand (const char * args) static bool HandleResetStatsOrLevelHelper(Player* player) { - PlayerInfo const *info = objmgr.GetPlayerInfo(player->getRace(), player->getClass()); - if(!info) return false; - ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(player->getClass()); if(!cEntry) { @@ -5268,21 +5189,7 @@ static bool HandleResetStatsOrLevelHelper(Player* player) // reset only if player not in some form; if(player->m_form==FORM_NONE) - { - switch(player->getGender()) - { - case GENDER_FEMALE: - player->SetDisplayId(info->displayId_f); - player->SetNativeDisplayId(info->displayId_f); - break; - case GENDER_MALE: - player->SetDisplayId(info->displayId_m); - player->SetNativeDisplayId(info->displayId_m); - break; - default: - break; - } - } + player->InitDisplayIds(); player->SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP ); player->SetByteValue(UNIT_FIELD_BYTES_2, 3, player->m_form); @@ -5310,6 +5217,8 @@ bool ChatHandler::HandleResetLevelCommand(const char * args) ? sWorld.getConfig(CONFIG_START_PLAYER_LEVEL) : sWorld.getConfig(CONFIG_START_HEROIC_PLAYER_LEVEL); + target->_ApplyAllLevelScaleItemMods(false); + target->SetLevel(start_level); target->InitRunes(); target->InitStatsForLevel(true); @@ -5318,6 +5227,8 @@ bool ChatHandler::HandleResetLevelCommand(const char * args) target->InitTalentForLevel(); target->SetUInt32Value(PLAYER_XP,0); + target->_ApplyAllLevelScaleItemMods(true); + // reset level for pet if(Pet* pet = target->GetPet()) pet->SynchronizeLevelWithOwner(); @@ -5374,42 +5285,52 @@ bool ChatHandler::HandleResetTalentsCommand(const char * args) uint64 target_guid; std::string target_name; if (!extractPlayerTarget((char*)args,&target,&target_guid,&target_name)) + { + // Try reset talents as Hunter Pet + Creature* creature = getSelectedCreature(); + if (!*args && creature && creature->isPet()) + { + Unit *owner = creature->GetOwner(); + if(owner && owner->GetTypeId() == TYPEID_PLAYER && ((Pet *)creature)->IsPermanentPetFor((Player*)owner)) + { + ((Pet *)creature)->resetTalents(true); + ((Player*)owner)->SendTalentsInfoData(true); + + ChatHandler((Player*)owner).SendSysMessage(LANG_RESET_PET_TALENTS); + if(!m_session || m_session->GetPlayer()!=((Player*)owner)) + PSendSysMessage(LANG_RESET_PET_TALENTS_ONLINE,GetNameLink((Player*)owner).c_str()); + } + return true; + } + + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); return false; + } if (target) { target->resetTalents(true); - + target->SendTalentsInfoData(false); ChatHandler(target).SendSysMessage(LANG_RESET_TALENTS); if (!m_session || m_session->GetPlayer()!=target) PSendSysMessage(LANG_RESET_TALENTS_ONLINE,GetNameLink(target).c_str()); + Pet* pet = target->GetPet(); + Pet::resetTalentsForAllPetsOf(target,pet); + if(pet) + target->SendTalentsInfoData(true); return true; } else if (target_guid) { - CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid = '%u'",uint32(AT_LOGIN_RESET_TALENTS), GUID_LOPART(target_guid) ); + uint32 at_flags = AT_LOGIN_NONE | AT_LOGIN_RESET_PET_TALENTS; + CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid = '%u'",at_flags, GUID_LOPART(target_guid) ); std::string nameLink = playerLink(target_name); PSendSysMessage(LANG_RESET_TALENTS_OFFLINE,nameLink.c_str()); return true; } - // Try reset talents as Hunter Pet - Creature* creature = getSelectedCreature(); - if (creature && creature->isPet() && ((Pet *)creature)->getPetType() == HUNTER_PET) - { - ((Pet *)creature)->resetTalents(true); - Unit *owner = creature->GetOwner(); - if (owner && owner->GetTypeId() == TYPEID_PLAYER) - { - Player* owner_player = (Player *)owner; - ChatHandler(owner_player).SendSysMessage(LANG_RESET_PET_TALENTS); - if(!m_session || m_session->GetPlayer()!=owner_player) - PSendSysMessage(LANG_RESET_PET_TALENTS_ONLINE,GetNameLink(owner_player).c_str()); - } - return true; - } - SendSysMessage(LANG_NO_CHAR_SELECTED); SetSentErrorMessage(true); return false; @@ -5434,21 +5355,11 @@ bool ChatHandler::HandleResetAllCommand(const char * args) } else if(casename=="talents") { - atLogin = AT_LOGIN_RESET_TALENTS; + atLogin = AtLoginFlags(AT_LOGIN_RESET_TALENTS | AT_LOGIN_RESET_PET_TALENTS); sWorld.SendWorldText(LANG_RESETALL_TALENTS); if(!m_session) SendSysMessage(LANG_RESETALL_TALENTS); } - else if(casename=="pet_spells") - { - CharacterDatabase.PExecute("UPDATE character_pet SET load_flags = load_flags | '%u' WHERE (load_flags & '%u') = '0'",uint32(AT_LOAD_RESET_SPELLS),uint32(AT_LOAD_RESET_SPELLS)); - HashMapHolder<Player>::MapType const& plist = ObjectAccessor::Instance().GetPlayers(); - for(HashMapHolder<Player>::MapType::const_iterator itr = plist.begin(); itr != plist.end(); ++itr) - if (itr->second->GetPet()) - itr->second->SetPetAtLoginFlag(AT_LOAD_RESET_SPELLS); - sWorld.SendWorldText(LANG_RESETALL_PET_SPELLS); - return true; - } else { PSendSysMessage(LANG_RESETALL_UNKNOWN_CASE,args); @@ -5774,8 +5685,9 @@ bool ChatHandler::HandleQuestComplete(const char* args) } else if(creature > 0) { - for(uint16 z = 0; z < creaturecount; ++z) - player->KilledMonster(creature,0); + if(CreatureInfo const* cInfo = objmgr.GetCreatureTemplate(creature)) + for(uint16 z = 0; z < creaturecount; ++z) + player->KilledMonster(cInfo,0); } else if(creature < 0) { @@ -5997,7 +5909,7 @@ bool ChatHandler::HandleBanInfoCharacterCommand(const char* args) bool ChatHandler::HandleBanInfoHelper(uint32 accountid, char const* accountname) { - QueryResult *result = LoginDatabase.PQuery("SELECT FROM_UNIXTIME(bandate), unbandate-bandate, active, unbandate,banreason,bannedby FROM account_banned WHERE id = '%u' ORDER BY bandate ASC",accountid); + QueryResult *result = loginDatabase.PQuery("SELECT FROM_UNIXTIME(bandate), unbandate-bandate, active, unbandate,banreason,bannedby FROM account_banned WHERE id = '%u' ORDER BY bandate ASC",accountid); if(!result) { PSendSysMessage(LANG_BANINFO_NOACCOUNTBAN, accountname); @@ -6037,8 +5949,8 @@ bool ChatHandler::HandleBanInfoIPCommand(const char* args) std::string IP = cIP; - LoginDatabase.escape_string(IP); - QueryResult *result = LoginDatabase.PQuery("SELECT ip, FROM_UNIXTIME(bandate), FROM_UNIXTIME(unbandate), unbandate-UNIX_TIMESTAMP(), banreason,bannedby,unbandate-bandate FROM ip_banned WHERE ip = '%s'",IP.c_str()); + loginDatabase.escape_string(IP); + QueryResult *result = loginDatabase.PQuery("SELECT ip, FROM_UNIXTIME(bandate), FROM_UNIXTIME(unbandate), unbandate-UNIX_TIMESTAMP(), banreason,bannedby,unbandate-bandate FROM ip_banned WHERE ip = '%s'",IP.c_str()); if(!result) { PSendSysMessage(LANG_BANINFO_NOIP); @@ -6056,14 +5968,14 @@ bool ChatHandler::HandleBanInfoIPCommand(const char* args) bool ChatHandler::HandleBanListCharacterCommand(const char* args) { - LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); + loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); char* cFilter = strtok ((char*)args, " "); if(!cFilter) return false; std::string filter = cFilter; - LoginDatabase.escape_string(filter); + loginDatabase.escape_string(filter); QueryResult* result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'"),filter.c_str()); if (!result) { @@ -6076,22 +5988,22 @@ bool ChatHandler::HandleBanListCharacterCommand(const char* args) bool ChatHandler::HandleBanListAccountCommand(const char* args) { - LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); + loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); char* cFilter = strtok((char*)args, " "); std::string filter = cFilter ? cFilter : ""; - LoginDatabase.escape_string(filter); + loginDatabase.escape_string(filter); QueryResult* result; if(filter.empty()) { - result = LoginDatabase.Query("SELECT account.id, username FROM account, account_banned" + result = loginDatabase.Query("SELECT account.id, username FROM account, account_banned" " WHERE account.id = account_banned.id AND active = 1 GROUP BY account.id"); } else { - result = LoginDatabase.PQuery("SELECT account.id, username FROM account, account_banned" + result = loginDatabase.PQuery("SELECT account.id, username FROM account, account_banned" " WHERE account.id = account_banned.id AND active = 1 AND username "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'")" GROUP BY account.id", filter.c_str()); } @@ -6117,7 +6029,7 @@ bool ChatHandler::HandleBanListHelper(QueryResult* result) Field* fields = result->Fetch(); uint32 accountid = fields[0].GetUInt32(); - QueryResult* banresult = LoginDatabase.PQuery("SELECT account.username FROM account,account_banned WHERE account_banned.id='%u' AND account_banned.id=account.id",accountid); + QueryResult* banresult = loginDatabase.PQuery("SELECT account.username FROM account,account_banned WHERE account_banned.id='%u' AND account_banned.id=account.id",accountid); if(banresult) { Field* fields2 = banresult->Fetch(); @@ -6148,7 +6060,7 @@ bool ChatHandler::HandleBanListHelper(QueryResult* result) accmgr.GetName (account_id,account_name); // No SQL injection. id is uint32. - QueryResult *banInfo = LoginDatabase.PQuery("SELECT bandate,unbandate,bannedby,banreason FROM account_banned WHERE id = %u ORDER BY unbandate", account_id); + QueryResult *banInfo = loginDatabase.PQuery("SELECT bandate,unbandate,bannedby,banreason FROM account_banned WHERE id = %u ORDER BY unbandate", account_id); if (banInfo) { Field *fields2 = banInfo->Fetch(); @@ -6185,23 +6097,23 @@ bool ChatHandler::HandleBanListHelper(QueryResult* result) bool ChatHandler::HandleBanListIPCommand(const char* args) { - LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); + loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); char* cFilter = strtok((char*)args, " "); std::string filter = cFilter ? cFilter : ""; - LoginDatabase.escape_string(filter); + loginDatabase.escape_string(filter); QueryResult* result; if(filter.empty()) { - result = LoginDatabase.Query ("SELECT ip,bandate,unbandate,bannedby,banreason FROM ip_banned" + result = loginDatabase.Query ("SELECT ip,bandate,unbandate,bannedby,banreason FROM ip_banned" " WHERE (bandate=unbandate OR unbandate>UNIX_TIMESTAMP())" " ORDER BY unbandate" ); } else { - result = LoginDatabase.PQuery( "SELECT ip,bandate,unbandate,bannedby,banreason FROM ip_banned" + result = loginDatabase.PQuery( "SELECT ip,bandate,unbandate,bannedby,banreason FROM ip_banned" " WHERE (bandate=unbandate OR unbandate>UNIX_TIMESTAMP()) AND ip "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'") " ORDER BY unbandate",filter.c_str() ); } @@ -6287,7 +6199,7 @@ bool ChatHandler::HandleRespawnCommand(const char* /*args*/) TypeContainerVisitor<Trinity::WorldObjectWorker<Trinity::RespawnDo>, GridTypeMapContainer > obj_worker(worker); CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, obj_worker, *MapManager::Instance().GetMap(pl->GetMapId(), pl)); + cell_lock->Visit(cell_lock, obj_worker, *pl->GetMap()); return true; } @@ -6362,18 +6274,18 @@ bool ChatHandler::HandlePDumpLoadCommand(const char *args) char* name_str = strtok(NULL, " "); std::string name; - if(name_str) + if (name_str) { name = name_str; // normalize the name if specified and check if it exists - if(!normalizePlayerName(name)) + if (!normalizePlayerName(name)) { PSendSysMessage(LANG_INVALID_CHARACTER_NAME); SetSentErrorMessage(true); return false; } - if(!ObjectMgr::IsValidName(name,true)) + if (ObjectMgr::CheckPlayerName(name,true) != CHAR_NAME_SUCCESS) { PSendSysMessage(LANG_INVALID_CHARACTER_NAME); SetSentErrorMessage(true); @@ -6385,17 +6297,17 @@ bool ChatHandler::HandlePDumpLoadCommand(const char *args) uint32 guid = 0; - if(guid_str) + if (guid_str) { guid = atoi(guid_str); - if(!guid) + if (!guid) { PSendSysMessage(LANG_INVALID_CHARACTER_GUID); SetSentErrorMessage(true); return false; } - if(objmgr.GetPlayerAccountIdByGUID(guid)) + if (objmgr.GetPlayerAccountIdByGUID(guid)) { PSendSysMessage(LANG_CHARACTER_GUID_IN_USE,guid); SetSentErrorMessage(true); @@ -6970,7 +6882,7 @@ bool ChatHandler::HandleInstanceSaveDataCommand(const char * /*args*/) bool ChatHandler::HandleGMListFullCommand(const char* /*args*/) { ///- Get the accounts with GM Level >0 - QueryResult *result = LoginDatabase.Query( "SELECT username,gmlevel FROM account WHERE gmlevel > 0" ); + QueryResult *result = loginDatabase.Query( "SELECT username,gmlevel FROM account WHERE gmlevel > 0" ); if(result) { SendSysMessage(LANG_GMLIST); @@ -7079,7 +6991,7 @@ bool ChatHandler::HandleAccountSetAddonCommand(const char* args) return false; // No SQL injection - LoginDatabase.PExecute("UPDATE account SET expansion = '%d' WHERE id = '%u'",lev,account_id); + loginDatabase.PExecute("UPDATE account SET expansion = '%d' WHERE id = '%u'",lev,account_id); PSendSysMessage(LANG_ACCOUNT_SETADDON,account_name.c_str(),account_id,lev); return true; } @@ -7330,8 +7242,7 @@ bool ChatHandler::HandleModifyGenderCommand(const char *args) player->SetByteValue(PLAYER_BYTES_3, 0, gender); // Change display ID - player->SetDisplayId(gender ? info->displayId_f : info->displayId_m); - player->SetNativeDisplayId(gender ? info->displayId_f : info->displayId_m); + player->InitDisplayIds(); char const* gender_full = gender ? "female" : "male"; diff --git a/src/game/LootHandler.cpp b/src/game/LootHandler.cpp index 172a6ef46c2..6b5f22b6a6b 100644 --- a/src/game/LootHandler.cpp +++ b/src/game/LootHandler.cpp @@ -259,6 +259,10 @@ void WorldSession::HandleLootOpcode( WorldPacket & recv_data ) uint64 guid; recv_data >> guid; + // Check possible cheat + if(!_player->isAlive()) + return; + GetPlayer()->SendLoot(guid, LOOT_CORPSE); } diff --git a/src/game/LootMgr.cpp b/src/game/LootMgr.cpp index 5654f540da1..b54d7b90774 100644 --- a/src/game/LootMgr.cpp +++ b/src/game/LootMgr.cpp @@ -1152,7 +1152,7 @@ void LoadLootTemplates_Gameobject() { if(GameObjectInfo const* gInfo = sGOStorage.LookupEntry<GameObjectInfo>(i)) { - if(uint32 lootid = GameObject::GetLootId(gInfo)) + if(uint32 lootid = gInfo->GetLootId()) { if(!ids_set.count(lootid)) LootTemplates_Gameobject.ReportNotExistedId(lootid); @@ -1323,10 +1323,12 @@ void LoadLootTemplates_Spell() if(!ids_set.count(spell_id)) { - // not report about not trainable spells (optionally supported by DB) except with SPELL_ATTR_EX2_UNK14 (clams) - // 61756 (Northrend Inscription Research (FAST QA VERSION) for example - if ((spellInfo->Attributes & SPELL_ATTR_UNK5) || (spellInfo->AttributesEx2 & SPELL_ATTR_EX2_UNK14)) + // not report about not trainable spells (optionally supported by DB) + // ignore 61756 (Northrend Inscription Research (FAST QA VERSION) for example + if (!(spellInfo->Attributes & SPELL_ATTR_NOT_SHAPESHIFT) || (spellInfo->Attributes & SPELL_ATTR_UNK5)) + { LootTemplates_Spell.ReportNotExistedId(spell_id); + } } else ids_set.erase(spell_id); diff --git a/src/game/Mail.cpp b/src/game/Mail.cpp index 0af4999092f..94f40f0079e 100644 --- a/src/game/Mail.cpp +++ b/src/game/Mail.cpp @@ -32,6 +32,15 @@ #include "AuctionHouseBot.h" #include "DBCStores.h" +enum MailShowFlags +{ + MAIL_SHOW_UNK0 = 0x0001, + MAIL_SHOW_DELETE = 0x0002, // forced show delete button instead return button + MAIL_SHOW_AUCTION = 0x0004, // from old comment + MAIL_SHOW_COD = 0x0008, // show subject prefix + MAIL_SHOW_UNK4 = 0x0010, +}; + void MailItem::deleteItem( bool inDB ) { if(item) @@ -381,7 +390,7 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data ) } } - if (m->sender == AHBplayerGUID) + if (m->sender == auctionbot.GetAHBplayerGUID()) { SendReturnToSender(MAIL_CREATURE, GetAccountId(), m->receiver, m->sender, m->subject, m->itemTextId, &mi, m->money, m->mailTemplateId); } @@ -567,7 +576,7 @@ void WorldSession::HandleMailTakeMoney(WorldPacket & recv_data ) // save money and mail to prevent cheating CharacterDatabase.BeginTransaction(); - pl->SaveDataFieldToDB(); // contains money + pl->SaveGoldToDB(); pl->_SaveMail(); CharacterDatabase.CommitTransaction(); } @@ -611,6 +620,14 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data ) if(data.wpos()+next_mail_size > maxPacketSize) break; + uint32 show_flags = 0; + if ((*itr)->messageType != MAIL_NORMAL) + show_flags |= MAIL_SHOW_DELETE; + if ((*itr)->messageType == MAIL_AUCTION) + show_flags |= MAIL_SHOW_AUCTION; + if ((*itr)->COD) + show_flags |= MAIL_SHOW_COD; + data << (uint16) 0x0040; // unknown 2.3.0, different values data << (uint32) (*itr)->messageID; // Message ID data << (uint8) (*itr)->messageType; // Message Type @@ -634,7 +651,7 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data ) data << (uint32) 0; // unknown data << (uint32) (*itr)->stationery; // stationery (Stationery.dbc) data << (uint32) (*itr)->money; // Gold - data << (uint32) 0x04; // unknown, 0x4 - auction, 0x10 - normal + data << (uint32) show_flags; // unknown, 0x4 - auction, 0x10 - normal // Time data << (float) ((*itr)->expire_time-time(NULL))/DAY; data << (uint32) (*itr)->mailTemplateId; // mail template (MailTemplate.dbc) @@ -772,38 +789,40 @@ void WorldSession::HandleQueryNextMailTime(WorldPacket & /*recv_data*/ ) { data << (uint32) 0; // float data << (uint32) 0; // count + uint32 count = 0; + time_t now = time(NULL); for(PlayerMails::iterator itr = _player->GetmailBegin(); itr != _player->GetmailEnd(); ++itr) { Mail *m = (*itr); - // not checked yet, already must be delivered - if((m->checked & MAIL_CHECK_MASK_READ)==0 && (m->deliver_time <= time(NULL))) - { - ++count; + // must be not checked yet + if(m->checked & MAIL_CHECK_MASK_READ) + continue; - if(count > 2) - { - count = 2; - break; - } + // and already delivered + if(now < m->deliver_time) + continue; - data << (uint64) m->sender; // sender guid + data << (uint64) m->sender; // sender guid - switch(m->messageType) - { - case MAIL_AUCTION: - data << (uint32) 2; - data << (uint32) 2; - data << (uint32) m->stationery; - break; - default: - data << (uint32) 0; - data << (uint32) 0; - data << (uint32) m->stationery; - break; - } - data << (uint32) 0xC6000000; // float unk, time or something + switch(m->messageType) + { + case MAIL_AUCTION: + data << (uint32) 2; + data << (uint32) 2; + data << (uint32) m->stationery; + break; + default: + data << (uint32) 0; + data << (uint32) 0; + data << (uint32) m->stationery; + break; } + data << (uint32) 0xC6000000; // float unk, time or something + + ++count; + if(count == 2) // do not display more than 2 mails + break; } data.put<uint32>(4, count); } @@ -817,7 +836,7 @@ void WorldSession::HandleQueryNextMailTime(WorldPacket & /*recv_data*/ ) void WorldSession::SendMailTo(Player* receiver, uint8 messageType, uint8 stationery, uint32 sender_guidlow_or_entry, uint32 receiver_guidlow, std::string subject, uint32 itemTextId, MailItemsInfo* mi, uint32 money, uint32 COD, uint32 checked, uint32 deliver_delay, uint16 mailTemplateId) { - if (receiver_guidlow == AHBplayerGUID) + if (receiver_guidlow == auctionbot.GetAHBplayerGUID()) { if(messageType == MAIL_AUCTION && mi) // auction mail with items { diff --git a/src/game/Makefile.am b/src/game/Makefile.am index ed918234265..bc5dace3985 100644 --- a/src/game/Makefile.am +++ b/src/game/Makefile.am @@ -624,6 +624,7 @@ libmangosgame_a_SOURCES = \ Channel.cpp \ Channel.h \ ChannelHandler.cpp \ + ChannelMgr.cpp \ ChannelMgr.h \ CharacterHandler.cpp \ Chat.cpp \ @@ -840,11 +841,8 @@ libmangosgame_a_SOURCES = \ GroupRefManager.h >>>>>>> 5a6594330caefc0dc00a5fe792dcb0e344b457cb:src/game/Makefile.am -## Link against shared library -libmangosgame_a_LIBADD = ../shared/libmangosshared.a ../shared/Auth/libmangosauth.a ../shared/Config/libmangosconfig.a ../shared/Database/libmangosdatabase.a ../shared/vmap/libmangosvmaps.a - ## Additional files to include when running 'make dist' # Precompiled Headers for WIN EXTRA_DIST = \ pchdef.cpp \ - pchdef.h
\ No newline at end of file + pchdef.h diff --git a/src/game/Map.cpp b/src/game/Map.cpp index 6dbb0e37c86..47925102892 100644 --- a/src/game/Map.cpp +++ b/src/game/Map.cpp @@ -20,6 +20,7 @@ #include "MapManager.h" #include "Player.h" +#include "Vehicle.h" #include "GridNotifiers.h" #include "Log.h" #include "GridStates.h" @@ -36,6 +37,7 @@ #include "Group.h" #include "MapRefManager.h" #include "Vehicle.h" +#include "WaypointManager.h" #include "MapInstanced.h" #include "InstanceSaveMgr.h" @@ -46,9 +48,20 @@ GridState* si_GridStates[MAX_GRID_STATE]; +struct ScriptAction +{ + uint64 sourceGUID; + uint64 targetGUID; + uint64 ownerGUID; // owner of source if source is item + ScriptInfo const* script; // pointer to static script data +}; + Map::~Map() { UnloadAll(); + + if(!m_scriptSchedule.empty()) + sWorld.DecreaseScheduledScriptCount(m_scriptSchedule.size()); } bool Map::ExistMap(uint32 mapid,int gx,int gy) @@ -127,14 +140,12 @@ void Map::LoadMap(int gx,int gy, bool reload) if(GridMaps[gx][gy]) return; - Map* baseMap = const_cast<Map*>(MapManager::Instance().CreateBaseMap(i_id)); - // load grid map for base map - if (!baseMap->GridMaps[gx][gy]) - baseMap->EnsureGridCreated(GridPair(63-gx,63-gy)); + if (!m_parentMap->GridMaps[gx][gy]) + m_parentMap->EnsureGridCreated(GridPair(63-gx,63-gy)); - ((MapInstanced*)(baseMap))->AddGridMapReference(GridPair(gx,gy)); - GridMaps[gx][gy] = baseMap->GridMaps[gx][gy]; + ((MapInstanced*)(m_parentMap))->AddGridMapReference(GridPair(gx,gy)); + GridMaps[gx][gy] = m_parentMap->GridMaps[gx][gy]; return; } @@ -144,7 +155,7 @@ void Map::LoadMap(int gx,int gy, bool reload) //map already load, delete it before reloading (Is it necessary? Do we really need the ability the reload maps during runtime?) if(GridMaps[gx][gy]) { - sLog.outDetail("Unloading already loaded map %u before reloading.",i_id); + sLog.outDetail("Unloading already loaded map %u before reloading.",GetId()); delete (GridMaps[gx][gy]); GridMaps[gx][gy]=NULL; } @@ -153,7 +164,7 @@ void Map::LoadMap(int gx,int gy, bool reload) char *tmp=NULL; int len = sWorld.GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1; tmp = new char[len]; - snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),i_id,gx,gy); + snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),GetId(),gx,gy); sLog.outDetail("Loading map %s",tmp); // loading data GridMaps[gx][gy] = new GridMap(); @@ -187,11 +198,10 @@ void Map::DeleteStateMachine() delete si_GridStates[GRID_STATE_REMOVAL]; } -Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode) - : i_mapEntry (sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), - i_id(id), i_InstanceId(InstanceId), m_unloadTimer(0), +Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode, Map* _parent) + : i_mapEntry (sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId), m_unloadTimer(0), m_activeNonPlayersIter(m_activeNonPlayers.end()), - i_gridExpiry(expiry) + i_gridExpiry(expiry), m_parentMap(_parent ? _parent : this) , i_lock(true) { m_notifyTimer.SetInterval(IN_MILISECONDS/2); @@ -364,6 +374,13 @@ void Map::DeleteFromWorld(T* obj) delete obj; } +template<> +void Map::DeleteFromWorld(Player* pl) +{ + ObjectAccessor::Instance().RemoveObject(pl); + delete pl; +} + template<class T> void Map::AddNotifier(T*) { @@ -393,7 +410,7 @@ Map::EnsureGridCreated(const GridPair &p) Guard guard(*this); if(!getNGrid(p.x_coord, p.y_coord)) { - sLog.outDebug("Creating grid[%u,%u] for map %u instance %u", p.x_coord, p.y_coord, i_id, i_InstanceId); + sLog.outDebug("Creating grid[%u,%u] for map %u instance %u", p.x_coord, p.y_coord, GetId(), i_InstanceId); setNGrid(new NGridType(p.x_coord*MAX_NUMBER_OF_GRIDS + p.y_coord, p.x_coord, p.y_coord, i_gridExpiry, sWorld.getConfig(CONFIG_GRID_UNLOAD)), p.x_coord, p.y_coord); @@ -425,11 +442,11 @@ Map::EnsureGridLoadedAtEnter(const Cell &cell, Player *player) if (player) { player->SendDelayResponse(MAX_GRID_LOAD_TIME); - DEBUG_LOG("Player %s enter cell[%u,%u] triggers of loading grid[%u,%u] on map %u", player->GetName(), cell.CellX(), cell.CellY(), cell.GridX(), cell.GridY(), i_id); + DEBUG_LOG("Player %s enter cell[%u,%u] triggers of loading grid[%u,%u] on map %u", player->GetName(), cell.CellX(), cell.CellY(), cell.GridX(), cell.GridY(), GetId()); } else { - DEBUG_LOG("Active object nearby triggers of loading grid [%u,%u] on map %u", cell.GridX(), cell.GridY(), i_id); + DEBUG_LOG("Active object nearby triggers of loading grid [%u,%u] on map %u", cell.GridX(), cell.GridY(), GetId()); } ResetGridExpiry(*getNGrid(cell.GridX(), cell.GridY()), 0.1f); @@ -450,7 +467,7 @@ bool Map::EnsureGridLoaded(const Cell &cell) assert(grid != NULL); if( !isGridObjectDataLoaded(cell.GridX(), cell.GridY()) ) { - sLog.outDebug("Loading grid[%u,%u] for map %u instance %u", cell.GridX(), cell.GridY(), i_id, i_InstanceId); + sLog.outDebug("Loading grid[%u,%u] for map %u instance %u", cell.GridX(), cell.GridY(), GetId(), i_InstanceId); ObjectGridLoader loader(*grid, this, cell); loader.LoadN(); @@ -474,10 +491,8 @@ void Map::LoadGrid(float x, float y) bool Map::Add(Player *player) { - player->GetMapRef().link(this, player); - - player->SetInstanceId(GetInstanceId()); - + // Check if we are adding to correct map + assert (player->GetMap() == this); // update player state for other player and visa-versa CellPair p = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY()); Cell cell(p); @@ -504,6 +519,8 @@ Map::Add(T *obj) return; } + obj->SetMap(this); + Cell cell(p); if(obj->isActiveObject()) EnsureGridLoadedAtEnter(cell); @@ -525,7 +542,6 @@ Map::Add(T *obj) //also, trigger needs to cast spell, if not update, cannot see visual //if(obj->GetTypeId() != TYPEID_UNIT) UpdateObjectVisibility(obj,cell,p); - AddNotifier(obj); } @@ -633,12 +649,14 @@ void Map::AddUnitToNotify(Unit* u) } } -void Map::RemoveUnitFromNotify(int32 slot) +void Map::RemoveUnitFromNotify(Unit *unit, int32 slot) { if(i_lock) { - assert(slot < i_unitsToNotifyBacklog.size()); - i_unitsToNotifyBacklog[slot] = NULL; + if(slot < i_unitsToNotifyBacklog.size() && i_unitsToNotifyBacklog[slot] == unit) + i_unitsToNotifyBacklog[slot] = NULL; + else if(slot < i_unitsToNotify.size() && i_unitsToNotify[slot] == unit) + i_unitsToNotify[slot] = NULL; } else { @@ -774,37 +792,39 @@ void Map::Update(const uint32 &t_diff) // Don't unload grids if it's battleground, since we may have manually added GOs,creatures, those doesn't load from DB at grid re-load ! // This isn't really bother us, since as soon as we have instanced BG-s, the whole map unloads as the BG gets ended - if (IsBattleGroundOrArena()) - return; - - for (GridRefManager<NGridType>::iterator i = GridRefManager<NGridType>::begin(); i != GridRefManager<NGridType>::end(); ) + if (!IsBattleGroundOrArena()) { - NGridType *grid = i->getSource(); - GridInfo *info = i->getSource()->getGridInfoRef(); - ++i; // The update might delete the map and we need the next map before the iterator gets invalid - assert(grid->GetGridState() >= 0 && grid->GetGridState() < MAX_GRID_STATE); - si_GridStates[grid->GetGridState()]->Update(*this, *grid, *info, grid->getX(), grid->getY(), t_diff); + for (GridRefManager<NGridType>::iterator i = GridRefManager<NGridType>::begin(); i != GridRefManager<NGridType>::end(); ) + { + NGridType *grid = i->getSource(); + GridInfo *info = i->getSource()->getGridInfoRef(); + ++i; // The update might delete the map and we need the next map before the iterator gets invalid + assert(grid->GetGridState() >= 0 && grid->GetGridState() < MAX_GRID_STATE); + si_GridStates[grid->GetGridState()]->Update(*this, *grid, *info, grid->getX(), grid->getY(), t_diff); + } } + + ///- Process necessary scripts + if (!m_scriptSchedule.empty()) + ScriptsProcess(); } void Map::Remove(Player *player, bool remove) { - // 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 - if(m_mapRefIter == player->GetMapRef()) - m_mapRefIter = m_mapRefIter->nocheck_prev(); - player->GetMapRef().unlink(); CellPair p = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY()); if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP) { + if(remove) + player->CleanupsBeforeDelete(); + // invalid coordinates player->RemoveFromWorld(); if( remove ) + { + player->ResetMap(); DeleteFromWorld(player); + } return; } @@ -821,6 +841,9 @@ void Map::Remove(Player *player, bool remove) NGridType *grid = getNGrid(cell.GridX(), cell.GridY()); assert(grid != NULL); + if(remove) + player->CleanupsBeforeDelete(); + player->RemoveFromWorld(); RemoveFromGrid(player,grid,cell); @@ -870,6 +893,7 @@ Map::Remove(T *obj, bool remove) UpdateObjectVisibility(obj,cell,p); + obj->ResetMap(); if( remove ) { // if option set then object already saved at this moment @@ -1010,7 +1034,6 @@ void Map::MoveAllCreaturesInMoveList() if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0) sLog.outDebug("Creature (GUID: %u Entry: %u ) can't be move to unloaded respawn grid.",c->GetGUIDLow(),c->GetEntry()); #endif - c->CleanupsBeforeDelete(); AddObjectToRemoveList(c); } } @@ -1121,7 +1144,7 @@ bool Map::UnloadGrid(const uint32 &x, const uint32 &y, bool unloadAll) if(!unloadAll && ActiveObjectsNearGrid(x, y) ) return false; - sLog.outDebug("Unloading grid[%u,%u] for map %u", x,y, i_id); + sLog.outDebug("Unloading grid[%u,%u] for map %u", x,y, GetId()); ObjectGridUnloader unloader(*grid); @@ -1167,13 +1190,29 @@ bool Map::UnloadGrid(const uint32 &x, const uint32 &y, bool unloadAll) VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gy, gx); } else - ((MapInstanced*)(MapManager::Instance().CreateBaseMap(i_id)))->RemoveGridMapReference(GridPair(gx, gy)); + ((MapInstanced*)m_parentMap)->RemoveGridMapReference(GridPair(gx, gy)); + GridMaps[gx][gy] = NULL; } - DEBUG_LOG("Unloading grid[%u,%u] for map %u finished", x,y, i_id); + DEBUG_LOG("Unloading grid[%u,%u] for map %u finished", x,y, GetId()); return true; } +void Map::RemoveAllPlayers() +{ + if(HavePlayers()) + { + sLog.outError("Map::UnloadAll: there are still players in the instance at unload, should not happen!"); + + for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) + { + Player* plr = itr->getSource(); + if(!plr->IsBeingTeleportedFar()) + plr->TeleportTo(plr->m_homebindMapId, plr->m_homebindX, plr->m_homebindY, plr->m_homebindZ, plr->GetOrientation()); + } + } +} + void Map::UnloadAll() { // clear all delayed moves, useless anyway do this moves before map unload. @@ -1297,11 +1336,11 @@ bool GridMap::loadHeihgtData(FILE *in, uint32 offset, uint32 size) map_heightHeader header; fseek(in, offset, SEEK_SET); fread(&header, sizeof(header), 1, in); - if (header.fourcc != uint32(MAP_HEIGTH_MAGIC)) + if (header.fourcc != uint32(MAP_HEIGHT_MAGIC)) return false; m_gridHeight = header.gridHeight; - if (!(header.flags & MAP_HEIGHT_NO_HIGHT)) + if (!(header.flags & MAP_HEIGHT_NO_HEIGHT)) { if ((header.flags & MAP_HEIGHT_AS_INT16)) { @@ -1350,12 +1389,12 @@ bool GridMap::loadLiquidData(FILE *in, uint32 offset, uint32 size) m_liquid_height= header.height; m_liquidLevel = header.liquidLevel; - if (!(header.flags&MAP_LIQUID_NO_TYPE)) + if (!(header.flags & MAP_LIQUID_NO_TYPE)) { m_liquid_type = new uint8 [16*16]; fread(m_liquid_type, sizeof(uint8), 16*16, in); } - if (!(header.flags&MAP_LIQUID_NO_HIGHT)) + if (!(header.flags & MAP_LIQUID_NO_HEIGHT)) { m_liquid_map = new float [m_liquid_width*m_liquid_height]; fread(m_liquid_map, sizeof(float), m_liquid_width*m_liquid_height, in); @@ -1792,7 +1831,7 @@ uint16 Map::GetAreaFlag(float x, float y, float z) const areaflag = gmap->getArea(x, y); // this used while not all *.map files generated (instances) else - areaflag = GetAreaFlagByMapId(i_id); + areaflag = GetAreaFlagByMapId(GetId()); //FIXME: some hacks for areas above or underground for ground area // required for area specific spells/etc, until map/vmap data @@ -1990,7 +2029,7 @@ void Map::GetZoneAndAreaIdByAreaFlag(uint32& zoneid, uint32& areaid, uint16 area zoneid = entry ? (( entry->zone != 0 ) ? entry->zone : entry->ID) : 0; } -bool Map::IsInWater(float x, float y, float pZ) const +bool Map::IsInWater(float x, float y, float pZ, float min_depth) const { // Check surface in x, y point for liquid if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y)) @@ -1998,7 +2037,7 @@ bool Map::IsInWater(float x, float y, float pZ) const LiquidData liquid_status; if (getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, &liquid_status)) { - if (liquid_status.level - liquid_status.depth_level > 2) + if (liquid_status.level - liquid_status.depth_level > min_depth) return true; } } @@ -2097,7 +2136,7 @@ void Map::SendInitTransports( Player * player ) for (MapManager::TransportSet::const_iterator i = tset.begin(); i != tset.end(); ++i) { // send data for current transport in other place - if((*i) != player->GetTransport() && (*i)->GetMapId()==i_id) + if((*i) != player->GetTransport() && (*i)->GetMapId()==GetId()) { (*i)->BuildCreateUpdateBlockForPlayer(&transData, player); } @@ -2123,7 +2162,7 @@ void Map::SendRemoveTransports( Player * player ) // except used transport for (MapManager::TransportSet::const_iterator i = tset.begin(); i != tset.end(); ++i) - if((*i) != player->GetTransport() && (*i)->GetMapId()!=i_id) + if((*i) != player->GetTransport() && (*i)->GetMapId()!=GetId()) (*i)->BuildOutOfRangeUpdateBlock(&transData); WorldPacket packet; @@ -2151,6 +2190,8 @@ void Map::AddObjectToRemoveList(WorldObject *obj) { assert(obj->GetMapId()==GetId() && obj->GetInstanceId()==GetInstanceId()); + obj->CleanupsBeforeDelete(); // remove or simplify at least cross referenced links + i_objectsToRemove.insert(obj); //sLog.outDebug("Object (GUID: %u TypeId: %u ) added to removing list.",obj->GetGUIDLow(),obj->GetTypeId()); } @@ -2326,8 +2367,8 @@ template void Map::Remove(DynamicObject *, bool); /* ******* Dungeon Instance Maps ******* */ -InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode) - : Map(id, expiry, InstanceId, SpawnMode), +InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode, Map* _parent) + : Map(id, expiry, InstanceId, SpawnMode, _parent), m_resetAfterUnload(false), m_unloadWhenEmpty(false), i_data(NULL), i_script_id(0) { @@ -2388,8 +2429,9 @@ bool InstanceMap::Add(Player *player) { Guard guard(*this); - if(!CanEnter(player)) - return false; + // Check moved to void WorldSession::HandleMoveWorldportAckOpcode() + //if(!CanEnter(player)) + //return false; // Dungeon only code if(IsDungeon()) @@ -2617,15 +2659,7 @@ void InstanceMap::PermBindAllPlayers(Player *player) void InstanceMap::UnloadAll() { - if(HavePlayers()) - { - sLog.outError("InstanceMap::UnloadAll: there are still players in the instance at unload, should not happen!"); - for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) - { - Player* plr = itr->getSource(); - plr->TeleportTo(plr->m_homebindMapId, plr->m_homebindX, plr->m_homebindY, plr->m_homebindZ, plr->GetOrientation()); - } - } + assert(!HavePlayers()); if(m_resetAfterUnload == true) objmgr.DeleteRespawnTimeForInstance(GetInstanceId()); @@ -2662,8 +2696,8 @@ uint32 InstanceMap::GetMaxPlayers() const /* ******* Battleground Instance Maps ******* */ -BattleGroundMap::BattleGroundMap(uint32 id, time_t expiry, uint32 InstanceId) - : Map(id, expiry, InstanceId, DIFFICULTY_NORMAL) +BattleGroundMap::BattleGroundMap(uint32 id, time_t expiry, uint32 InstanceId, Map* _parent) + : Map(id, expiry, InstanceId, DIFFICULTY_NORMAL, _parent) { } @@ -2692,8 +2726,9 @@ bool BattleGroundMap::Add(Player * player) { { Guard guard(*this); - if(!CanEnter(player)) - return false; + //Check moved to void WorldSession::HandleMoveWorldportAckOpcode() + //if(!CanEnter(player)) + //return false; // reset instance validity, battleground maps do not homebind player->m_InstanceValid = true; } @@ -2711,21 +2746,847 @@ void BattleGroundMap::SetUnload() m_unloadTimer = MIN_UNLOAD_DELAY; } -void BattleGroundMap::UnloadAll() +void BattleGroundMap::RemoveAllPlayers() { - while(HavePlayers()) + if(HavePlayers()) { - if(Player * plr = m_mapRefManager.getFirst()->getSource()) + for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) { - plr->TeleportTo(plr->GetBattleGroundEntryPoint()); - // TeleportTo removes the player from this map (if the map exists) -> calls BattleGroundMap::Remove -> invalidates the iterator. - // just in case, remove the player from the list explicitly here as well to prevent a possible infinite loop - // note that this remove is not needed if the code works well in other places - plr->GetMapRef().unlink(); + Player* plr = itr->getSource(); + if(!plr->IsBeingTeleportedFar()) + plr->TeleportTo(plr->GetBattleGroundEntryPoint()); } } +} - Map::UnloadAll(); +/// Put scripts in the execution queue +void Map::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, Object* target) +{ + ///- Find the script map + ScriptMapMap::const_iterator s = scripts.find(id); + if (s == scripts.end()) + return; + + // prepare static data + uint64 sourceGUID = source ? source->GetGUID() : (uint64)0; //some script commands doesn't have source + uint64 targetGUID = target ? target->GetGUID() : (uint64)0; + uint64 ownerGUID = (source->GetTypeId()==TYPEID_ITEM) ? ((Item*)source)->GetOwnerGUID() : (uint64)0; + + ///- Schedule script execution for all scripts in the script map + ScriptMap const *s2 = &(s->second); + bool immedScript = false; + for (ScriptMap::const_iterator iter = s2->begin(); iter != s2->end(); ++iter) + { + ScriptAction sa; + sa.sourceGUID = sourceGUID; + sa.targetGUID = targetGUID; + sa.ownerGUID = ownerGUID; + + sa.script = &iter->second; + m_scriptSchedule.insert(std::pair<time_t, ScriptAction>(time_t(sWorld.GetGameTime() + iter->first), sa)); + if (iter->first == 0) + immedScript = true; + + sWorld.IncreaseScheduledScriptsCount(); + } + ///- If one of the effects should be immediate, launch the script execution + if (/*start &&*/ immedScript) + ScriptsProcess(); +} + +void Map::ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target) +{ + // NOTE: script record _must_ exist until command executed + + // prepare static data + uint64 sourceGUID = source ? source->GetGUID() : (uint64)0; + uint64 targetGUID = target ? target->GetGUID() : (uint64)0; + uint64 ownerGUID = (source->GetTypeId()==TYPEID_ITEM) ? ((Item*)source)->GetOwnerGUID() : (uint64)0; + + ScriptAction sa; + sa.sourceGUID = sourceGUID; + sa.targetGUID = targetGUID; + sa.ownerGUID = ownerGUID; + + sa.script = &script; + m_scriptSchedule.insert(std::pair<time_t, ScriptAction>(time_t(sWorld.GetGameTime() + delay), sa)); + + sWorld.IncreaseScheduledScriptsCount(); + + ///- If effects should be immediate, launch the script execution + if(delay == 0) + ScriptsProcess(); +} + +/// Process queued scripts +void Map::ScriptsProcess() +{ + if (m_scriptSchedule.empty()) + return; + + ///- Process overdue queued scripts + std::multimap<time_t, ScriptAction>::iterator iter = m_scriptSchedule.begin(); + // ok as multimap is a *sorted* associative container + while (!m_scriptSchedule.empty() && (iter->first <= sWorld.GetGameTime())) + { + ScriptAction const& step = iter->second; + + Object* source = NULL; + + if(step.sourceGUID) + { + switch(GUID_HIPART(step.sourceGUID)) + { + case HIGHGUID_ITEM: + // case HIGHGUID_CONTAINER: ==HIGHGUID_ITEM + { + Player* player = HashMapHolder<Player>::Find(step.ownerGUID); + if(player) + source = player->GetItemByGuid(step.sourceGUID); + break; + } + case HIGHGUID_UNIT: + source = HashMapHolder<Creature>::Find(step.sourceGUID); + break; + case HIGHGUID_PET: + source = HashMapHolder<Pet>::Find(step.sourceGUID); + break; + case HIGHGUID_VEHICLE: + source = HashMapHolder<Vehicle>::Find(step.sourceGUID); + break; + case HIGHGUID_PLAYER: + source = HashMapHolder<Player>::Find(step.sourceGUID); + break; + case HIGHGUID_GAMEOBJECT: + source = HashMapHolder<GameObject>::Find(step.sourceGUID); + break; + case HIGHGUID_CORPSE: + source = HashMapHolder<Corpse>::Find(step.sourceGUID); + break; + case HIGHGUID_MO_TRANSPORT: + for (MapManager::TransportSet::iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().m_Transports.end(); ++iter) + { + if((*iter)->GetGUID() == step.sourceGUID) + { + source = reinterpret_cast<Object*>(*iter); + break; + } + } + break; + default: + sLog.outError("*_script source with unsupported high guid value %u",GUID_HIPART(step.sourceGUID)); + break; + } + } + + //if(source && !source->IsInWorld()) source = NULL; + + Object* target = NULL; + + if(step.targetGUID) + { + switch(GUID_HIPART(step.targetGUID)) + { + case HIGHGUID_UNIT: + target = HashMapHolder<Creature>::Find(step.targetGUID); + break; + case HIGHGUID_PET: + target = HashMapHolder<Pet>::Find(step.targetGUID); + break; + case HIGHGUID_VEHICLE: + target = HashMapHolder<Vehicle>::Find(step.targetGUID); + break; + case HIGHGUID_PLAYER: // empty GUID case also + target = HashMapHolder<Player>::Find(step.targetGUID); + break; + case HIGHGUID_GAMEOBJECT: + target = HashMapHolder<GameObject>::Find(step.targetGUID); + break; + case HIGHGUID_CORPSE: + target = HashMapHolder<Corpse>::Find(step.targetGUID); + break; + default: + sLog.outError("*_script source with unsupported high guid value %u",GUID_HIPART(step.targetGUID)); + break; + } + } + + //if(target && !target->IsInWorld()) target = NULL; + + switch (step.script->command) + { + case SCRIPT_COMMAND_TALK: + { + if(!source) + { + sLog.outError("SCRIPT_COMMAND_TALK call for NULL creature."); + break; + } + + if(source->GetTypeId()!=TYPEID_UNIT) + { + sLog.outError("SCRIPT_COMMAND_TALK call for non-creature (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + if(step.script->datalong > 3) + { + sLog.outError("SCRIPT_COMMAND_TALK invalid chat type (%u), skipping.",step.script->datalong); + break; + } + + uint64 unit_target = target ? target->GetGUID() : 0; + + //datalong 0=normal say, 1=whisper, 2=yell, 3=emote text + switch(step.script->datalong) + { + case 0: // Say + ((Creature *)source)->Say(step.script->dataint, LANG_UNIVERSAL, unit_target); + break; + case 1: // Whisper + if(!unit_target) + { + sLog.outError("SCRIPT_COMMAND_TALK attempt to whisper (%u) NULL, skipping.",step.script->datalong); + break; + } + ((Creature *)source)->Whisper(step.script->dataint,unit_target); + break; + case 2: // Yell + ((Creature *)source)->Yell(step.script->dataint, LANG_UNIVERSAL, unit_target); + break; + case 3: // Emote text + ((Creature *)source)->TextEmote(step.script->dataint, unit_target); + break; + default: + break; // must be already checked at load + } + break; + } + + case SCRIPT_COMMAND_EMOTE: + if(!source) + { + sLog.outError("SCRIPT_COMMAND_EMOTE call for NULL creature."); + break; + } + + if(source->GetTypeId()!=TYPEID_UNIT) + { + sLog.outError("SCRIPT_COMMAND_EMOTE call for non-creature (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + ((Creature *)source)->HandleEmoteCommand(step.script->datalong); + break; + case SCRIPT_COMMAND_FIELD_SET: + if(!source) + { + sLog.outError("SCRIPT_COMMAND_FIELD_SET call for NULL object."); + break; + } + if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount()) + { + sLog.outError("SCRIPT_COMMAND_FIELD_SET call for wrong field %u (max count: %u) in object (TypeId: %u).", + step.script->datalong,source->GetValuesCount(),source->GetTypeId()); + break; + } + + source->SetUInt32Value(step.script->datalong, step.script->datalong2); + break; + case SCRIPT_COMMAND_MOVE_TO: + if(!source) + { + sLog.outError("SCRIPT_COMMAND_MOVE_TO call for NULL creature."); + break; + } + + if(source->GetTypeId()!=TYPEID_UNIT) + { + sLog.outError("SCRIPT_COMMAND_MOVE_TO call for non-creature (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + ((Creature*)source)->SendMonsterMoveWithSpeed(step.script->x, step.script->y, step.script->z, step.script->datalong2 ); + ((Creature*)source)->GetMap()->CreatureRelocation(((Creature*)source), step.script->x, step.script->y, step.script->z, 0); + break; + case SCRIPT_COMMAND_FLAG_SET: + if(!source) + { + sLog.outError("SCRIPT_COMMAND_FLAG_SET call for NULL object."); + break; + } + if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount()) + { + sLog.outError("SCRIPT_COMMAND_FLAG_SET call for wrong field %u (max count: %u) in object (TypeId: %u).", + step.script->datalong,source->GetValuesCount(),source->GetTypeId()); + break; + } + + source->SetFlag(step.script->datalong, step.script->datalong2); + break; + case SCRIPT_COMMAND_FLAG_REMOVE: + if(!source) + { + sLog.outError("SCRIPT_COMMAND_FLAG_REMOVE call for NULL object."); + break; + } + if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount()) + { + sLog.outError("SCRIPT_COMMAND_FLAG_REMOVE call for wrong field %u (max count: %u) in object (TypeId: %u).", + step.script->datalong,source->GetValuesCount(),source->GetTypeId()); + break; + } + + source->RemoveFlag(step.script->datalong, step.script->datalong2); + break; + + case SCRIPT_COMMAND_TELEPORT_TO: + { + // accept player in any one from target/source arg + if (!target && !source) + { + sLog.outError("SCRIPT_COMMAND_TELEPORT_TO call for NULL object."); + break; + } + + // must be only Player + if((!target || target->GetTypeId() != TYPEID_PLAYER) && (!source || source->GetTypeId() != TYPEID_PLAYER)) + { + sLog.outError("SCRIPT_COMMAND_TELEPORT_TO call for non-player (TypeIdSource: %u)(TypeIdTarget: %u), skipping.", source ? source->GetTypeId() : 0, target ? target->GetTypeId() : 0); + break; + } + + Player* pSource = target && target->GetTypeId() == TYPEID_PLAYER ? (Player*)target : (Player*)source; + + pSource->TeleportTo(step.script->datalong, step.script->x, step.script->y, step.script->z, step.script->o); + break; + } + + case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: + { + if(!step.script->datalong) // creature not specified + { + sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for NULL creature."); + break; + } + + if(!source) + { + sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for NULL world object."); + break; + } + + WorldObject* summoner = dynamic_cast<WorldObject*>(source); + + if(!summoner) + { + sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for non-WorldObject (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + float x = step.script->x; + float y = step.script->y; + float z = step.script->z; + float o = step.script->o; + + Creature* pCreature = summoner->SummonCreature(step.script->datalong, x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,step.script->datalong2); + if (!pCreature) + { + sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON failed for creature (entry: %u).",step.script->datalong); + break; + } + + break; + } + + case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: + { + if(!step.script->datalong) // gameobject not specified + { + sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for NULL gameobject."); + break; + } + + if(!source) + { + sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for NULL world object."); + break; + } + + WorldObject* summoner = dynamic_cast<WorldObject*>(source); + + if(!summoner) + { + sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for non-WorldObject (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + GameObject *go = NULL; + int32 time_to_despawn = step.script->datalong2<5 ? 5 : (int32)step.script->datalong2; + + CellPair p(MaNGOS::ComputeCellPair(summoner->GetPositionX(), summoner->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + + MaNGOS::GameObjectWithDbGUIDCheck go_check(*summoner,step.script->datalong); + MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(summoner, go,go_check); + + TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); + CellLock<GridReadGuard> cell_lock(cell, p); + cell_lock->Visit(cell_lock, object_checker, *summoner->GetMap()); + + if ( !go ) + { + sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT failed for gameobject(guid: %u).", step.script->datalong); + break; + } + + if( go->GetGoType()==GAMEOBJECT_TYPE_FISHINGNODE || + go->GetGoType()==GAMEOBJECT_TYPE_DOOR || + go->GetGoType()==GAMEOBJECT_TYPE_BUTTON || + go->GetGoType()==GAMEOBJECT_TYPE_TRAP ) + { + sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT can not be used with gameobject of type %u (guid: %u).", uint32(go->GetGoType()), step.script->datalong); + break; + } + + if( go->isSpawned() ) + break; //gameobject already spawned + + go->SetLootState(GO_READY); + go->SetRespawnTime(time_to_despawn); //despawn object in ? seconds + + go->GetMap()->Add(go); + break; + } + case SCRIPT_COMMAND_OPEN_DOOR: + { + if(!step.script->datalong) // door not specified + { + sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for NULL door."); + break; + } + + if(!source) + { + sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for NULL unit."); + break; + } + + if(!source->isType(TYPEMASK_UNIT)) // must be any Unit (creature or player) + { + sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for non-unit (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + Unit* caster = (Unit*)source; + + GameObject *door = NULL; + int32 time_to_close = step.script->datalong2 < 15 ? 15 : (int32)step.script->datalong2; + + CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + + MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong); + MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(caster,door,go_check); + + TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); + CellLock<GridReadGuard> cell_lock(cell, p); + cell_lock->Visit(cell_lock, object_checker, *caster->GetMap()); + + if (!door) + { + sLog.outError("SCRIPT_COMMAND_OPEN_DOOR failed for gameobject(guid: %u).", step.script->datalong); + break; + } + if (door->GetGoType() != GAMEOBJECT_TYPE_DOOR) + { + sLog.outError("SCRIPT_COMMAND_OPEN_DOOR failed for non-door(GoType: %u).", door->GetGoType()); + break; + } + + if (door->GetGoState() != GO_STATE_READY) + break; //door already open + + door->UseDoorOrButton(time_to_close); + + if(target && target->isType(TYPEMASK_GAMEOBJECT) && ((GameObject*)target)->GetGoType()==GAMEOBJECT_TYPE_BUTTON) + ((GameObject*)target)->UseDoorOrButton(time_to_close); + break; + } + case SCRIPT_COMMAND_CLOSE_DOOR: + { + if(!step.script->datalong) // guid for door not specified + { + sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for NULL door."); + break; + } + + if(!source) + { + sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for NULL unit."); + break; + } + + if(!source->isType(TYPEMASK_UNIT)) // must be any Unit (creature or player) + { + sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for non-unit (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + Unit* caster = (Unit*)source; + + GameObject *door = NULL; + int32 time_to_open = step.script->datalong2 < 15 ? 15 : (int32)step.script->datalong2; + + CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + + MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong); + MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(caster,door,go_check); + + TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); + CellLock<GridReadGuard> cell_lock(cell, p); + cell_lock->Visit(cell_lock, object_checker, *caster->GetMap()); + + if ( !door ) + { + sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR failed for gameobject(guid: %u).", step.script->datalong); + break; + } + if ( door->GetGoType() != GAMEOBJECT_TYPE_DOOR ) + { + sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR failed for non-door(GoType: %u).", door->GetGoType()); + break; + } + + if( door->GetGoState() == GO_STATE_READY ) + break; //door already closed + + door->UseDoorOrButton(time_to_open); + + if(target && target->isType(TYPEMASK_GAMEOBJECT) && ((GameObject*)target)->GetGoType()==GAMEOBJECT_TYPE_BUTTON) + ((GameObject*)target)->UseDoorOrButton(time_to_open); + + break; + } + case SCRIPT_COMMAND_QUEST_EXPLORED: + { + if(!source) + { + sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for NULL source."); + break; + } + + if(!target) + { + sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for NULL target."); + break; + } + + // when script called for item spell casting then target == (unit or GO) and source is player + WorldObject* worldObject; + Player* player; + + if(target->GetTypeId()==TYPEID_PLAYER) + { + if(source->GetTypeId()!=TYPEID_UNIT && source->GetTypeId()!=TYPEID_GAMEOBJECT) + { + sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-creature and non-gameobject (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + worldObject = (WorldObject*)source; + player = (Player*)target; + } + else + { + if(target->GetTypeId()!=TYPEID_UNIT && target->GetTypeId()!=TYPEID_GAMEOBJECT) + { + sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-creature and non-gameobject (TypeId: %u), skipping.",target->GetTypeId()); + break; + } + + if(source->GetTypeId()!=TYPEID_PLAYER) + { + sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-player(TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + worldObject = (WorldObject*)target; + player = (Player*)source; + } + + // quest id and flags checked at script loading + if( (worldObject->GetTypeId()!=TYPEID_UNIT || ((Unit*)worldObject)->isAlive()) && + (step.script->datalong2==0 || worldObject->IsWithinDistInMap(player,float(step.script->datalong2))) ) + player->AreaExploredOrEventHappens(step.script->datalong); + else + player->FailQuest(step.script->datalong); + + break; + } + + case SCRIPT_COMMAND_ACTIVATE_OBJECT: + { + if(!source) + { + sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT must have source caster."); + break; + } + + if(!source->isType(TYPEMASK_UNIT)) + { + sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT source caster isn't unit (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + if(!target) + { + sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT call for NULL gameobject."); + break; + } + + if(target->GetTypeId()!=TYPEID_GAMEOBJECT) + { + sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT call for non-gameobject (TypeId: %u), skipping.",target->GetTypeId()); + break; + } + + Unit* caster = (Unit*)source; + + GameObject *go = (GameObject*)target; + + go->Use(caster); + break; + } + + case SCRIPT_COMMAND_REMOVE_AURA: + { + Object* cmdTarget = step.script->datalong2 ? source : target; + + if(!cmdTarget) + { + sLog.outError("SCRIPT_COMMAND_REMOVE_AURA call for NULL %s.",step.script->datalong2 ? "source" : "target"); + break; + } + + if(!cmdTarget->isType(TYPEMASK_UNIT)) + { + sLog.outError("SCRIPT_COMMAND_REMOVE_AURA %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 ? "source" : "target",cmdTarget->GetTypeId()); + break; + } + + ((Unit*)cmdTarget)->RemoveAurasDueToSpell(step.script->datalong); + break; + } + + case SCRIPT_COMMAND_CAST_SPELL: + { + if(!source) + { + sLog.outError("SCRIPT_COMMAND_CAST_SPELL must have source caster."); + break; + } + + Object* cmdTarget = step.script->datalong2 & 0x01 ? source : target; + + if(!cmdTarget) + { + sLog.outError("SCRIPT_COMMAND_CAST_SPELL call for NULL %s.",step.script->datalong2 & 0x01 ? "source" : "target"); + break; + } + + if(!cmdTarget->isType(TYPEMASK_UNIT)) + { + sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 & 0x01 ? "source" : "target",cmdTarget->GetTypeId()); + break; + } + + Unit* spellTarget = (Unit*)cmdTarget; + + Object* cmdSource = step.script->datalong2 & 0x02 ? target : source; + + if(!cmdSource) + { + sLog.outError("SCRIPT_COMMAND_CAST_SPELL call for NULL %s.",step.script->datalong2 & 0x02 ? "target" : "source"); + break; + } + + if(!cmdSource->isType(TYPEMASK_UNIT)) + { + sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 & 0x02 ? "target" : "source", cmdSource->GetTypeId()); + break; + } + + Unit* spellSource = (Unit*)cmdSource; + + //TODO: when GO cast implemented, code below must be updated accordingly to also allow GO spell cast + spellSource->CastSpell(spellTarget,step.script->datalong,false); + + break; + } + + case SCRIPT_COMMAND_LOAD_PATH: + { + if(!source) + { + sLog.outError("SCRIPT_COMMAND_START_MOVE is tried to apply to NON-existing unit."); + break; + } + + if(!source->isType(TYPEMASK_UNIT)) + { + sLog.outError("SCRIPT_COMMAND_START_MOVE source mover isn't unit (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + if(!WaypointMgr.GetPath(step.script->datalong)) + { + sLog.outError("SCRIPT_COMMAND_START_MOVE source mover has an invallid path, skipping.", step.script->datalong2); + break; + } + + dynamic_cast<Unit*>(source)->GetMotionMaster()->MovePath(step.script->datalong, step.script->datalong2); + break; + } + + case SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT: + { + if(!step.script->datalong || !step.script->datalong2) + { + sLog.outError("SCRIPT_COMMAND_CALLSCRIPT calls invallid db_script_id or lowguid not present: skipping."); + break; + } + //our target + Creature* target = NULL; + + if(source) //using grid searcher + { + CellPair p(Trinity::ComputeCellPair(((Unit*)source)->GetPositionX(), ((Unit*)source)->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + + //sLog.outDebug("Attempting to find Creature: Db GUID: %i", step.script->datalong); + Trinity::CreatureWithDbGUIDCheck target_check(((Unit*)source), step.script->datalong); + Trinity::CreatureSearcher<Trinity::CreatureWithDbGUIDCheck> checker(((Unit*)source), target, target_check); + + TypeContainerVisitor<Trinity::CreatureSearcher <Trinity::CreatureWithDbGUIDCheck>, GridTypeMapContainer > unit_checker(checker); + CellLock<GridReadGuard> cell_lock(cell, p); + cell_lock->Visit(cell_lock, unit_checker, *(((Unit*)source)->GetMap())); + } + else //check hashmap holders + { + if(CreatureData const* data = objmgr.GetCreatureData(step.script->datalong)) + target = ObjectAccessor::GetObjectInWorld<Creature>(data->mapid, data->posX, data->posY, MAKE_NEW_GUID(step.script->datalong, data->id, HIGHGUID_UNIT), target); + } + //sLog.outDebug("attempting to pass target..."); + if(!target) + break; + //sLog.outDebug("target passed"); + //Lets choose our ScriptMap map + ScriptMapMap *datamap = NULL; + switch(step.script->dataint) + { + case 1://QUEST END SCRIPTMAP + datamap = &sQuestEndScripts; + break; + case 2://QUEST START SCRIPTMAP + datamap = &sQuestStartScripts; + break; + case 3://SPELLS SCRIPTMAP + datamap = &sSpellScripts; + break; + case 4://GAMEOBJECTS SCRIPTMAP + datamap = &sGameObjectScripts; + break; + case 5://EVENTS SCRIPTMAP + datamap = &sEventScripts; + break; + case 6://WAYPOINTS SCRIPTMAP + datamap = &sWaypointScripts; + break; + default: + sLog.outError("SCRIPT_COMMAND_CALLSCRIPT ERROR: no scriptmap present... ignoring"); + break; + } + //if no scriptmap present... + if(!datamap) + break; + + uint32 script_id = step.script->datalong2; + //insert script into schedule but do not start it + ScriptsStart(*datamap, script_id, target, NULL/*, false*/); + break; + } + + case SCRIPT_COMMAND_KILL: + { + if(!source || ((Creature*)source)->isDead()) + break; + + ((Creature*)source)->DealDamage(((Creature*)source), ((Creature*)source)->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + switch(step.script->dataint) + { + case 0: break; //return false not remove corpse + case 1: ((Creature*)source)->RemoveCorpse(); break; + } + break; + } + + case SCRIPT_COMMAND_PLAY_SOUND: + { + if(!source) + { + sLog.outError("SCRIPT_COMMAND_PLAY_SOUND call for NULL creature."); + break; + } + + WorldObject* pSource = dynamic_cast<WorldObject*>(source); + if(!pSource) + { + sLog.outError("SCRIPT_COMMAND_PLAY_SOUND call for non-world object (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + // bitmask: 0/1=anyone/target, 0/2=with distance dependent + Player* pTarget = NULL; + if(step.script->datalong2 & 1) + { + if(!target) + { + sLog.outError("SCRIPT_COMMAND_PLAY_SOUND in targeted mode call for NULL target."); + break; + } + + if(target->GetTypeId()!=TYPEID_PLAYER) + { + sLog.outError("SCRIPT_COMMAND_PLAY_SOUND in targeted mode call for non-player (TypeId: %u), skipping.",target->GetTypeId()); + break; + } + + pTarget = (Player*)target; + } + + // bitmask: 0/1=anyone/target, 0/2=with distance dependent + if(step.script->datalong2 & 2) + pSource->PlayDistanceSound(step.script->datalong,pTarget); + else + pSource->PlayDirectSound(step.script->datalong,pTarget); + break; + } + default: + sLog.outError("Unknown script command %u called.",step.script->command); + break; + } + + m_scriptSchedule.erase(iter); + sWorld.DecreaseScheduledScriptCount(); + + iter = m_scriptSchedule.begin(); + } + return; } Creature* @@ -2774,3 +3635,10 @@ Map::GetDynamicObject(uint64 guid) return NULL; return ret; } + +void Map::UpdateIteratorBack(Player *player) +{ + if(m_mapRefIter == player->GetMapRef()) + m_mapRefIter = m_mapRefIter->nocheck_prev(); +} + diff --git a/src/game/Map.h b/src/game/Map.h index 4caa1942c3c..68d5b90f48d 100644 --- a/src/game/Map.h +++ b/src/game/Map.h @@ -43,9 +43,13 @@ class WorldPacket; class InstanceData; class Group; class InstanceSave; +class Object; class WorldObject; class TempSummon; +class Player; class CreatureGroup; +struct ScriptInfo; +struct ScriptAction; typedef ACE_RW_Thread_Mutex GridRWLock; @@ -74,10 +78,11 @@ typedef MaNGOS::SingleThreaded<GridRWLock>::Lock NullGuard; #define MAP_MAGIC 'SPAM' #define MAP_VERSION_MAGIC '0.1w' #define MAP_AREA_MAGIC 'AERA' -#define MAP_HEIGTH_MAGIC 'TGHM' +#define MAP_HEIGHT_MAGIC 'TGHM' #define MAP_LIQUID_MAGIC 'QILM' -struct map_fileheader{ +struct map_fileheader +{ uint32 mapMagic; uint32 versionMagic; uint32 areaMapOffset; @@ -89,17 +94,20 @@ struct map_fileheader{ }; #define MAP_AREA_NO_AREA 0x0001 -struct map_areaHeader{ + +struct map_areaHeader +{ uint32 fourcc; uint16 flags; uint16 gridArea; }; -#define MAP_HEIGHT_NO_HIGHT 0x0001 +#define MAP_HEIGHT_NO_HEIGHT 0x0001 #define MAP_HEIGHT_AS_INT16 0x0002 #define MAP_HEIGHT_AS_INT8 0x0004 -struct map_heightHeader{ +struct map_heightHeader +{ uint32 fourcc; uint32 flags; float gridHeight; @@ -107,8 +115,10 @@ struct map_heightHeader{ }; #define MAP_LIQUID_NO_TYPE 0x0001 -#define MAP_LIQUID_NO_HIGHT 0x0002 -struct map_liquidHeader{ +#define MAP_LIQUID_NO_HEIGHT 0x0002 + +struct map_liquidHeader +{ uint32 fourcc; uint16 flags; uint16 liquidType; @@ -119,7 +129,8 @@ struct map_liquidHeader{ float liquidLevel; }; -enum ZLiquidStatus{ +enum ZLiquidStatus +{ LIQUID_MAP_NO_WATER = 0x00000000, LIQUID_MAP_ABOVE_WATER = 0x00000001, LIQUID_MAP_WATER_WALK = 0x00000002, @@ -138,7 +149,8 @@ enum ZLiquidStatus{ #define MAP_LIQUID_TYPE_DARK_WATER 0x10 #define MAP_LIQUID_TYPE_WMO_WATER 0x20 -struct LiquidData{ +struct LiquidData +{ uint32 type; float level; float depth_level; @@ -251,7 +263,7 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj { friend class MapReference; public: - Map(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode); + Map(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode, Map* _parent = NULL); virtual ~Map(); // currently unused for normal maps @@ -298,7 +310,7 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj } time_t GetGridExpiry(void) const { return i_gridExpiry; } - uint32 GetId(void) const { return i_id; } + uint32 GetId(void) const { return i_mapEntry->MapID; } static bool ExistMap(uint32 mapid, int gx, int gy); static bool ExistVMap(uint32 mapid, int gx, int gy); @@ -306,11 +318,13 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj static void InitStateMachine(); static void DeleteStateMachine(); + Map const * GetParent() const { return m_parentMap; } + // some calls like isInWater should not use vmaps due to processor power // can return INVALID_HEIGHT if under z+2 z coord not found height float GetHeight(float x, float y, float z, bool pCheckVMap=true) const; float GetVmapHeight(float x, float y, float z, bool useMaps) const; - bool IsInWater(float x, float y, float z) const; // does not use z pos. This is for future use + bool IsInWater(float x, float y, float z, float min_depth = 2.0f) const; ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData *data = 0) const; @@ -325,22 +339,23 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj uint32 GetAreaId(float x, float y, float z) const { - return GetAreaIdByAreaFlag(GetAreaFlag(x,y,z),i_id); + return GetAreaIdByAreaFlag(GetAreaFlag(x,y,z),GetId()); } uint32 GetZoneId(float x, float y, float z) const { - return GetZoneIdByAreaFlag(GetAreaFlag(x,y,z),i_id); + return GetZoneIdByAreaFlag(GetAreaFlag(x,y,z),GetId()); } void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const { - GetZoneAndAreaIdByAreaFlag(zoneid,areaid,GetAreaFlag(x,y,z),i_id); + GetZoneAndAreaIdByAreaFlag(zoneid,areaid,GetAreaFlag(x,y,z),GetId()); } virtual void MoveAllCreaturesInMoveList(); virtual void RemoveAllObjectsInRemoveList(); virtual void RelocationNotify(); + virtual void RemoveAllPlayers(); bool CreatureRespawnRelocation(Creature *c); // used only in MoveAllCreaturesInMoveList and ObjectGridUnloader @@ -389,13 +404,17 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj bool ActiveObjectsNearGrid(uint32 x, uint32 y) const; void AddUnitToNotify(Unit* unit); - void RemoveUnitFromNotify(int32 slot); + void RemoveUnitFromNotify(Unit *unit, int32 slot); void SendToPlayers(WorldPacket const* data) const; typedef MapRefManager PlayerList; PlayerList const& GetPlayers() const { return m_mapRefManager; } + //per-map script storage + void ScriptsStart(std::map<uint32, std::multimap<uint32, ScriptInfo> > const& scripts, uint32 id, Object* source, Object* target); + void ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target); + // must called with AddToWorld template<class T> void AddToActive(T* obj) { AddToActiveHelper(obj); } @@ -414,6 +433,8 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj template<class NOTIFIER> void VisitGrid(const float &x, const float &y, float radius, NOTIFIER ¬ifier); CreatureGroupHolderType CreatureGroupHolder; + void UpdateIteratorBack(Player *player); + #ifdef MAP_BASED_RAND_GEN MTRand mtRand; int32 irand(int32 min, int32 max) { return int32 (mtRand.randInt(max - min)) + min; } @@ -465,6 +486,7 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj void setGridObjectDataLoaded(bool pLoaded, uint32 x, uint32 y) { getNGrid(x,y)->setGridObjectDataLoaded(pLoaded); } void setNGrid(NGridType* grid, uint32 x, uint32 y); + void ScriptsProcess(); void UpdateActiveCells(const float &x, const float &y, const uint32 &t_diff); protected: @@ -474,7 +496,6 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj MapEntry const* i_mapEntry; uint8 i_spawnMode; - uint32 i_id; uint32 i_InstanceId; uint32 m_unloadTimer; @@ -487,6 +508,10 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj private: + //used for fast base_map (e.g. MapInstanced class object) search for + //InstanceMaps and BattleGroundMaps... + Map* m_parentMap; + typedef GridReadGuard ReadGuard; typedef GridWriteGuard WriteGuard; @@ -502,6 +527,7 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj std::vector<Unit*> i_unitsToNotify; std::set<WorldObject *> i_objectsToRemove; std::map<WorldObject*, bool> i_objectsToSwitch; + std::multimap<time_t, ScriptAction> m_scriptSchedule; // Type specific code for add/remove to/from grid template<class T> @@ -553,7 +579,7 @@ enum InstanceResetMethod class TRINITY_DLL_SPEC InstanceMap : public Map { public: - InstanceMap(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode); + InstanceMap(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode, Map* _parent); ~InstanceMap(); bool Add(Player *); void Remove(Player *, bool); @@ -578,14 +604,14 @@ class TRINITY_DLL_SPEC InstanceMap : public Map class TRINITY_DLL_SPEC BattleGroundMap : public Map { public: - BattleGroundMap(uint32 id, time_t, uint32 InstanceId); + BattleGroundMap(uint32 id, time_t, uint32 InstanceId, Map* _parent); ~BattleGroundMap(); bool Add(Player *); void Remove(Player *, bool); bool CanEnter(Player* player); void SetUnload(); - void UnloadAll(); + void RemoveAllPlayers(); }; /*inline diff --git a/src/game/MapInstanced.cpp b/src/game/MapInstanced.cpp index ca106d6506c..e423abbad31 100644 --- a/src/game/MapInstanced.cpp +++ b/src/game/MapInstanced.cpp @@ -46,7 +46,10 @@ void MapInstanced::Update(const uint32& t) { if(i->second->CanUnload(t)) { - DestroyInstance(i); // iterator incremented + if(!DestroyInstance(i)) // iterator incremented + { + //m_unloadTimer + } } else { @@ -116,17 +119,8 @@ void MapInstanced::UnloadAll() - create the instance if it's not created already - the player is not actually added to the instance (only in InstanceMap::Add) */ -Map* MapInstanced::GetInstance(const WorldObject* obj) +Map* MapInstanced::CreateInstance(const uint32 mapId, Player * player, uint32 instanceId) { - if(obj->GetTypeId() == TYPEID_UNIT) - { - assert(obj->GetMapId() == GetId() && obj->GetInstanceId()); - return _FindMap(obj->GetInstanceId()); - } - - Player* player = (Player*)obj; - uint32 instanceId = player->GetInstanceId(); - if(instanceId) if(Map *map = _FindMap(instanceId)) return map; @@ -134,28 +128,12 @@ Map* MapInstanced::GetInstance(const WorldObject* obj) if(IsBattleGroundOrArena()) { instanceId = player->GetBattleGroundId(); - if(instanceId) - { if(Map *map = _FindMap(instanceId)) return map; - else - return CreateBattleGround(instanceId); - } - else - return NULL; - } - - InstancePlayerBind *pBind = player->GetBoundInstance(GetId(), player->GetDifficulty()); - InstanceSave *pSave = pBind ? pBind->save : NULL; - if(!pBind || !pBind->perm) - { - if(Group *group = player->GetGroup()) - if(InstanceGroupBind *groupBind = group->GetBoundInstance(GetId(), player->GetDifficulty())) - pSave = groupBind->save; + return CreateBattleGround(instanceId); } - - if(pSave) + else if(InstanceSave *pSave = player->GetInstanceSave(GetId())) { if(!instanceId) { @@ -202,10 +180,10 @@ InstanceMap* MapInstanced::CreateInstance(uint32 InstanceId, InstanceSave *save, // some instances only have one difficulty if (entry && !entry->SupportsHeroicMode()) difficulty = DIFFICULTY_NORMAL; - sLog.outDebug("MapInstanced::CreateInstance: %smap instance %d for %d created with difficulty %s", save?"":"new ", InstanceId, GetId(), difficulty?"heroic":"normal"); + sLog.outDebug("MapInstanced::CreateInstance: %s map instance %d for %d created with difficulty %s", save?"":"new ", InstanceId, GetId(), difficulty?"heroic":"normal"); - InstanceMap *map = new InstanceMap(GetId(), GetGridExpiry(), InstanceId, difficulty); - assert(map->IsDungeon()); + InstanceMap *map = new InstanceMap(GetId(), GetGridExpiry(), InstanceId, difficulty, this); + ASSERT(map->IsDungeon()); bool load_data = save != NULL; map->CreateInstanceData(load_data); @@ -221,23 +199,23 @@ BattleGroundMap* MapInstanced::CreateBattleGround(uint32 InstanceId) sLog.outDebug("MapInstanced::CreateBattleGround: map bg %d for %d created.", InstanceId, GetId()); - BattleGroundMap *map = new BattleGroundMap(GetId(), GetGridExpiry(), InstanceId); - assert(map->IsBattleGroundOrArena()); + BattleGroundMap *map = new BattleGroundMap(GetId(), GetGridExpiry(), InstanceId, this); + ASSERT(map->IsBattleGroundOrArena()); m_InstancedMaps[InstanceId] = map; return map; } -void MapInstanced::DestroyInstance(uint32 InstanceId) -{ - InstancedMaps::iterator itr = m_InstancedMaps.find(InstanceId); - if(itr != m_InstancedMaps.end()) - DestroyInstance(itr); -} - // increments the iterator after erase -void MapInstanced::DestroyInstance(InstancedMaps::iterator &itr) +bool MapInstanced::DestroyInstance(InstancedMaps::iterator &itr) { + itr->second->RemoveAllPlayers(); + if(itr->second->HavePlayers()) + { + ++itr; + return false; + } + itr->second->UnloadAll(); // should only unload VMaps if this is the last instance and grid unloading is enabled if(m_InstancedMaps.size() <= 1 && sWorld.getConfig(CONFIG_GRID_UNLOAD)) @@ -250,13 +228,12 @@ void MapInstanced::DestroyInstance(InstancedMaps::iterator &itr) // erase map delete itr->second; m_InstancedMaps.erase(itr++); + return true; } bool MapInstanced::CanEnter(Player *player) { - if(Map* map = GetInstance(player)) - return map->CanEnter(player); - - return false; + //assert(false); + return true; } diff --git a/src/game/MapInstanced.h b/src/game/MapInstanced.h index 6338726fd47..851fe8942a0 100644 --- a/src/game/MapInstanced.h +++ b/src/game/MapInstanced.h @@ -42,10 +42,9 @@ class TRINITY_DLL_DECL MapInstanced : public Map void UnloadAll(); bool CanEnter(Player* player); - Map* GetInstance(const WorldObject* obj); + Map* CreateInstance(const uint32 mapId, Player * player, uint32 instanceId); Map* FindMap(uint32 InstanceId) const { return _FindMap(InstanceId); } - void DestroyInstance(uint32 InstanceId); - void DestroyInstance(InstancedMaps::iterator &itr); + bool DestroyInstance(InstancedMaps::iterator &itr); void AddGridMapReference(const GridPair &p) { diff --git a/src/game/MapManager.cpp b/src/game/MapManager.cpp index 5fde20c55eb..297bc1a3874 100644 --- a/src/game/MapManager.cpp +++ b/src/game/MapManager.cpp @@ -117,10 +117,14 @@ MapManager::_createBaseMap(uint32 id) { m = new MapInstanced(id, i_gridCleanUpDelay); } - else + else if (entry) { m = new Map(id, i_gridCleanUpDelay, 0, 0); } + else + { + assert(false); + } i_maps[id] = m; } @@ -128,13 +132,13 @@ MapManager::_createBaseMap(uint32 id) return m; } -Map* MapManager::GetMap(uint32 id, const WorldObject* obj) +Map* MapManager::CreateMap(uint32 id, const WorldObject* obj, uint32 instanceId) { ASSERT(obj); //if(!obj->IsInWorld()) sLog.outError("GetMap: called for map %d with object (typeid %d, guid %d, mapid %d, instanceid %d) who is not in world!", id, obj->GetTypeId(), obj->GetGUIDLow(), obj->GetMapId(), obj->GetInstanceId()); Map *m = _createBaseMap(id); - if (m && obj && m->Instanceable()) m = ((MapInstanced*)m)->GetInstance(obj); + if (m && (obj->GetTypeId() == TYPEID_PLAYER) && m->Instanceable()) m = ((MapInstanced*)m)->CreateInstance(id, (Player*)obj, instanceId); return m; } @@ -230,13 +234,6 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player) return true; } -void MapManager::DeleteInstance(uint32 mapid, uint32 instanceId) -{ - Map *m = _createBaseMap(mapid); - if (m && m->Instanceable()) - ((MapInstanced*)m)->DestroyInstance(instanceId); -} - void MapManager::RemoveBonesFromMap(uint32 mapid, uint64 guid, float x, float y) { bool remove_result = _createBaseMap(mapid)->RemoveBones(guid, x, y); @@ -259,20 +256,19 @@ MapManager::Update(uint32 diff) MapMapType::iterator iter; std::vector<Map*> update_queue(i_maps.size()); int omp_set_num_threads(sWorld.getConfig(CONFIG_NUMTHREADS)); - for(iter = i_maps.begin(), i=0;iter != i_maps.end(); ++iter, i++) - update_queue[i]=iter->second; + for(iter = i_maps.begin(), i=0;iter != i_maps.end(); ++iter, i++) + update_queue[i]=iter->second; /* - gomp in gcc <4.4 version cannot parallelise loops using random access iterators - so until gcc 4.4 isnt standard, we need the update_queue workaround + gomp in gcc <4.4 version cannot parallelise loops using random access iterators + so until gcc 4.4 isnt standard, we need the update_queue workaround */ #pragma omp parallel for schedule(dynamic) private(i) shared(update_queue) for(int32 i = 0; i < i_maps.size(); ++i) { checkAndCorrectGridStatesArray(); // debugging code, should be deleted some day - update_queue[i]->Update(i_timer.GetCurrent()); - sWorld.RecordTimeDiff("UpdateMap %u", update_queue[i]->GetId()); - // sLog.outError("This is thread %d out of %d threads,updating map %u",omp_get_thread_num(),omp_get_num_threads(),iter->second->GetId()); - + update_queue[i]->Update(i_timer.GetCurrent()); + sWorld.RecordTimeDiff("UpdateMap %u", update_queue[i]->GetId()); + // sLog.outError("This is thread %d out of %d threads,updating map %u",omp_get_thread_num(),omp_get_num_threads(),iter->second->GetId()); } #else for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter) diff --git a/src/game/MapManager.h b/src/game/MapManager.h index b07a26ed0f0..118a057c9af 100644 --- a/src/game/MapManager.h +++ b/src/game/MapManager.h @@ -39,13 +39,10 @@ class MANGOS_DLL_DECL MapManager : public MaNGOS::Singleton<MapManager, MaNGOS:: public: - Map* GetMap(uint32, const WorldObject* obj); + Map* CreateMap(uint32, const WorldObject* obj, uint32 instanceId); Map const* CreateBaseMap(uint32 id) const { return const_cast<MapManager*>(this)->_createBaseMap(id); } Map* FindMap(uint32 mapid, uint32 instanceId = 0) const; - // only const version for outer users - void DeleteInstance(uint32 mapid, uint32 instanceId); - uint16 GetAreaFlag(uint32 mapid, float x, float y, float z) const { Map const* m = CreateBaseMap(mapid); diff --git a/src/game/MiscHandler.cpp b/src/game/MiscHandler.cpp index f5a1013f085..48fdda872e9 100644 --- a/src/game/MiscHandler.cpp +++ b/src/game/MiscHandler.cpp @@ -231,7 +231,7 @@ void WorldSession::HandleWhoOpcode( WorldPacket & recv_data ) uint32 team = _player->GetTeam(); uint32 security = GetSecurity(); bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST); - bool gmInWhoList = sWorld.getConfig(CONFIG_GM_IN_WHO_LIST); + uint32 gmLevelInWhoList = sWorld.getConfig(CONFIG_GM_LEVEL_IN_WHO_LIST); WorldPacket data( SMSG_WHO, 50 ); // guess size data << clientcount; // clientcount place holder @@ -248,10 +248,14 @@ void WorldSession::HandleWhoOpcode( WorldPacket & recv_data ) continue; // player can see MODERATOR, GAME MASTER, ADMINISTRATOR only if CONFIG_GM_IN_WHO_LIST - if ((itr->second->GetSession()->GetSecurity() > SEC_PLAYER && !gmInWhoList)) + if ((itr->second->GetSession()->GetSecurity() > gmLevelInWhoList)) continue; } + //do not process players which are not in world + if(!(itr->second->IsInWorld())) + continue; + // check if target is globally visible for player if (!(itr->second->IsVisibleGloballyFor(_player))) continue; @@ -1014,10 +1018,11 @@ void WorldSession::HandleRequestAccountData(WorldPacket& recv_data) uint32 size = adata->Data.size(); + uLongf destSize = compressBound(size); + ByteBuffer dest; - dest.resize(size); + dest.resize(destSize); - uLongf destSize = size; if(size && compress(const_cast<uint8*>(dest.contents()), &destSize, (uint8*)adata->Data.c_str(), size) != Z_OK) { sLog.outDebug("RAD: Failed to compress account data"); @@ -1040,40 +1045,41 @@ void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recv_data) CHECK_PACKET_SIZE(recv_data,1+2+1+1); sLog.outDebug( "WORLD: Received CMSG_SET_ACTION_BUTTON" ); - uint8 button, misc, type; - uint16 action; - recv_data >> button >> action >> misc >> type; - sLog.outDetail( "BUTTON: %u ACTION: %u TYPE: %u MISC: %u", button, action, type, misc ); - if(action==0) + uint8 button; + uint32 packetData; + recv_data >> button >> packetData; + + uint32 action = ACTION_BUTTON_ACTION(packetData); + uint8 type = ACTION_BUTTON_TYPE(packetData); + + sLog.outDetail( "BUTTON: %u ACTION: %u TYPE: %u", button, action, type ); + if (!packetData) { sLog.outDetail( "MISC: Remove action from button %u", button ); - GetPlayer()->removeActionButton(button); } else { - if(type==ACTION_BUTTON_MACRO || type==ACTION_BUTTON_CMACRO) - { - sLog.outDetail( "MISC: Added Macro %u into button %u", action, button ); - GetPlayer()->addActionButton(button,action,type,misc); - } - else if(type==ACTION_BUTTON_EQSET) - { - sLog.outDetail( "MISC: Added EquipmentSet %u into button %u", action, button ); - GetPlayer()->addActionButton(button,action,type,misc); - } - else if(type==ACTION_BUTTON_SPELL) + switch(type) { - sLog.outDetail( "MISC: Added Spell %u into button %u", action, button ); - GetPlayer()->addActionButton(button,action,type,misc); - } - else if(type==ACTION_BUTTON_ITEM) - { - sLog.outDetail( "MISC: Added Item %u into button %u", action, button ); - GetPlayer()->addActionButton(button,action,type,misc); + case ACTION_BUTTON_MACRO: + case ACTION_BUTTON_CMACRO: + sLog.outDetail( "MISC: Added Macro %u into button %u", action, button ); + break; + case ACTION_BUTTON_EQSET: + sLog.outDetail( "MISC: Added EquipmentSet %u into button %u", action, button ); + break; + case ACTION_BUTTON_SPELL: + sLog.outDetail( "MISC: Added Spell %u into button %u", action, button ); + break; + case ACTION_BUTTON_ITEM: + sLog.outDetail( "MISC: Added Item %u into button %u", action, button ); + break; + default: + sLog.outError( "MISC: Unknown action button type %u for action %u into button %u", type, action, button ); + return; } - else - sLog.outError( "MISC: Unknown action button type %u for action %u into button %u", type, action, button ); + GetPlayer()->addActionButton(button,action,type); } } @@ -1210,15 +1216,17 @@ void WorldSession::HandleWardenDataOpcode(WorldPacket& /*recv_data*/) */ } -void WorldSession::HandlePlayedTime(WorldPacket& /*recv_data*/) +void WorldSession::HandlePlayedTime(WorldPacket& recv_data) { - uint32 TotalTimePlayed = GetPlayer()->GetTotalPlayedTime(); - uint32 LevelPlayedTime = GetPlayer()->GetLevelPlayedTime(); + CHECK_PACKET_SIZE(recv_data, 1); - WorldPacket data(SMSG_PLAYED_TIME, 9); - data << TotalTimePlayed; - data << LevelPlayedTime; - data << uint8(0); + uint8 unk1; + recv_data >> unk1; // 0 or 1 expected + + WorldPacket data(SMSG_PLAYED_TIME, 4 + 4 + 1); + data << uint32(_player->GetTotalPlayedTime()); + data << uint32(_player->GetLevelPlayedTime()); + data << uint8(unk1); // 0 - will not show in chat frame SendPacket(&data); } @@ -1350,7 +1358,7 @@ void WorldSession::HandleWhoisOpcode(WorldPacket& recv_data) uint32 accid = plr->GetSession()->GetAccountId(); - QueryResult *result = LoginDatabase.PQuery("SELECT username,email,last_ip FROM account WHERE id=%u", accid); + QueryResult *result = loginDatabase.PQuery("SELECT username,email,last_ip FROM account WHERE id=%u", accid); if(!result) { SendNotification(LANG_ACCOUNT_FOR_PLAYER_NOT_FOUND, charname.c_str()); @@ -1488,7 +1496,7 @@ void WorldSession::HandleSetTitleOpcode( WorldPacket & recv_data ) recv_data >> title; // -1 at none - if(title > 0 && title < 192) + if(title > 0 && title < MAX_TITLE_INDEX) { if(!GetPlayer()->HasTitle(title)) return; diff --git a/src/game/MovementHandler.cpp b/src/game/MovementHandler.cpp index 7d7733763cd..f0ff9ec05d7 100644 --- a/src/game/MovementHandler.cpp +++ b/src/game/MovementHandler.cpp @@ -66,27 +66,30 @@ void WorldSession::HandleMoveWorldportAckOpcode() GetPlayer()->SetSemaphoreTeleportFar(false); + Map * oldMap = GetPlayer()->GetMap(); // relocate the player to the teleport destination - GetPlayer()->SetMapId(loc.mapid); - GetPlayer()->Relocate(loc.coord_x, loc.coord_y, loc.coord_z, loc.orientation); + Map * newMap = MapManager::Instance().CreateMap(loc.mapid, GetPlayer(), 0); + // the CanEnter checks are done in TeleporTo but conditions may change + // while the player is in transit, for example the map may get full + if (!newMap || !newMap->CanEnter(GetPlayer())) + { + sLog.outError("Map %d could not be created for player %d, porting player to homebind", loc.mapid, GetPlayer()->GetGUIDLow()); + GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation()); + return; + } + else + GetPlayer()->Relocate(loc.coord_x, loc.coord_y, loc.coord_z, loc.orientation); - // since the MapId is set before the GetInstance call, the InstanceId must be set to 0 - // to let GetInstance() determine the proper InstanceId based on the player's binds - GetPlayer()->SetInstanceId(0); + GetPlayer()->ResetMap(); + GetPlayer()->SetMap(newMap); GetPlayer()->SendInitialPacketsBeforeAddToMap(); - // the CanEnter checks are done in TeleporTo but conditions may change - // while the player is in transit, for example the map may get full if(!GetPlayer()->GetMap()->Add(GetPlayer())) { - sLog.outDebug("WORLD: teleport of player %s (%d) to location %d, %f, %f, %f, %f failed", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z, loc.orientation); - // teleport the player home - if(!GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation())) - { - // the player must always be able to teleport home - sLog.outError("WORLD: failed to teleport player %s (%d) to homebind location %d, %f, %f, %f, %f!", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation()); - assert(false); - } + sLog.outError("WORLD: failed to teleport player %s (%d) to map %d because of unknown reason!", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), loc.mapid); + GetPlayer()->ResetMap(); + GetPlayer()->SetMap(oldMap); + GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation()); return; } @@ -156,11 +159,14 @@ void WorldSession::HandleMoveWorldportAckOpcode() // resummon pet GetPlayer()->ResummonPetTemporaryUnSummonedIfAny(); + + //lets process all delayed operations on successful teleport + GetPlayer()->ProcessDelayedOperations(); } void WorldSession::HandleMoveTeleportAck(WorldPacket& recv_data) { - CHECK_PACKET_SIZE(recv_data, 8+4); + CHECK_PACKET_SIZE(recv_data, 8+4+4); sLog.outDebug("MSG_MOVE_TELEPORT_ACK"); uint64 guid; @@ -202,6 +208,9 @@ void WorldSession::HandleMoveTeleportAck(WorldPacket& recv_data) // resummon pet GetPlayer()->ResummonPetTemporaryUnSummonedIfAny(); + + //lets process all delayed operations on successful teleport + GetPlayer()->ProcessDelayedOperations(); } void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) @@ -223,7 +232,7 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) if(recv_data.size() != recv_data.rpos()) { - sLog.outError("MovementHandler: player %s (guid %d, account %u) sent a packet (opcode %u) that is %u bytes larger than it should be. Kicked as cheater.", _player->GetName(), _player->GetGUIDLow(), _player->GetSession()->GetAccountId(), recv_data.GetOpcode(), recv_data.size() - recv_data.rpos()); + sLog.outError("MovementHandler: player %s (guid %d, account %u) sent a packet (opcode %u) that is " SIZEFMTD " bytes larger than it should be. Kicked as cheater.", _player->GetName(), _player->GetGUIDLow(), _player->GetSession()->GetAccountId(), recv_data.GetOpcode(), recv_data.size() - recv_data.rpos()); KickPlayer(); return; } @@ -294,6 +303,12 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) mover->m_movementInfo = movementInfo; + if(mover->m_Vehicle) + { + mover->SetOrientation(movementInfo.o); + return; + } + if(plMover) // nothing is charmed, or player charmed { plMover->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); @@ -332,9 +347,6 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) } else // creature charmed { - uint32 entry = mover->GetEntry(); - if(mover->m_Vehicle) - return; mover->GetMap()->CreatureRelocation((Creature*)mover, movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); /*if(mover->canFly()) diff --git a/src/game/NPCHandler.cpp b/src/game/NPCHandler.cpp index c60d368659a..bb9823ee71f 100644 --- a/src/game/NPCHandler.cpp +++ b/src/game/NPCHandler.cpp @@ -422,7 +422,7 @@ void WorldSession::HandleBinderActivateOpcode( WorldPacket & recv_data ) uint64 npcGUID; recv_data >> npcGUID; - if(!GetPlayer()->isAlive()) + if(!GetPlayer()->IsInWorld() || !GetPlayer()->isAlive()) return; Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID,UNIT_NPC_FLAG_INNKEEPER); @@ -442,7 +442,7 @@ void WorldSession::HandleBinderActivateOpcode( WorldPacket & recv_data ) void WorldSession::SendBindPoint(Creature *npc) { // prevent set homebind to instances in any case - if(sMapStore.LookupEntry(GetPlayer()->GetMapId())->Instanceable()) + if(GetPlayer()->GetMap()->Instanceable()) return; uint32 bindspell = 3286; diff --git a/src/game/Object.cpp b/src/game/Object.cpp index 3561621f0bd..69a286289b3 100644 --- a/src/game/Object.cpp +++ b/src/game/Object.cpp @@ -259,13 +259,13 @@ void Object::BuildOutOfRangeUpdateBlock(UpdateData * data) const data->AddOutOfRangeGUID(GetGUID()); } -void Object::DestroyForPlayer(Player *target) const +void Object::DestroyForPlayer( Player *target, bool anim ) const { ASSERT(target); WorldPacket data(SMSG_DESTROY_OBJECT, 8); data << uint64(GetGUID()); - data << uint8(0); // WotLK (bool) + data << uint8(anim ? 1 : 0); // WotLK (bool), may be despawn animation target->GetSession()->SendPacket( &data ); } @@ -477,6 +477,13 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask if (((GameObject*)this)->GetGoArtKit()) updateMask->SetBit(GAMEOBJECT_BYTES_1); } + else if (isType(TYPEMASK_UNIT)) + { + if( ((Unit*)this)->HasFlag(UNIT_FIELD_AURASTATE, PER_CASTER_AURA_STATE_MASK)) + { + updateMask->SetBit(UNIT_FIELD_AURASTATE); + } + } } else // case UPDATETYPE_VALUES { @@ -489,6 +496,13 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask updateMask->SetBit(GAMEOBJECT_DYNAMIC); updateMask->SetBit(GAMEOBJECT_BYTES_1); } + else if (isType(TYPEMASK_UNIT)) + { + if( ((Unit*)this)->HasFlag(UNIT_FIELD_AURASTATE, PER_CASTER_AURA_STATE_MASK)) + { + updateMask->SetBit(UNIT_FIELD_AURASTATE); + } + } } WPAssert(updateMask && updateMask->GetCount() == m_valuesCount); @@ -513,6 +527,11 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask *data << uint32(appendValue); } + else if (index == UNIT_FIELD_AURASTATE) + { + // Check per caster aura states to not enable using a pell in client if specified aura is not by target + *data << ((Unit*)this)->BuildAuraStateUpdateForTarget(target); + } // FIXME: Some values at server stored in float format but must be sent to client in uint32 format else if(index >= UNIT_FIELD_BASEATTACKTIME && index <= UNIT_FIELD_RANGEDATTACKTIME) { @@ -730,7 +749,7 @@ void Object::_SetCreateBits(UpdateMask *updateMask, Player* /*target*/) const void Object::SetInt32Value( uint16 index, int32 value ) { - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); + ASSERT( index < m_valuesCount || PrintIndexError( index, true ) ); if(m_int32Values[ index ] != value) { @@ -749,7 +768,7 @@ void Object::SetInt32Value( uint16 index, int32 value ) void Object::SetUInt32Value( uint16 index, uint32 value ) { - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); + ASSERT( index < m_valuesCount || PrintIndexError( index, true ) ); if(m_uint32Values[ index ] != value) { @@ -768,7 +787,7 @@ void Object::SetUInt32Value( uint16 index, uint32 value ) void Object::SetUInt64Value( uint16 index, const uint64 &value ) { - ASSERT( index + 1 < m_valuesCount || PrintIndexError( index , true ) ); + ASSERT( index + 1 < m_valuesCount || PrintIndexError( index, true ) ); if(*((uint64*)&(m_uint32Values[ index ])) != value) { m_uint32Values[ index ] = *((uint32*)&value); @@ -829,7 +848,7 @@ bool Object::RemoveUInt64Value(uint16 index, const uint64 &value) void Object::SetFloatValue( uint16 index, float value ) { - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); + ASSERT( index < m_valuesCount || PrintIndexError( index, true ) ); if(m_floatValues[ index ] != value) { @@ -848,7 +867,7 @@ void Object::SetFloatValue( uint16 index, float value ) void Object::SetByteValue( uint16 index, uint8 offset, uint8 value ) { - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); + ASSERT( index < m_valuesCount || PrintIndexError( index, true ) ); if(offset > 4) { @@ -874,7 +893,7 @@ void Object::SetByteValue( uint16 index, uint8 offset, uint8 value ) void Object::SetUInt16Value( uint16 index, uint8 offset, uint16 value ) { - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); + ASSERT( index < m_valuesCount || PrintIndexError( index, true ) ); if(offset > 2) { @@ -948,7 +967,7 @@ void Object::ApplyModPositiveFloatValue(uint16 index, float val, bool apply) void Object::SetFlag( uint16 index, uint32 newFlag ) { - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); + ASSERT( index < m_valuesCount || PrintIndexError( index, true ) ); uint32 oldval = m_uint32Values[ index ]; uint32 newval = oldval | newFlag; @@ -969,7 +988,7 @@ void Object::SetFlag( uint16 index, uint32 newFlag ) void Object::RemoveFlag( uint16 index, uint32 oldFlag ) { - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); + ASSERT( index < m_valuesCount || PrintIndexError( index, true ) ); uint32 oldval = m_uint32Values[ index ]; uint32 newval = oldval & ~oldFlag; @@ -990,7 +1009,7 @@ void Object::RemoveFlag( uint16 index, uint32 oldFlag ) void Object::SetByteFlag( uint16 index, uint8 offset, uint8 newFlag ) { - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); + ASSERT( index < m_valuesCount || PrintIndexError( index, true ) ); if(offset > 4) { @@ -1015,7 +1034,7 @@ void Object::SetByteFlag( uint16 index, uint8 offset, uint8 newFlag ) void Object::RemoveByteFlag( uint16 index, uint8 offset, uint8 oldFlag ) { - ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); + ASSERT( index < m_valuesCount || PrintIndexError( index, true ) ); if(offset > 4) { @@ -1047,9 +1066,9 @@ bool Object::PrintIndexError(uint32 index, bool set) const } WorldObject::WorldObject() - : m_mapId(0), m_InstanceId(0), m_phaseMask(PHASEMASK_NORMAL), - m_positionX(0.0f), m_positionY(0.0f), m_positionZ(0.0f), m_orientation(0.0f) - , m_map(NULL), m_zoneScript(NULL) + : m_phaseMask(PHASEMASK_NORMAL), + m_positionX(0.0f), m_positionY(0.0f), m_positionZ(0.0f), m_orientation(0.0f), m_currMap(NULL) + , m_zoneScript(NULL) , m_isActive(false), IsTempWorldObject(false) , m_name("") { @@ -1096,11 +1115,13 @@ void WorldObject::setActive( bool on ) } } -void WorldObject::_Create( uint32 guidlow, HighGuid guidhigh, uint32 mapid, uint32 phaseMask ) +void WorldObject::CleanupsBeforeDelete() { - Object::_Create(guidlow, 0, guidhigh); +} - m_mapId = mapid; +void WorldObject::_Create( uint32 guidlow, HighGuid guidhigh, uint32 phaseMask ) +{ + Object::_Create(guidlow, 0, guidhigh); m_phaseMask = phaseMask; } @@ -1430,7 +1451,7 @@ bool WorldObject::IsInBetween(const WorldObject *obj1, const WorldObject *obj2, void WorldObject::GetRandomPoint( float x, float y, float z, float distance, float &rand_x, float &rand_y, float &rand_z) const { - if(distance==0) + if(distance == 0) { rand_x = x; rand_y = y; @@ -1621,8 +1642,6 @@ void WorldObject::MonsterWhisper(int32 textId, uint64 receiver, bool IsBossWhisp void WorldObject::BuildMonsterChat(WorldPacket *data, uint8 msgtype, char const* text, uint32 language, char const* name, uint64 targetGuid) const { - bool pre = (msgtype==CHAT_MSG_MONSTER_EMOTE || msgtype==CHAT_MSG_RAID_BOSS_EMOTE); - *data << (uint8)msgtype; *data << (uint32)language; *data << (uint64)GetGUID(); @@ -1635,9 +1654,7 @@ void WorldObject::BuildMonsterChat(WorldPacket *data, uint8 msgtype, char const* *data << (uint32)1; // target name length *data << (uint8)0; // target name } - *data << (uint32)(strlen(text)+1+(pre?3:0)); - if(pre) - data->append("%s ",3); + *data << (uint32)(strlen(text)+1); *data << text; *data << (uint8)0; // ChatTag } @@ -1668,23 +1685,20 @@ void WorldObject::SendMessageToSetInRange(WorldPacket *data, float dist, bool /* void WorldObject::SendObjectDeSpawnAnim(uint64 guid) { WorldPacket data(SMSG_GAMEOBJECT_DESPAWN_ANIM, 8); - data << guid; + data << uint64(guid); SendMessageToSet(&data, true); } -Map* WorldObject::_getMap() -{ - return m_map = MapManager::Instance().GetMap(GetMapId(), this); -} - -Map* WorldObject::_findMap() +void WorldObject::SetMap(Map * map) { - return m_map = MapManager::Instance().FindMap(GetMapId(), GetInstanceId()); + ASSERT(map); + m_currMap = map; } Map const* WorldObject::GetBaseMap() const { - return MapManager::Instance().CreateBaseMap(GetMapId()); + ASSERT(m_currMap); + return m_currMap->GetParent(); } void WorldObject::AddObjectToRemoveList() @@ -1720,6 +1734,8 @@ TempSummon *Map::SummonCreature(uint32 entry, float x, float y, float z, float a mask = SUMMON_MASK_PUPPET; else if(properties->Type == SUMMON_TYPE_MINIPET) mask = SUMMON_MASK_MINION; + else if (properties->Flags & 512) // Mirror Image, Summon Gargoyle + mask = SUMMON_MASK_GUARDIAN; } uint32 phase = PHASEMASK_NORMAL, team = 0; @@ -1992,6 +2008,36 @@ GameObject* WorldObject::FindNearestGameObject(uint32 entry, float range) return go; } +void WorldObject::GetGameObjectListWithEntryInGrid(std::list<GameObject*>& lList, uint32 uiEntry, float fMaxSearchRange) +{ + CellPair pair(Trinity::ComputeCellPair(this->GetPositionX(), this->GetPositionY())); + Cell cell(pair); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + Trinity::AllGameObjectsWithEntryInRange check(this, uiEntry, fMaxSearchRange); + Trinity::GameObjectListSearcher<Trinity::AllGameObjectsWithEntryInRange> searcher(this, lList, check); + TypeContainerVisitor<Trinity::GameObjectListSearcher<Trinity::AllGameObjectsWithEntryInRange>, GridTypeMapContainer> visitor(searcher); + + CellLock<GridReadGuard> cell_lock(cell, pair); + cell_lock->Visit(cell_lock, visitor, *(this->GetMap())); +} + +void WorldObject::GetCreatureListWithEntryInGrid(std::list<Creature*>& lList, uint32 uiEntry, float fMaxSearchRange) +{ + CellPair pair(Trinity::ComputeCellPair(this->GetPositionX(), this->GetPositionY())); + Cell cell(pair); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + Trinity::AllCreaturesOfEntryInRange check(this, uiEntry, fMaxSearchRange); + Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange> searcher(this, lList, check); + TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange>, GridTypeMapContainer> visitor(searcher); + + CellLock<GridReadGuard> cell_lock(cell, pair); + cell_lock->Visit(cell_lock, visitor, *(this->GetMap())); +} + /* namespace MaNGOS { diff --git a/src/game/Object.h b/src/game/Object.h index 8e86ff1c87b..c44dd4fd65c 100644 --- a/src/game/Object.h +++ b/src/game/Object.h @@ -160,7 +160,7 @@ class TRINITY_DLL_SPEC Object uint32 GetEntry() const { return GetUInt32Value(OBJECT_FIELD_ENTRY); } void SetEntry(uint32 entry) { SetUInt32Value(OBJECT_FIELD_ENTRY, entry); } - uint8 GetTypeId() const { return m_objectTypeId; } + TypeID GetTypeId() const { return m_objectTypeId; } bool isType(uint16 mask) const { return (mask & m_objectType); } virtual void BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) const; @@ -171,7 +171,7 @@ class TRINITY_DLL_SPEC Object void BuildMovementUpdateBlock( UpdateData * data, uint32 flags = 0 ) const; void BuildUpdate(UpdateDataMapType &); - virtual void DestroyForPlayer( Player *target ) const; + virtual void DestroyForPlayer( Player *target, bool anim = false ) const; const int32& GetInt32Value( uint16 index ) const { @@ -337,7 +337,7 @@ class TRINITY_DLL_SPEC Object uint16 m_objectType; - uint8 m_objectTypeId; + TypeID m_objectTypeId; uint16 m_updateFlag; union @@ -371,7 +371,7 @@ class TRINITY_DLL_SPEC WorldObject : public Object virtual void Update ( uint32 /*time_diff*/ ) { } - void _Create( uint32 guidlow, HighGuid guidhigh, uint32 mapid, uint32 phaseMask); + void _Create( uint32 guidlow, HighGuid guidhigh, uint32 phaseMask); void Relocate(WorldObject *obj) { @@ -407,7 +407,7 @@ class TRINITY_DLL_SPEC WorldObject : public Object void GetPosition( float &x, float &y, float &z ) const { x = m_positionX; y = m_positionY; z = m_positionZ; } void GetPosition( WorldLocation &loc ) const - { loc.mapid = m_mapId; GetPosition(loc.coord_x, loc.coord_y, loc.coord_z); loc.orientation = GetOrientation(); } + { loc.mapid = GetMapId(); GetPosition(loc.coord_x, loc.coord_y, loc.coord_z); loc.orientation = GetOrientation(); } void GetPosition(Position pos) const { pos[0] = m_positionX; pos[1] = m_positionY; pos[2] = m_positionZ; pos[3] = m_orientation; } float GetOrientation( ) const { return m_orientation; } @@ -439,10 +439,8 @@ class TRINITY_DLL_SPEC WorldObject : public Object void GetRandomPoint( float x, float y, float z, float distance, float &rand_x, float &rand_y, float &rand_z ) const; - void SetMapId(uint32 newMap) { m_mapId = newMap; m_map = NULL; } - uint32 GetMapId() const { return m_mapId; } - void SetInstanceId(uint32 val) { m_InstanceId = val; m_map = NULL; } - uint32 GetInstanceId() const { return m_InstanceId; } + virtual uint32 GetMapId() const { return m_currMap ? m_currMap->GetId() : 0; } + virtual uint32 GetInstanceId() const { return m_currMap ? m_currMap->GetInstanceId() : 0; } virtual void SetPhaseMask(uint32 newPhaseMask, bool update); uint32 GetPhaseMask() const { return m_phaseMask; } @@ -470,8 +468,7 @@ class TRINITY_DLL_SPEC WorldObject : public Object float GetDistanceZ(const WorldObject* obj) const; bool IsInMap(const WorldObject* obj) const { - return IsInWorld() && obj->IsInWorld() && GetMapId()==obj->GetMapId() && - GetInstanceId()==obj->GetInstanceId() && InSamePhase(obj); + return IsInWorld() && obj->IsInWorld() && (GetMap() == obj->GetMap()) && InSamePhase(obj); } bool IsWithinDist3d(float x, float y, float z, float dist2compare) const; bool IsWithinDist2d(float x, float y, float dist2compare) const; @@ -498,6 +495,8 @@ class TRINITY_DLL_SPEC WorldObject : public Object bool HasInArc( const float arcangle, const WorldObject* obj ) const; bool IsInBetween(const WorldObject *obj1, const WorldObject *obj2, float size = 0) const; + virtual void CleanupsBeforeDelete(); // used in destructor or explicitly before mass creature delete to remove cross-references to already deleted units + virtual void SendMessageToSet(WorldPacket *data, bool self); virtual void SendMessageToSetInRange(WorldPacket *data, float dist, bool self); @@ -518,7 +517,6 @@ class TRINITY_DLL_SPEC WorldObject : public Object void SendObjectDeSpawnAnim(uint64 guid); virtual void SaveRespawnTime() {} - void AddObjectToRemoveList(); // main visibility check function in normal case (ignore grey zone distance check) @@ -530,8 +528,13 @@ class TRINITY_DLL_SPEC WorldObject : public Object // Low Level Packets void SendPlaySound(uint32 Sound, bool OnlySelf); - Map * GetMap() const { return m_map ? m_map : const_cast<WorldObject*>(this)->_getMap(); } - Map * FindMap() const { return m_map ? m_map : const_cast<WorldObject*>(this)->_findMap(); } + virtual void SetMap(Map * map); + Map * GetMap() const { ASSERT(m_currMap); return m_currMap; } + Map * FindMap() const { return m_currMap; } + //used to check all object's GetMap() calls when object is not in world! + virtual void ResetMap() { assert(m_currMap); m_currMap = NULL; } + + //this function should be removed in nearest time... Map const* GetBaseMap() const; void SetZoneScript(); @@ -545,6 +548,9 @@ class TRINITY_DLL_SPEC WorldObject : public Object Creature* FindNearestCreature(uint32 entry, float range, bool alive = true); GameObject* FindNearestGameObject(uint32 entry, float range); + void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& lList, uint32 uiEntry, float fMaxSearchRange); + void GetCreatureListWithEntryInGrid(std::list<Creature*>& lList, uint32 uiEntry, float fMaxSearchRange); + bool isActiveObject() const { return m_isActive; } void setActive(bool isActiveObject); void SetWorldObject(bool apply); @@ -555,10 +561,10 @@ class TRINITY_DLL_SPEC WorldObject : public Object #ifdef MAP_BASED_RAND_GEN int32 irand(int32 min, int32 max) const { return int32 (GetMap()->mtRand.randInt(max - min)) + min; } - uint32 urand(uint32 min, uint32 max) const { return GetMap()->mtRand.randInt(max - min) + min; } - int32 rand32() const { return GetMap()->mtRand.randInt(); } - double rand_norm() const { return GetMap()->mtRand.randExc(); } - double rand_chance() const { return GetMap()->mtRand.randExc(100.0); } + uint32 urand(uint32 min, uint32 max) const { return GetMap()->mtRand.randInt(max - min) + min;} + int32 rand32() const { return GetMap()->mtRand.randInt();} + double rand_norm() const { return GetMap()->mtRand.randExc();} + double rand_chance() const { return GetMap()->mtRand.randExc(100.0);} #endif protected: @@ -568,13 +574,8 @@ class TRINITY_DLL_SPEC WorldObject : public Object ZoneScript *m_zoneScript; private: - uint32 m_mapId; // object at map with map_id - uint32 m_InstanceId; // in map copy with instance id + Map * m_currMap; //current object's Map location uint32 m_phaseMask; // in area phase state - Map *m_map; - - Map* _getMap(); - Map* _findMap(); float m_positionX; float m_positionY; diff --git a/src/game/ObjectAccessor.cpp b/src/game/ObjectAccessor.cpp index b420e4af4b2..43d3d0a1534 100644 --- a/src/game/ObjectAccessor.cpp +++ b/src/game/ObjectAccessor.cpp @@ -46,7 +46,11 @@ INSTANTIATE_SINGLETON_2(ObjectAccessor, CLASS_LOCK); INSTANTIATE_CLASS_MUTEX(ObjectAccessor, ACE_Thread_Mutex); ObjectAccessor::ObjectAccessor() {} -ObjectAccessor::~ObjectAccessor() {} +ObjectAccessor::~ObjectAccessor() +{ + for(Player2CorpsesMapType::const_iterator itr = i_player2corpse.begin(); itr != i_player2corpse.end(); ++itr) + delete itr->second; +} Creature* ObjectAccessor::GetCreatureOrPetOrVehicle(WorldObject const &u, uint64 guid) @@ -60,7 +64,7 @@ ObjectAccessor::GetCreatureOrPetOrVehicle(WorldObject const &u, uint64 guid) if(IS_VEHICLE_GUID(guid)) return GetVehicle(guid); - return u.GetMap()->GetCreature(guid); + return u.IsInWorld() ? u.GetMap()->GetCreature(guid) : NULL; } /* @@ -135,7 +139,11 @@ Object* ObjectAccessor::GetObjectByTypeMask(WorldObject const &p, uint64 guid, u Player* ObjectAccessor::FindPlayer(uint64 guid) { - return GetObjectInWorld(guid, (Player*)NULL); + Player * plr = GetObjectInWorld(guid, (Player*)NULL); + if(!plr || !plr->IsInWorld()) + return NULL; + + return plr; } Player* @@ -145,7 +153,7 @@ ObjectAccessor::FindPlayerByName(const char *name) HashMapHolder<Player>::MapType& m = HashMapHolder<Player>::GetContainer(); HashMapHolder<Player>::MapType::iterator iter = m.begin(); for(; iter != m.end(); ++iter) - if( ::strcmp(name, iter->second->GetName()) == 0 ) + if(iter->second->IsInWorld() && ( ::strcmp(name, iter->second->GetName()) == 0 )) return iter->second; return NULL; } @@ -346,8 +354,6 @@ ObjectAccessor::ConvertCorpseForPlayer(uint64 player_guid, bool insignia) // bones->m_inWorld = m_inWorld; // don't overwrite world state // bones->m_type = m_type; // don't overwrite type bones->Relocate(corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetOrientation()); - bones->SetMapId(corpse->GetMapId()); - bones->SetInstanceId(corpse->GetInstanceId()); bones->SetPhaseMask(corpse->GetPhaseMask(), false); bones->SetUInt32Value(CORPSE_FIELD_FLAGS, CORPSE_FLAG_UNK2 | CORPSE_FLAG_BONES); diff --git a/src/game/ObjectAccessor.h b/src/game/ObjectAccessor.h index 80aac0bcf92..1941ccea30c 100644 --- a/src/game/ObjectAccessor.h +++ b/src/game/ObjectAccessor.h @@ -103,8 +103,14 @@ class MANGOS_DLL_DECL ObjectAccessor : public MaNGOS::Singleton<ObjectAccessor, if(!guid) return NULL; - if(IS_PLAYER_GUID(guid)) - return (Unit*)HashMapHolder<Player>::Find(guid); + if (IS_PLAYER_GUID(guid)) + { + Unit * u = (Unit*)HashMapHolder<Player>::Find(guid); + if(!u || !u->IsInWorld()) + return NULL; + + return u; + } if(IS_CREATURE_GUID(guid)) return (Unit*)HashMapHolder<Creature>::Find(guid); @@ -115,6 +121,24 @@ class MANGOS_DLL_DECL ObjectAccessor : public MaNGOS::Singleton<ObjectAccessor, return (Unit*)HashMapHolder<Vehicle>::Find(guid); } + static Unit* GetUnitInOrOutOfWorld(uint64 guid, Unit* /*fake*/) + { + if(!guid) + return NULL; + + if (IS_PLAYER_GUID(guid)) + { + Unit * u = (Unit*)HashMapHolder<Player>::Find(guid); + if(!u) + return NULL; + + return u; + } + // Other object types than player are unloaded while out of world + return GetObjectInWorld(guid, ((Unit*)NULL)); + } + + template<class T> static T* GetObjectInWorld(uint32 mapid, float x, float y, uint64 guid, T* /*fake*/) { T* obj = HashMapHolder<T>::Find(guid); @@ -144,6 +168,7 @@ class MANGOS_DLL_DECL ObjectAccessor : public MaNGOS::Singleton<ObjectAccessor, static Object* GetObjectByTypeMask(WorldObject const &, uint64, uint32 typemask); static Creature* GetCreatureOrPetOrVehicle(WorldObject const &, uint64); static Unit* GetUnit(WorldObject const &, uint64 guid) { return GetObjectInWorld(guid, (Unit*)NULL); } + static Unit* GetUnitInOrOutOfWorld(WorldObject const &, uint64 guid) { return GetUnitInOrOutOfWorld(guid, (Unit*)NULL); } static Pet* GetPet(Unit const &, uint64 guid) { return GetPet(guid); } static Player* GetPlayer(Unit const &, uint64 guid) { return FindPlayer(guid); } static Corpse* GetCorpse(WorldObject const &u, uint64 guid); @@ -204,7 +229,7 @@ class MANGOS_DLL_DECL ObjectAccessor : public MaNGOS::Singleton<ObjectAccessor, Corpse* GetCorpseForPlayerGUID(uint64 guid); void RemoveCorpse(Corpse *corpse); - void AddCorpse(Corpse* corpse); + void AddCorpse(Corpse *corpse); void AddCorpsesToGrid(GridPair const& gridpair,GridType& grid,Map* map); Corpse* ConvertCorpseForPlayer(uint64 player_guid, bool insignia = false); diff --git a/src/game/ObjectGridLoader.cpp b/src/game/ObjectGridLoader.cpp index 368edce0a53..d08e3010a78 100644 --- a/src/game/ObjectGridLoader.cpp +++ b/src/game/ObjectGridLoader.cpp @@ -126,6 +126,7 @@ void LoadHelper(CellGuidSet const& guid_set, CellPair &cell, GridRefManager<T> & obj->GetGridRef().link(&m, obj); addUnitState(obj,cell); + obj->SetMap(map); obj->AddToWorld(); if(obj->isActiveObject()) map->AddToActive(obj); @@ -184,6 +185,7 @@ void LoadHelper(CellCorpseSet const& cell_corpses, CellPair &cell, CorpseMapType obj->GetGridRef().link(&m, obj); addUnitState(obj,cell); + obj->SetMap(map); obj->AddToWorld(); if(obj->isActiveObject()) map->AddToActive(obj); diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index cbf445ebc6b..ec300715f86 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -178,6 +178,9 @@ ObjectMgr::~ObjectMgr() for (CachePlayerInfoMap::iterator itr = m_mPlayerInfoMap.begin(); itr != m_mPlayerInfoMap.end(); ++itr) delete itr->second; + for (ArenaTeamMap::iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr) + delete itr->second; + for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr) itr->second.Clear(); @@ -635,7 +638,7 @@ void ObjectMgr::LoadCreatureTemplates() // used later for scale CreatureDisplayInfoEntry const* displayScaleEntry = NULL; - + if (cInfo->DisplayID_A[0]) { CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_A[0]); @@ -703,6 +706,18 @@ void ObjectMgr::LoadCreatureTemplates() if (!displayScaleEntry) sLog.outErrorDb("Creature (Entry: %u) not has any existed display id in DisplayID_A/DisplayID_A2/DisplayID_H/DisplayID_H2", cInfo->Entry); + for(int k = 0; k < MAX_KILL_CREDIT; ++k) + { + if(cInfo->KillCredit[k]) + { + if(!GetCreatureTemplate(cInfo->KillCredit[k])) + { + sLog.outErrorDb("Creature (Entry: %u) has not existed creature entry in `KillCredit%d` (%u)",cInfo->Entry,k+1,cInfo->KillCredit[k]); + const_cast<CreatureInfo*>(cInfo)->KillCredit[k] = 0; + } + } + } + if (cInfo->unit_class && ((1 << (cInfo->unit_class-1)) & CLASSMASK_ALL_CREATURES) == 0) sLog.outErrorDb("Creature (Entry: %u) has invalid unit_class(%u) for creature_template", cInfo->Entry, cInfo->unit_class); @@ -786,7 +801,7 @@ void ObjectMgr::LoadCreatureTemplates() const_cast<CreatureInfo*>(cInfo)->scale = 1.0f; } - //const_cast<CreatureInfo*>(cInfo)->dmg_multiplier *= Creature::_GetDamageMod(cInfo->rank); + const_cast<CreatureInfo*>(cInfo)->dmg_multiplier *= Creature::_GetDamageMod(cInfo->rank); } } @@ -865,17 +880,17 @@ void ObjectMgr::ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* endAura.effect_idx = 0; } -void ObjectMgr::LoadCreatureAddons() +void ObjectMgr::LoadCreatureAddons(SQLStorage& creatureaddons, char const* entryName, char const* comment) { - sCreatureInfoAddonStorage.Load(); + creatureaddons.Load(); - sLog.outString( ">> Loaded %u creature template addons", sCreatureInfoAddonStorage.RecordCount ); + sLog.outString(">> Loaded %u %s", creatureaddons.RecordCount, comment); sLog.outString(); // check data correctness and convert 'auras' - for(uint32 i = 1; i < sCreatureInfoAddonStorage.MaxEntry; ++i) + for(uint32 i = 1; i < creatureaddons.MaxEntry; ++i) { - CreatureDataAddon const* addon = sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(i); + CreatureDataAddon const* addon = creatureaddons.LookupEntry<CreatureDataAddon>(i); if(!addon) continue; @@ -883,49 +898,41 @@ void ObjectMgr::LoadCreatureAddons() { if (!sCreatureDisplayInfoStore.LookupEntry(addon->mount)) { - sLog.outErrorDb("Creature (Entry %u) have invalid displayInfoId for mount (%u) defined in `creature_template_addon`.",addon->guidOrEntry, addon->mount); + sLog.outErrorDb("Creature (%s %u) have invalid displayInfoId for mount (%u) defined in `%s`.", entryName, addon->guidOrEntry, addon->mount, creatureaddons.GetTableName()); const_cast<CreatureDataAddon*>(addon)->mount = 0; } } if (!sEmotesStore.LookupEntry(addon->emote)) - sLog.outErrorDb("Creature (Entry %u) have invalid emote (%u) defined in `creature_template_addon`.",addon->guidOrEntry, addon->emote); + sLog.outErrorDb("Creature (%s %u) have invalid emote (%u) defined in `%s`.", entryName, addon->guidOrEntry, addon->emote, creatureaddons.GetTableName()); - ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), "creature_template_addon", "Entry"); + /*if (addon->move_flags & (MONSTER_MOVE_UNK1|MONSTER_MOVE_UNK4)) + { + sLog.outErrorDb("Creature (%s %u) movement flags mask defined in `%s` include forbidden flags (" I32FMT ") that can crash client, cleanup at load.", entryName, addon->guidOrEntry, creatureaddons.GetTableName(), (MONSTER_MOVE_UNK1|MONSTER_MOVE_UNK4)); + const_cast<CreatureDataAddon*>(addon)->move_flags &= ~(MONSTER_MOVE_UNK1|MONSTER_MOVE_UNK4); + }*/ - if(!sCreatureStorage.LookupEntry<CreatureInfo>(addon->guidOrEntry)) - sLog.outErrorDb("Creature (Entry: %u) does not exist but has a record in `creature_template_addon`",addon->guidOrEntry); + ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), creatureaddons.GetTableName(), entryName); } +} - sCreatureDataAddonStorage.Load(); - - sLog.outString( ">> Loaded %u creature addons", sCreatureDataAddonStorage.RecordCount ); - sLog.outString(); - - // check data correctness and convert 'auras' - for(uint32 i = 1; i < sCreatureDataAddonStorage.MaxEntry; ++i) - { - CreatureDataAddon const* addon = sCreatureDataAddonStorage.LookupEntry<CreatureDataAddon>(i); - if(!addon) - continue; - - if (addon->mount) - { - if (!sCreatureDisplayInfoStore.LookupEntry(addon->mount)) - { - sLog.outErrorDb("Creature (GUID %u) have invalid displayInfoId for mount (%u) defined in `creature_addon`.",addon->guidOrEntry, addon->mount); - const_cast<CreatureDataAddon*>(addon)->mount = 0; - } - } +void ObjectMgr::LoadCreatureAddons() +{ + LoadCreatureAddons(sCreatureInfoAddonStorage,"Entry","creature template addons"); - if (!sEmotesStore.LookupEntry(addon->emote)) - sLog.outErrorDb("Creature (GUID %u) have invalid emote (%u) defined in `creature_addon`.",addon->guidOrEntry, addon->emote); + // check entry ids + for(uint32 i = 1; i < sCreatureInfoAddonStorage.MaxEntry; ++i) + if(CreatureDataAddon const* addon = sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(i)) + if(!sCreatureStorage.LookupEntry<CreatureInfo>(addon->guidOrEntry)) + sLog.outErrorDb("Creature (Entry: %u) does not exist but has a record in `%s`",addon->guidOrEntry, sCreatureInfoAddonStorage.GetTableName()); - ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), "creature_addon", "GUIDLow"); + LoadCreatureAddons(sCreatureDataAddonStorage,"GUID","creature addons"); - if(mCreatureDataMap.find(addon->guidOrEntry)==mCreatureDataMap.end()) - sLog.outErrorDb("Creature (GUID: %u) does not exist but has a record in `creature_addon`",addon->guidOrEntry); - } + // check entry ids + for(uint32 i = 1; i < sCreatureDataAddonStorage.MaxEntry; ++i) + if(CreatureDataAddon const* addon = sCreatureDataAddonStorage.LookupEntry<CreatureDataAddon>(i)) + if(mCreatureDataMap.find(addon->guidOrEntry)==mCreatureDataMap.end()) + sLog.outErrorDb("Creature (GUID: %u) does not exist but has a record in `creature_addon`",addon->guidOrEntry); } EquipmentInfo const* ObjectMgr::GetEquipmentInfo(uint32 entry) @@ -1533,6 +1540,12 @@ void ObjectMgr::LoadGameobjects() data.rotation2 = fields[ 9].GetFloat(); data.rotation3 = fields[10].GetFloat(); data.spawntimesecs = fields[11].GetInt32(); + + if (data.spawntimesecs==0 && gInfo->IsDespawnAtAction()) + { + sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with `spawntimesecs` (0) value, but gameobejct marked as despawnable at action.",guid,data.id); + } + data.animprogress = fields[12].GetUInt32(); data.ArtKit = 0; @@ -2035,6 +2048,12 @@ void ObjectMgr::LoadItemPrototypes() const_cast<ItemPrototype*>(proto)->Stackable = 1000; } + if(proto->ContainerSlots > MAX_BAG_SIZE) + { + sLog.outErrorDb("Item (Entry: %u) has too large value in ContainerSlots (%u), replace by hardcoded limit (%u).",i,proto->ContainerSlots,MAX_BAG_SIZE); + const_cast<ItemPrototype*>(proto)->ContainerSlots = MAX_BAG_SIZE; + } + if(proto->StatsCount > MAX_ITEM_PROTO_STATS) { sLog.outErrorDb("Item (Entry: %u) has too large value in statscount (%u), replace by hardcoded limit (%u).",i,proto->StatsCount,MAX_ITEM_PROTO_STATS); @@ -2706,8 +2725,8 @@ void ObjectMgr::LoadPlayerInfo() // Load playercreate actions { - // 0 1 2 3 4 5 - QueryResult *result = WorldDatabase.Query("SELECT race, class, button, action, type, misc FROM playercreateinfo_action"); + // 0 1 2 3 4 + QueryResult *result = WorldDatabase.Query("SELECT race, class, button, action, type FROM playercreateinfo_action"); uint32 count = 0; @@ -2742,10 +2761,7 @@ void ObjectMgr::LoadPlayerInfo() } PlayerInfo* pInfo = &playerInfo[current_race][current_class]; - pInfo->action[0].push_back(fields[2].GetUInt16()); - pInfo->action[1].push_back(fields[3].GetUInt16()); - pInfo->action[2].push_back(fields[4].GetUInt16()); - pInfo->action[3].push_back(fields[5].GetUInt16()); + pInfo->action.push_back(PlayerCreateInfoAction(fields[2].GetUInt8(),fields[3].GetUInt32(),fields[4].GetUInt8())); bar.step(); ++count; @@ -3901,13 +3917,20 @@ void ObjectMgr::LoadQuests() qinfo->RewSpell = 0; // no spell reward will display for this quest } - else if(!SpellMgr::IsSpellValid(spellInfo)) + if(!SpellMgr::IsSpellValid(spellInfo)) { - sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is broken, quest can't be done.", + sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is broken, quest will not have a spell reward.", qinfo->GetQuestId(),qinfo->RewSpell,qinfo->RewSpell); qinfo->RewSpell = 0; // no spell reward will display for this quest } + if(GetTalentSpellCost(qinfo->RewSpell)) + { + sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is talent, quest will not have a spell reward.", + qinfo->GetQuestId(),qinfo->RewSpell,qinfo->RewSpell); + qinfo->RewSpell = 0; // no spell reward will display for this quest + continue; + } } if(qinfo->RewSpellCast) @@ -3921,13 +3944,20 @@ void ObjectMgr::LoadQuests() qinfo->RewSpellCast = 0; // no spell will be casted on player } - else if(!SpellMgr::IsSpellValid(spellInfo)) + if(!SpellMgr::IsSpellValid(spellInfo)) { - sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u is broken, quest can't be done.", + sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u is broken, quest will not have a spell reward.", qinfo->GetQuestId(),qinfo->RewSpellCast,qinfo->RewSpellCast); qinfo->RewSpellCast = 0; // no spell will be casted on player } + if(GetTalentSpellCost(qinfo->RewSpellCast)) + { + sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is talent, quest will not have a spell reward.", + qinfo->GetQuestId(),qinfo->RewSpellCast,qinfo->RewSpellCast); + qinfo->RewSpellCast = 0; // no spell will be casted on player + continue; + } } if(qinfo->RewMailTemplateId) @@ -6131,6 +6161,16 @@ inline void CheckGONoDamageImmuneId(GameObjectInfo const* goInfo,uint32 dataN,ui goInfo->id,goInfo->type,N,dataN); } +inline void CheckGOConsumable(GameObjectInfo const* goInfo,uint32 dataN,uint32 N) +{ + // 0/1 correct values + if (dataN <= 1) + return; + + sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) consumable field value.", + goInfo->id,goInfo->type,N,dataN); +} + void ObjectMgr::LoadGameobjectInfo() { SQLGameObjectLoader loader; @@ -6173,6 +6213,8 @@ void ObjectMgr::LoadGameobjectInfo() if (goInfo->chest.lockId) CheckGOLockId(goInfo,goInfo->chest.lockId,0); + CheckGOConsumable(goInfo,goInfo->chest.consumable,3); + if (goInfo->chest.linkedTrapId) // linked trap CheckGOLinkedTrapId(goInfo,goInfo->chest.linkedTrapId,7); break; @@ -6208,6 +6250,8 @@ void ObjectMgr::LoadGameobjectInfo() if (goInfo->goober.lockId) CheckGOLockId(goInfo,goInfo->goober.lockId,0); + CheckGOConsumable(goInfo,goInfo->goober.consumable,3); + if (goInfo->goober.pageId) // pageId { if (!sPageTextStore.LookupEntry<PageText>(goInfo->goober.pageId)) @@ -7054,18 +7098,24 @@ bool isValidString(std::wstring wstr, uint32 strictMask, bool numericOrSpace, bo return false; } -bool ObjectMgr::IsValidName( const std::string& name, bool create ) +uint8 ObjectMgr::CheckPlayerName( const std::string& name, bool create ) { std::wstring wname; if(!Utf8toWStr(name,wname)) - return false; + return CHAR_NAME_INVALID_CHARACTER; - if(wname.size() < 1 || wname.size() > MAX_PLAYER_NAME) - return false; + if(wname.size() > MAX_PLAYER_NAME) + return CHAR_NAME_TOO_LONG; + + uint32 minName = sWorld.getConfig(CONFIG_MIN_PLAYER_NAME); + if(wname.size() < minName) + return CHAR_NAME_TOO_SHORT; uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PLAYER_NAMES); + if(!isValidString(wname,strictMask,false,create)) + return CHAR_NAME_MIXED_LANGUAGES; - return isValidString(wname,strictMask,false,create); + return CHAR_NAME_SUCCESS; } bool ObjectMgr::IsValidCharterName( const std::string& name ) @@ -7074,7 +7124,11 @@ bool ObjectMgr::IsValidCharterName( const std::string& name ) if(!Utf8toWStr(name,wname)) return false; - if(wname.size() < 1) + if(wname.size() > MAX_CHARTER_NAME) + return false; + + uint32 minName = sWorld.getConfig(CONFIG_MIN_CHARTER_NAME); + if(wname.size() < minName) return false; uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_CHARTER_NAMES); @@ -7082,18 +7136,24 @@ bool ObjectMgr::IsValidCharterName( const std::string& name ) return isValidString(wname,strictMask,true); } -bool ObjectMgr::IsValidPetName( const std::string& name ) +PetNameInvalidReason ObjectMgr::CheckPetName( const std::string& name ) { std::wstring wname; if(!Utf8toWStr(name,wname)) - return false; + return PET_NAME_INVALID; - if(wname.size() < 1) - return false; + if(wname.size() > MAX_PET_NAME) + return PET_NAME_TOO_LONG; + + uint32 minName = sWorld.getConfig(CONFIG_MIN_PET_NAME); + if(wname.size() < minName) + return PET_NAME_TOO_SHORT; uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PET_NAMES); + if(!isValidString(wname,strictMask,false)) + return PET_NAME_MIXED_LANGUAGES; - return isValidString(wname,strictMask,false); + return PET_NAME_SUCCESS; } int ObjectMgr::GetIndexForLocale( LocaleConstant loc ) @@ -7158,7 +7218,7 @@ void ObjectMgr::LoadGameObjectForQuests() // scan GO chest with loot including quest items case GAMEOBJECT_TYPE_CHEST: { - uint32 loot_id = GameObject::GetLootId(goInfo); + uint32 loot_id = goInfo->GetLootId(); // find quest loot for GO if(LootTemplates_Gameobject.HaveQuestLootFor(loot_id)) diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index f5189116463..b770c3044a4 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -234,7 +234,7 @@ enum ConditionType CONDITION_SKILL = 7, // skill_id skill_value CONDITION_QUESTREWARDED = 8, // quest_id 0 CONDITION_QUESTTAKEN = 9, // quest_id 0, for condition true while quest active. - CONDITION_AD_COMMISSION_AURA = 10, // 0 0, for condition true while one from AD Å„ommission aura active + CONDITION_AD_COMMISSION_AURA = 10, // 0 0, for condition true while one from AD commission aura active CONDITION_NO_AURA = 11, // spell_id effindex CONDITION_ACTIVE_EVENT = 12, // event_id CONDITION_INSTANCE_DATA = 13, // entry data @@ -312,8 +312,10 @@ struct GM_Ticket typedef std::list<GM_Ticket*> GmTicketList; SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial); -#define MAX_PLAYER_NAME 12 // max allowed by client name length +#define MAX_PLAYER_NAME 12 // max allowed by client name length #define MAX_INTERNAL_PLAYER_NAME 15 // max server internal player name length ( > MAX_PLAYER_NAME for support declined names ) +#define MAX_PET_NAME 12 // max allowed by client name length +#define MAX_CHARTER_NAME 24 // max allowed by client name length bool normalizePlayerName(std::string& name); @@ -628,6 +630,7 @@ class ObjectMgr CachePlayerInfoMap m_mPlayerInfoMap; uint32 CreateItemText(std::string text); + void AddItemText(uint32 itemTextId, std::string text) { mItemTexts[itemTextId] = text; } std::string GetItemText( uint32 id ) { ItemTextMap::const_iterator itr = mItemTexts.find( id ); @@ -759,9 +762,9 @@ class ObjectMgr bool IsReservedName(const std::string& name) const; // name with valid structure and symbols - static bool IsValidName( const std::string& name, bool create = false ); + static uint8 CheckPlayerName( const std::string& name, bool create = false ); + static PetNameInvalidReason CheckPetName( const std::string& name ); static bool IsValidCharterName( const std::string& name ); - static bool IsValidPetName( const std::string& name ); static bool CheckDeclinedNames(std::wstring mainpart, DeclinedName const& names); @@ -845,7 +848,7 @@ class ObjectMgr GM_Ticket *GetGMTicket(uint64 ticketGuid) { for(GmTicketList::const_iterator i = m_GMTicketList.begin(); i != m_GMTicketList.end(); ++i) - if((*i) && (*i)->guid == ticketGuid && (*i)->closed == 0) + if((*i) && (*i)->guid == ticketGuid) return (*i); return NULL; @@ -943,6 +946,7 @@ class ObjectMgr private: void LoadScripts(ScriptMapMap& scripts, char const* tablename); void CheckScripts(ScriptMapMap const& scripts,std::set<int32>& ids); + void LoadCreatureAddons(SQLStorage& creatureaddons, char const* entryName, char const* comment); void ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr); void LoadQuestRelationsHelper(QuestRelations& map,char const* table); diff --git a/src/game/Opcodes.cpp b/src/game/Opcodes.cpp index b66786f9d91..8fd57ad52be 100644 --- a/src/game/Opcodes.cpp +++ b/src/game/Opcodes.cpp @@ -1025,7 +1025,7 @@ OpcodeHandler opcodeTable[NUM_MSG_TYPES] = /*0x3E2*/ { "SMSG_COMSAT_CONNECT_FAIL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, /*0x3E3*/ { "SMSG_VOICE_CHAT_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, /*0x3E4*/ { "CMSG_REPORT_PVP_AFK", STATUS_LOGGEDIN, &WorldSession::HandleReportPvPAFK }, - /*0x3E5*/ { "CMSG_REPORT_PVP_AFK_RESULT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3E5*/ { "SMSG_REPORT_PVP_AFK_RESULT", STATUS_NEVER, &WorldSession::Handle_NULL }, /*0x3E6*/ { "CMSG_GUILD_BANKER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankerActivate }, /*0x3E7*/ { "CMSG_GUILD_BANK_QUERY_TAB", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankQueryTab }, /*0x3E8*/ { "SMSG_GUILD_BANK_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, @@ -1053,7 +1053,7 @@ OpcodeHandler opcodeTable[NUM_MSG_TYPES] = /*0x3FE*/ { "MSG_GUILD_BANK_MONEY_WITHDRAWN", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankMoneyWithdrawn }, /*0x3FF*/ { "MSG_GUILD_EVENT_LOG_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleGuildEventLogQueryOpcode }, /*0x400*/ { "CMSG_MAELSTROM_RENAME_GUILD", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x401*/ { "CMSG_GET_MIRRORIMAGE_DATA", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x401*/ { "CMSG_GET_MIRRORIMAGE_DATA", STATUS_LOGGEDIN, &WorldSession::HandleMirrrorImageDataRequest }, /*0x402*/ { "SMSG_MIRRORIMAGE_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, /*0x403*/ { "SMSG_FORCE_DISPLAY_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, /*0x404*/ { "SMSG_SPELL_CHANCE_RESIST_PUSHBACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, diff --git a/src/game/Opcodes.h b/src/game/Opcodes.h index b3d9c4f8f11..b468cc64978 100644 --- a/src/game/Opcodes.h +++ b/src/game/Opcodes.h @@ -694,8 +694,8 @@ enum Opcodes CMSG_GROUP_ASSISTANT_LEADER = 0x28F, CMSG_BUYBACK_ITEM = 0x290, SMSG_SERVER_MESSAGE = 0x291, - CMSG_MEETINGSTONE_JOIN = 0x292, - CMSG_MEETINGSTONE_LEAVE = 0x293, // SMSG? + CMSG_MEETINGSTONE_JOIN = 0x292, // lua: SetSavedInstanceExtend + SMSG_MEETINGSTONE_LEAVE = 0x293, CMSG_MEETINGSTONE_CHEAT = 0x294, SMSG_MEETINGSTONE_SETQUEUE = 0x295, CMSG_MEETINGSTONE_INFO = 0x296, @@ -1033,7 +1033,7 @@ enum Opcodes SMSG_COMSAT_CONNECT_FAIL = 0x3E2, SMSG_VOICE_CHAT_STATUS = 0x3E3, CMSG_REPORT_PVP_AFK = 0x3E4, - CMSG_REPORT_PVP_AFK_RESULT = 0x3E5, + SMSG_REPORT_PVP_AFK_RESULT = 0x3E5, // SMSG? CMSG_GUILD_BANKER_ACTIVATE = 0x3E6, CMSG_GUILD_BANK_QUERY_TAB = 0x3E7, SMSG_GUILD_BANK_LIST = 0x3E8, diff --git a/src/game/OutdoorPvP.cpp b/src/game/OutdoorPvP.cpp index fd1cf87e6d6..d3c96f53fcb 100644 --- a/src/game/OutdoorPvP.cpp +++ b/src/game/OutdoorPvP.cpp @@ -116,7 +116,6 @@ bool OPvPCapturePoint::DelCreature(uint32 type) // Don't save respawn time cr->SetRespawnTime(0); cr->RemoveCorpse(); - cr->CleanupsBeforeDelete(); // explicit removal from map // beats me why this is needed, but with the recent removal "cleanup" some creatures stay in the map if "properly" deleted // so this is a big fat workaround, if AddObjectToRemoveList and DoDelayedMovesAndRemoves worked correctly, this wouldn't be needed @@ -360,7 +359,7 @@ void OPvPCapturePoint::SendObjectiveComplete(uint32 id,uint64 guid) // send to all players present in the area for(PlayerSet::iterator itr = m_activePlayers[team].begin(); itr != m_activePlayers[team].end(); ++itr) - (*itr)->KilledMonster(id, guid); + (*itr)->KilledMonsterCredit(id, guid); } void OutdoorPvP::HandleKill(Player *killer, Unit * killed) diff --git a/src/game/OutdoorPvPNA.cpp b/src/game/OutdoorPvPNA.cpp index 53bf1a3c770..7a421b848ba 100644 --- a/src/game/OutdoorPvPNA.cpp +++ b/src/game/OutdoorPvPNA.cpp @@ -33,7 +33,7 @@ void OutdoorPvPNA::HandleKillImpl(Player *plr, Unit * killed) { if(killed->GetTypeId() == TYPEID_PLAYER && plr->GetTeam() != ((Player*)killed)->GetTeam()) { - plr->KilledMonster(NA_CREDIT_MARKER,0); // 0 guid, btw it isn't even used in killedmonster function :S + plr->KilledMonsterCredit(NA_CREDIT_MARKER,0); // 0 guid, btw it isn't even used in killedmonster function :S if(plr->GetTeam() == ALLIANCE) plr->CastSpell(plr,NA_KILL_TOKEN_ALLIANCE,true); else diff --git a/src/game/OutdoorPvPSI.cpp b/src/game/OutdoorPvPSI.cpp index c1f47db49c7..e60888c7e08 100644 --- a/src/game/OutdoorPvPSI.cpp +++ b/src/game/OutdoorPvPSI.cpp @@ -107,7 +107,7 @@ bool OutdoorPvPSI::HandleAreaTrigger(Player *plr, uint32 trigger) // add 20 cenarion circle repu plr->GetReputationMgr().ModifyReputation(sFactionStore.LookupEntry(609),20); // complete quest - plr->KilledMonster(SI_TURNIN_QUEST_CM_A,0); + plr->KilledMonsterCredit(SI_TURNIN_QUEST_CM_A,0); } return true; case SI_AREATRIGGER_H: @@ -132,7 +132,7 @@ bool OutdoorPvPSI::HandleAreaTrigger(Player *plr, uint32 trigger) // add 20 cenarion circle repu plr->GetReputationMgr().ModifyReputation(sFactionStore.LookupEntry(609),20); // complete quest - plr->KilledMonster(SI_TURNIN_QUEST_CM_H,0); + plr->KilledMonsterCredit(SI_TURNIN_QUEST_CM_H,0); } return true; } @@ -156,7 +156,7 @@ bool OutdoorPvPSI::HandleDropFlag(Player *plr, uint32 spellId) { // he dropped it further, summon mound GameObject * go = new GameObject; - Map * map = MapManager::Instance().GetMap(plr->GetMapId(), plr); + Map * map = plr->GetMap(); if(!map) { delete go; @@ -186,7 +186,7 @@ bool OutdoorPvPSI::HandleDropFlag(Player *plr, uint32 spellId) { // he dropped it further, summon mound GameObject * go = new GameObject; - Map * map = MapManager::Instance().GetMap(plr->GetMapId(), plr); + Map * map = plr->GetMap(); if(!map) { delete go; diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index 7448d6440ce..05764beec11 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -49,10 +49,15 @@ m_declinedname(NULL), m_owner(owner) m_summonMask |= SUMMON_MASK_PET; if(type == HUNTER_PET) m_summonMask |= SUMMON_MASK_HUNTER_PET; + + if (!(m_summonMask & SUMMON_MASK_CONTROLABLE_GUARDIAN)) + { + m_summonMask |= SUMMON_MASK_CONTROLABLE_GUARDIAN; + InitCharmInfo(); + } + m_name = "Pet"; m_regenTimer = 4000; - - owner->SetPetAtLoginFlag(0); } Pet::~Pet() @@ -93,24 +98,24 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool if (petnumber) // known petnumber entry 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType, load_flags " + result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " "FROM character_pet WHERE owner = '%u' AND id = '%u'", ownerid, petnumber); else if (current) // current pet (slot 0) 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType, load_flags " + result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " "FROM character_pet WHERE owner = '%u' AND slot = '%u'", ownerid, PET_SAVE_AS_CURRENT ); else if (petentry) // known petentry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets) // 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType, load_flags " + result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " "FROM character_pet WHERE owner = '%u' AND entry = '%u' AND (slot = '%u' OR slot > '%u') ", ownerid, petentry,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT); else // any current or other non-stabled pet (for hunter "call pet") // 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType, load_flags " + result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " "FROM character_pet WHERE owner = '%u' AND (slot = '%u' OR slot > '%u') ", ownerid,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT); @@ -151,7 +156,7 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool } uint32 pet_number = fields[0].GetUInt32(); - + if (current && owner->IsPetNeedBeTemporaryUnsummoned()) { owner->SetTemporaryUnsummonedPetNumber(pet_number); @@ -168,7 +173,7 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool } float px, py, pz; - owner->GetClosePoint(px, py, pz, GetObjectSize(), PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + owner->GetClosePoint(px, py, pz, GetObjectSize(), PET_FOLLOW_DIST, GetFollowAngle()); Relocate(px, py, pz, owner->GetOrientation()); if (!IsPositionValid()) @@ -284,29 +289,16 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool uint32 timediff = (time(NULL) - fields[14].GetUInt32()); _LoadAuras(timediff); - uint8 loadFlags = fields[19].GetUInt8(); - owner->SetPetAtLoginFlag(loadFlags); - if (loadFlags & AT_LOAD_RESET_SPELLS) - { - CharacterDatabase.PExecute("UPDATE character_pet SET load_flags = load_flags & ~ %u WHERE id = '%u'",uint32(AT_LOAD_RESET_SPELLS),pet_number); - loadFlags &= ~uint8(AT_LOAD_RESET_SPELLS); - CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u'",pet_number); - InitPetCreateSpells(); - resetTalents(true); - } - else + // load action bar, if data broken will fill later by default spells. + if (!is_temporary_summoned) { - // load action bar, if data broken will fill later by default spells. - if (!is_temporary_summoned) - { - m_charmInfo->LoadPetActionBar(fields[13].GetCppString()); + m_charmInfo->LoadPetActionBar(fields[13].GetCppString()); - _LoadSpells(); - _LoadSpellCooldowns(); - LearnPetPassives(); - InitLevelupSpellsForLevel(); - CastPetAuras(current); - } + _LoadSpells(); + _LoadSpellCooldowns(); + LearnPetPassives(); + InitLevelupSpellsForLevel(); + CastPetAuras(current); } CleanupActionBar(); // remove unknown spells from action bar after load @@ -408,7 +400,7 @@ void Pet::SavePetToDB(PetSaveMode mode) owner,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT); // save pet std::ostringstream ss; - ss << "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType, load_flags) " + ss << "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType) " << "VALUES (" << m_charmInfo->GetPetNumber() << ", " << GetEntry() << ", " @@ -427,8 +419,8 @@ void Pet::SavePetToDB(PetSaveMode mode) // save only spell slots from action bar for(uint32 i = ACTION_BAR_INDEX_PET_SPELL_START; i < ACTION_BAR_INDEX_PET_SPELL_END; ++i) { - ss << uint32(m_charmInfo->GetActionBarEntry(i)->Type) << " " - << uint32(m_charmInfo->GetActionBarEntry(i)->SpellOrAction) << " "; + ss << uint32(m_charmInfo->GetActionBarEntry(i)->GetType()) << " " + << uint32(m_charmInfo->GetActionBarEntry(i)->GetAction()) << " "; }; ss << "', " @@ -436,8 +428,7 @@ void Pet::SavePetToDB(PetSaveMode mode) << uint32(m_resetTalentsCost) << ", " << uint64(m_resetTalentsTime) << ", " << GetUInt32Value(UNIT_CREATED_BY_SPELL) << ", " - << uint32(getPetType()) << ", " - << (pOwner->GetAtLoginFlag()>>AT_LOAD_PET_FLAGS) << ")"; + << uint32(getPetType()) << ")"; CharacterDatabase.Execute( ss.str().c_str() ); CharacterDatabase.CommitTransaction(); @@ -448,7 +439,6 @@ void Pet::SavePetToDB(PetSaveMode mode) RemoveAllAuras(); DeleteFromDB(m_charmInfo->GetPetNumber()); } - pOwner->SetPetAtLoginFlag(0); } void Pet::DeleteFromDB(uint32 guidlow) @@ -732,9 +722,6 @@ bool Pet::CreateBaseAtCreature(Creature* creature) } uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET); - sLog.outDebug("SetInstanceID()"); - SetInstanceId(creature->GetInstanceId()); - sLog.outDebug("Create pet"); uint32 pet_number = objmgr.GeneratePetNumber(); if(!Create(guid, creature->GetMap(), creature->GetPhaseMask(), creature->GetEntry(), pet_number)) @@ -941,6 +928,16 @@ bool Guardian::InitStatsForLevel(uint32 petlevel) SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel * 4 + petlevel)); break; } + case 31216: // Mirror Image + { + SetBonusDamage( int32(m_owner->SpellBaseDamageBonus(SPELL_SCHOOL_MASK_FROST) * 0.33f)); + if(!pInfo) + { + SetCreateMana(28 + 30*petlevel); + SetCreateHealth(28 + 10*petlevel); + } + break; + } default: { if(!pInfo) @@ -1081,7 +1078,7 @@ void Pet::_LoadSpells() { Field *fields = result->Fetch(); - addSpell(fields[0].GetUInt32(), ActiveStates(fields[1].GetUInt16()), PETSPELL_UNCHANGED); + addSpell(fields[0].GetUInt32(), ActiveStates(fields[1].GetUInt8()), PETSPELL_UNCHANGED); } while( result->NextRow() ); @@ -1346,8 +1343,8 @@ bool Pet::learnSpell(uint32 spell_id) if(!m_loading) { - WorldPacket data(SMSG_PET_LEARNED_SPELL, 2); - data << uint16(spell_id); + WorldPacket data(SMSG_PET_LEARNED_SPELL, 4); + data << uint32(spell_id); m_owner->GetSession()->SendPacket(&data); m_owner->PetSpellInitialize(); } @@ -1399,8 +1396,8 @@ bool Pet::unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab) { if(!m_loading) { - WorldPacket data(SMSG_PET_REMOVED_SPELL, 2); - data << uint16(spell_id); + WorldPacket data(SMSG_PET_REMOVED_SPELL, 4); + data << uint32(spell_id); m_owner->GetSession()->SendPacket(&data); } return true; @@ -1464,12 +1461,12 @@ void Pet::CleanupActionBar() { for(uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i) if(UnitActionBarEntry const* ab = m_charmInfo->GetActionBarEntry(i)) - if(ab->SpellOrAction && ab->IsActionBarForSpell()) + if(ab->GetAction() && ab->IsActionBarForSpell()) { - if(!HasSpell(ab->SpellOrAction)) + if(!HasSpell(ab->GetAction())) m_charmInfo->SetActionBar(i, 0, ACT_PASSIVE); - else if(ab->Type == ACT_ENABLED) - ToggleAutocast(ab->SpellOrAction, true); + else if(ab->GetType() == ACT_ENABLED) + ToggleAutocast(ab->GetAction(), true); } } @@ -1490,6 +1487,10 @@ bool Pet::resetTalents(bool no_cost) if (!owner || owner->GetTypeId()!=TYPEID_PLAYER) return false; + // not need after this call + if(((Player*)owner)->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS)) + ((Player*)owner)->RemoveAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS,true); + CreatureInfo const * ci = GetCreatureInfo(); if(!ci) return false; @@ -1576,6 +1577,90 @@ bool Pet::resetTalents(bool no_cost) return true; } +void Pet::resetTalentsForAllPetsOf(Player* owner, Pet* online_pet /*= NULL*/) +{ + // not need after this call + if(((Player*)owner)->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS)) + ((Player*)owner)->RemoveAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS,true); + + // reset for online + if(online_pet) + online_pet->resetTalents(true); + + // now need only reset for offline pets (all pets except online case) + uint32 except_petnumber = online_pet ? online_pet->GetCharmInfo()->GetPetNumber() : 0; + + QueryResult *resultPets = CharacterDatabase.PQuery( + "SELECT id FROM character_pet WHERE owner = '%u' AND id <> '%u'", + owner->GetGUIDLow(),except_petnumber); + + // no offline pets + if(!resultPets) + return; + + QueryResult *result = CharacterDatabase.PQuery( + "SELECT DISTINCT pet_spell.spell FROM pet_spell, character_pet " + "WHERE character_pet.owner = '%u' AND character_pet.id = pet_spell.guid AND character_pet.id <> %u", + owner->GetGUIDLow(),except_petnumber); + + if(!result) + { + delete resultPets; + return; + } + + bool need_comma = false; + std::ostringstream ss; + ss << "DELETE FROM pet_spell WHERE guid IN ("; + + do + { + Field *fields = resultPets->Fetch(); + + uint32 id = fields[0].GetUInt32(); + + if(need_comma) + ss << ","; + + ss << id; + + need_comma = true; + } + while( resultPets->NextRow() ); + + delete resultPets; + + ss << ") AND spell IN ("; + + bool need_execute = false; + do + { + Field *fields = result->Fetch(); + + uint32 spell = fields[0].GetUInt32(); + + if(!GetTalentSpellCost(spell)) + continue; + + if(need_execute) + ss << ","; + + ss << spell; + + need_execute = true; + } + while( result->NextRow() ); + + delete result; + + if(!need_execute) + return; + + ss << ")"; + + CharacterDatabase.Execute(ss.str().c_str()); +} + void Pet::InitTalentForLevel() { uint32 level = getLevel(); @@ -1693,10 +1778,10 @@ bool Pet::IsPermanentPetFor(Player* owner) bool Pet::Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 pet_number) { - SetMapId(map->GetId()); - SetInstanceId(map->GetInstanceId()); - SetPhaseMask(phaseMask,false); + assert(map); + SetMap(map); + SetPhaseMask(phaseMask,false); Object::_Create(guidlow, pet_number, HIGHGUID_PET); m_DBTableGuid = guidlow; @@ -1761,7 +1846,7 @@ void Pet::CastPetAuras(bool current) void Pet::CastPetAura(PetAura const* aura) { - uint16 auraId = aura->GetAura(GetEntry()); + uint32 auraId = aura->GetAura(GetEntry()); if(!auraId) return; diff --git a/src/game/Pet.h b/src/game/Pet.h index 49daad429ed..88287ce5dee 100644 --- a/src/game/Pet.h +++ b/src/game/Pet.h @@ -89,14 +89,11 @@ enum PetTalk PET_TALK_ATTACK = 1 }; -enum AtLoadFlags -{ - AT_LOAD_NONE = 0, - AT_LOAD_RESET_SPELLS = 1, -}; - enum PetNameInvalidReason { + // custom, not send + PET_NAME_SUCCESS = 0, + PET_NAME_INVALID = 1, PET_NAME_NO_NAME = 2, PET_NAME_TOO_SHORT = 3, @@ -212,6 +209,7 @@ class Pet : public Guardian void InitPetCreateSpells(); bool resetTalents(bool no_cost = false); + static void resetTalentsForAllPetsOf(Player* owner, Pet* online_pet = NULL); uint32 resetTalentsCost() const; void InitTalentForLevel(); diff --git a/src/game/PetAI.cpp b/src/game/PetAI.cpp index 7851ee6773f..0fcc707b104 100644 --- a/src/game/PetAI.cpp +++ b/src/game/PetAI.cpp @@ -74,7 +74,7 @@ void PetAI::_stopAttack() if(owner && m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) { - m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); + m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST, m_creature->GetFollowAngle()); } else { @@ -112,17 +112,22 @@ void PetAI::UpdateAI(const uint32 diff) if(owner->isInCombat() && !(m_creature->HasReactState(REACT_PASSIVE) || m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY))) AttackStart(owner->getAttackerForHelper()); else if(m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW) && !m_creature->hasUnitState(UNIT_STAT_FOLLOW)) - m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); + m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST, m_creature->GetFollowAngle()); } + else if (owner && !m_creature->hasUnitState(UNIT_STAT_FOLLOW)) // no charm info and no victim + m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST, m_creature->GetFollowAngle()); if(!me->GetCharmInfo()) return; + bool inCombat = me->getVictim(); + + // Autocast (casted only in combat or persistent spells in any state) if (m_creature->GetGlobalCooldown() == 0 && !m_creature->hasUnitState(UNIT_STAT_CASTING)) { - bool inCombat = me->getVictim(); + typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; + TargetSpellList targetSpellStore; - //Autocast for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); ++i) { uint32 spellID = m_creature->GetPetAutoSpellOnPos(i); @@ -136,20 +141,38 @@ void PetAI::UpdateAI(const uint32 diff) // ignore some combinations of combat state and combat/noncombat spells if (!inCombat) { + // ignore attacking spells, and allow only self/around spells if (!IsPositiveSpell(spellInfo->Id)) continue; + + // non combat spells allowed + // only pet spells have IsNonCombatSpell and not fit this reqs: + // Consume Shadows, Lesser Invisibility, so ignore checks for its + if (!IsNonCombatSpell(spellInfo)) + { + // allow only spell without spell cost or with spell cost but not duration limit + int32 duration = GetSpellDuration(spellInfo); + if ((spellInfo->manaCost || spellInfo->ManaCostPercentage || spellInfo->manaPerSecond) && duration > 0) + continue; + + // allow only spell without cooldown > duration + int32 cooldown = GetSpellRecoveryTime(spellInfo); + if (cooldown >= 0 && duration >= 0 && cooldown > duration) + continue; + } } else { + // just ignore non-combat spells if (IsNonCombatSpell(spellInfo)) continue; } Spell *spell = new Spell(m_creature, spellInfo, false, 0); - if(inCombat && !m_creature->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(m_creature->getVictim())) + if (inCombat && !m_creature->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(m_creature->getVictim())) { - m_targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(m_creature->getVictim(), spell)); + targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(m_creature->getVictim(), spell)); continue; } else @@ -165,7 +188,7 @@ void PetAI::UpdateAI(const uint32 diff) if(spell->CanAutoCast(Target)) { - m_targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(Target, spell)); + targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(Target, spell)); spellUsed = true; break; } @@ -176,14 +199,14 @@ void PetAI::UpdateAI(const uint32 diff) } //found units to cast on to - if(!m_targetSpellStore.empty()) + if (!targetSpellStore.empty()) { - uint32 index = urand(0, m_targetSpellStore.size() - 1); + uint32 index = urand(0, targetSpellStore.size() - 1); - Spell* spell = m_targetSpellStore[index].second; - Unit* target = m_targetSpellStore[index].first; + Spell* spell = targetSpellStore[index].second; + Unit* target = targetSpellStore[index].first; - m_targetSpellStore.erase(m_targetSpellStore.begin() + index); + targetSpellStore.erase(targetSpellStore.begin() + index); SpellCastTargets targets; targets.setUnitTarget( target ); @@ -202,11 +225,10 @@ void PetAI::UpdateAI(const uint32 diff) spell->prepare(&targets); } - while (!m_targetSpellStore.empty()) - { - delete m_targetSpellStore.begin()->second; - m_targetSpellStore.erase(m_targetSpellStore.begin()); - } + + // deleted cached Spell objects + for(TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) + delete itr->second; } } diff --git a/src/game/PetAI.h b/src/game/PetAI.h index 67ce6edefe3..918259cd098 100644 --- a/src/game/PetAI.h +++ b/src/game/PetAI.h @@ -50,9 +50,6 @@ class TRINITY_DLL_DECL PetAI : public CreatureAI bool inCombat; std::set<uint64> m_AllySet; uint32 m_updateAlliesTimer; - - typedef std::pair<Unit*, Spell*> TargetSpellPair; - std::vector<TargetSpellPair> m_targetSpellStore; }; #endif diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp index 3960b59d6d4..3de8ee028d3 100644 --- a/src/game/PetHandler.cpp +++ b/src/game/PetHandler.cpp @@ -37,17 +37,18 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) CHECK_PACKET_SIZE(recv_data, 8+2+2+8); uint64 guid1; - uint16 spellid; - uint16 flag; + uint32 data; uint64 guid2; recv_data >> guid1; //pet guid - recv_data >> spellid; - recv_data >> flag; //delete = 0x0700 CastSpell = C100 + recv_data >> data; recv_data >> guid2; //tag guid + uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data); + uint8 flag = UNIT_ACTION_BUTTON_TYPE(data); //delete = 0x07 CastSpell = C1 + // used also for charmed creature Unit* pet= ObjectAccessor::GetUnit(*_player, guid1); - sLog.outDetail("HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.", uint32(GUID_LOPART(guid1)), flag, spellid, uint32(GUID_LOPART(guid2)) ); + sLog.outDetail("HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.", uint32(GUID_LOPART(guid1)), uint32(flag), spellid, uint32(GUID_LOPART(guid2)) ); if(!pet) { sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid1)) ); @@ -89,7 +90,7 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid switch(flag) { - case ACT_COMMAND: //0x0700 + case ACT_COMMAND: //0x07 switch(spellid) { case COMMAND_STAY: //flat=1792 //STAY @@ -101,7 +102,7 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid case COMMAND_FOLLOW: //spellid=1792 //FOLLOW pet->AttackStop(); pet->InterruptNonMeleeSpells(false); - pet->GetMotionMaster()->MoveFollow(_player,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); + pet->GetMotionMaster()->MoveFollow(_player,PET_FOLLOW_DIST,pet->GetFollowAngle()); charmInfo->SetCommandState( COMMAND_FOLLOW ); break; case COMMAND_ATTACK: //spellid=1792 //ATTACK @@ -151,7 +152,7 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid pet->SendPetAIReaction(guid1); } } - else // charmed player + else // charmed player { if(pet->getVictim() && pet->getVictim() != TargetUnit) pet->AttackStop(); @@ -183,10 +184,10 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid } break; default: - sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", flag, spellid); + sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } break; - case ACT_REACTION: // 0x600 + case ACT_REACTION: // 0x6 switch(spellid) { case REACT_PASSIVE: //passive @@ -197,9 +198,9 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid break; } break; - case ACT_DISABLED: // 0x8100 spell (disabled), ignore - case ACT_PASSIVE: // 0x0100 - case ACT_ENABLED: // 0xC100 spell + case ACT_DISABLED: // 0x81 spell (disabled), ignore + case ACT_PASSIVE: // 0x01 + case ACT_ENABLED: // 0xC1 spell { Unit* unit_target = NULL; if (((Creature*)pet)->GetGlobalCooldown() > 0) @@ -291,7 +292,7 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid pet->SendPetCastFail(spellid, result); if(!((Creature*)pet)->HasSpellCooldown(spellid)) - pet->SendPetClearCooldown(spellid); + GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; @@ -299,7 +300,7 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid break; } default: - sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", flag, spellid); + sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } } @@ -350,9 +351,6 @@ void WorldSession::HandlePetSetAction( WorldPacket & recv_data ) sLog.outDetail( "HandlePetSetAction. CMSG_PET_SET_ACTION" ); uint64 petguid; - uint32 position; - uint16 spell_id; - uint16 act_state; uint8 count; recv_data >> petguid; @@ -375,11 +373,16 @@ void WorldSession::HandlePetSetAction( WorldPacket & recv_data ) count = (recv_data.size() == 24) ? 2 : 1; for(uint8 i = 0; i < count; ++i) { + uint32 position; + uint32 data; + recv_data >> position; - recv_data >> spell_id; - recv_data >> act_state; + recv_data >> data; - sLog.outDetail( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", _player->GetName(), position, spell_id, act_state); + uint32 spell_id = UNIT_ACTION_BUTTON_ACTION(data); + uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data); + + sLog.outDetail( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", _player->GetName(), position, spell_id, uint32(act_state)); //ignore invalid position if(position >= MAX_UNIT_ACTION_BAR_INDEX) @@ -435,9 +438,10 @@ void WorldSession::HandlePetRename( WorldPacket & recv_data ) pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo() ) return; - if(!ObjectMgr::IsValidPetName(name)) + PetNameInvalidReason res = ObjectMgr::CheckPetName(name); + if(res != PET_NAME_SUCCESS) { - SendPetNameInvalid(PET_NAME_INVALID, name, NULL); + SendPetNameInvalid(res, name, NULL); return; } @@ -497,6 +501,9 @@ void WorldSession::HandlePetAbandon( WorldPacket & recv_data ) recv_data >> guid; //pet guid sLog.outDetail( "HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid) ); + if(!_player->IsInWorld()) + return; + // pet/charmed Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); if(pet) @@ -551,10 +558,9 @@ void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket ) sLog.outDetail("CMSG_PET_SPELL_AUTOCAST"); uint64 guid; - uint16 spellid; - uint16 spellid2; //maybe second spell, automatically toggled off when first toggled on? + uint32 spellid; uint8 state; //1 for on, 0 for off - recvPacket >> guid >> spellid >> spellid2 >> state; + recvPacket >> guid >> spellid >> state; if(!_player->GetGuardianPet() && !_player->GetCharm()) return; @@ -615,12 +621,6 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) return; } - if (caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->GetGlobalCooldown() > 0) - { - caster->SendPetCastFail(spellid, SPELL_FAILED_NOT_READY); - return; - } - SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); if(!spellInfo) { @@ -628,6 +628,13 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) return; } + if (spellInfo->StartRecoveryCategory > 0) //Check if spell is affected by GCD + if (caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->GetGlobalCooldown() > 0) + { + caster->SendPetCastFail(spellid, SPELL_FAILED_NOT_READY); + return; + } + // do not cast not learned spells if(!caster->HasSpell(spellid) || IsPassiveSpell(spellid)) return; @@ -669,12 +676,12 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) if(caster->GetTypeId() == TYPEID_PLAYER) { if(!((Player*)caster)->HasSpellCooldown(spellid)) - caster->SendPetClearCooldown(spellid); + GetPlayer()->SendClearCooldown(spellid, caster); } else { if(!((Creature*)caster)->HasSpellCooldown(spellid)) - caster->SendPetClearCooldown(spellid); + GetPlayer()->SendClearCooldown(spellid, caster); } spell->finish(false); 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); +} diff --git a/src/game/Player.h b/src/game/Player.h index 111e5def205..a63cb719d6f 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -57,7 +57,6 @@ typedef std::deque<Mail*> PlayerMails; #define PLAYER_MAX_SKILLS 127 #define PLAYER_MAX_DAILY_QUESTS 25 -#define AT_LOAD_PET_FLAGS 16 // Note: SPELLMOD_* values is aura types in fact enum SpellModType @@ -135,24 +134,40 @@ enum ActionButtonUpdateState ACTIONBUTTON_DELETED = 3 }; +enum ActionButtonType +{ + ACTION_BUTTON_SPELL = 0x00, + ACTION_BUTTON_C = 0x01, // click? + ACTION_BUTTON_EQSET = 0x20, + ACTION_BUTTON_MACRO = 0x40, + ACTION_BUTTON_CMACRO = ACTION_BUTTON_C | ACTION_BUTTON_MACRO, + ACTION_BUTTON_ITEM = 0x80 +}; + +#define ACTION_BUTTON_ACTION(X) (uint32(X) & 0x00FFFFFF) +#define ACTION_BUTTON_TYPE(X) ((uint32(X) & 0xFF000000) >> 24) +#define MAX_ACTION_BUTTON_ACTION_VALUE (0x00FFFFFF+1) + struct ActionButton { - ActionButton() : action(0), type(0), misc(0), uState( ACTIONBUTTON_NEW ) {} - ActionButton(uint16 _action, uint8 _type, uint8 _misc) : action(_action), type(_type), misc(_misc), uState( ACTIONBUTTON_NEW ) {} + ActionButton() : packedData(0), uState( ACTIONBUTTON_NEW ) {} - uint16 action; - uint8 type; - uint8 misc; + uint32 packedData; ActionButtonUpdateState uState; -}; -enum ActionButtonType -{ - ACTION_BUTTON_SPELL = 0, - ACTION_BUTTON_EQSET = 32, - ACTION_BUTTON_MACRO = 64, - ACTION_BUTTON_CMACRO= 65, - ACTION_BUTTON_ITEM = 128 + // helpers + ActionButtonType GetType() const { return ActionButtonType(ACTION_BUTTON_TYPE(packedData)); } + uint32 GetAction() const { return ACTION_BUTTON_ACTION(packedData); } + void SetActionAndType(uint32 action, ActionButtonType type) + { + uint32 newData = action | (uint32(type) << 24); + if (newData != packedData) + { + packedData = newData; + if (uState != ACTIONBUTTON_NEW) + uState = ACTIONBUTTON_CHANGED; + } + } }; #define MAX_ACTION_BUTTONS 132 //checked in 2.3.0 @@ -192,6 +207,18 @@ struct PlayerLevelInfo typedef std::list<uint32> PlayerCreateInfoSpells; +struct PlayerCreateInfoAction +{ + PlayerCreateInfoAction() : button(0), type(0), action(0) {} + PlayerCreateInfoAction(uint8 _button, uint32 _action, uint8 _type) : button(_button), type(_type), action(_action) {} + + uint8 button; + uint8 type; + uint32 action; +}; + +typedef std::list<PlayerCreateInfoAction> PlayerCreateInfoActions; + struct PlayerInfo { // existence checked by displayId != 0 // existence checked by displayId != 0 @@ -208,7 +235,7 @@ struct PlayerInfo uint16 displayId_f; PlayerCreateInfoItems item; PlayerCreateInfoSpells spell; - std::list<uint16> action[4]; + PlayerCreateInfoActions action; PlayerLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1 }; @@ -267,6 +294,7 @@ struct Runes { RuneInfo runes[MAX_RUNES]; uint8 runeState; // mask of available runes + RuneType lastUsedRune; void SetRuneState(uint8 index, bool set = true) { @@ -450,6 +478,8 @@ enum PlayerFlags #define PLAYER_TITLE_HAND_OF_ADAL UI64LIT(0x0000008000000000) // 39 #define PLAYER_TITLE_VENGEFUL_GLADIATOR UI64LIT(0x0000010000000000) // 40 +#define MAX_TITLE_INDEX (3*64) // 3 uint64 fields + // used in PLAYER_FIELD_BYTES values enum PlayerFieldByteFlags { @@ -510,11 +540,12 @@ enum PlayerExtraFlags // 2^n values enum AtLoginFlags { - AT_LOGIN_NONE = 0, - AT_LOGIN_RENAME = 1, - AT_LOGIN_RESET_SPELLS = 2, - AT_LOGIN_RESET_TALENTS = 4, - AT_LOGIN_CUSTOMIZE = 8, + AT_LOGIN_NONE = 0x00, + AT_LOGIN_RENAME = 0x01, + AT_LOGIN_RESET_SPELLS = 0x02, + AT_LOGIN_RESET_TALENTS = 0x04, + AT_LOGIN_CUSTOMIZE = 0x08, + AT_LOGIN_RESET_PET_TALENTS = 0x10, }; typedef std::map<uint32, QuestStatusData> QuestStatusMap; @@ -726,6 +757,14 @@ enum EnviromentalDamage DAMAGE_FALL_TO_VOID = 6 // custom case for fall without durability loss }; +enum PlayedTimeIndex +{ + PLAYED_TIME_TOTAL = 0, + PLAYED_TIME_LEVEL = 1 +}; + +#define MAX_PLAYED_TIME_INDEX 2 + // used at player loading query list preparing, and later result selection enum PlayerLoginQueryIndex { @@ -753,6 +792,14 @@ enum PlayerLoginQueryIndex MAX_PLAYER_LOGIN_QUERY = 21 }; +enum PlayerDelayedOperations +{ + DELAYED_SAVE_PLAYER = 1, + DELAYED_RESURRECT_PLAYER = 2, + DELAYED_SPELL_CAST_DESERTER = 4, + DELAYED_END +}; + // Player summoning auto-decline time (in secs) #define MAX_PLAYER_SUMMON_DELAY (2*MINUTE) #define MAX_MONEY_AMOUNT (0x7FFFFFFF-1) @@ -853,10 +900,11 @@ class TRINITY_DLL_SPEC Player : public Unit void RemoveFromWorld(); bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options = 0); + void TeleportOutOfMap(Map *oldMap); bool TeleportTo(WorldLocation const &loc, uint32 options = 0) { - return TeleportTo(loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z, options); + return TeleportTo(loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z, loc.orientation, options); } void SetSummonPoint(uint32 mapid, float x, float y, float z) @@ -873,7 +921,7 @@ class TRINITY_DLL_SPEC Player : public Unit void Update( uint32 time ); - void BuildEnumData( QueryResult * result, WorldPacket * p_data ); + static bool BuildEnumData( QueryResult * result, WorldPacket * p_data ); void SetInWater(bool apply); @@ -905,6 +953,7 @@ class TRINITY_DLL_SPEC Player : public Unit void InitTaxiNodesForLevel() { m_taxi.InitTaxiNodesForLevel(getRace(), getClass(), getLevel()); } bool ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc = NULL, uint32 spellid = 0); bool ActivateTaxiPathTo(uint32 taxi_path_id, uint32 spellid = 0); + void CleanupAfterTaxiFlight(); // mount_id can be used in scripting calls bool isAcceptWhispers() const { return m_ExtraFlags & PLAYER_EXTRA_ACCEPT_WHISPERS; } void SetAcceptWhispers(bool on) { if(on) m_ExtraFlags |= PLAYER_EXTRA_ACCEPT_WHISPERS; else m_ExtraFlags &= ~PLAYER_EXTRA_ACCEPT_WHISPERS; } @@ -926,9 +975,9 @@ class TRINITY_DLL_SPEC Player : public Unit // Played Time Stuff time_t m_logintime; time_t m_Last_tick; - uint32 m_Played_time[2]; - uint32 GetTotalPlayedTime() { return m_Played_time[0]; }; - uint32 GetLevelPlayedTime() { return m_Played_time[1]; }; + uint32 m_Played_time[MAX_PLAYED_TIME_INDEX]; + uint32 GetTotalPlayedTime() { return m_Played_time[PLAYED_TIME_TOTAL]; }; + uint32 GetLevelPlayedTime() { return m_Played_time[PLAYED_TIME_LEVEL]; }; void setDeathState(DeathState s); // overwrite Unit::setDeathState @@ -1090,7 +1139,7 @@ class TRINITY_DLL_SPEC Player : public Unit return mainItem && mainItem->GetProto()->InventoryType == INVTYPE_2HWEAPON && !CanTitanGrip(); } void SendNewItem( Item *item, uint32 count, bool received, bool created, bool broadcast = false ); - bool BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint64 bagguid, uint8 slot); + bool BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint8 bag, uint8 slot); float GetReputationPriceDiscount( Creature const* pCreature ) const; Player* GetTrader() const { return pTrader; } @@ -1193,7 +1242,8 @@ class TRINITY_DLL_SPEC Player : public Unit void GroupEventHappens( uint32 questId, WorldObject const* pEventObject ); void ItemAddedQuestCheck( uint32 entry, uint32 count ); void ItemRemovedQuestCheck( uint32 entry, uint32 count ); - void KilledMonster( uint32 entry, uint64 guid ); + void KilledMonster( CreatureInfo const* cInfo, uint64 guid ); + void KilledMonsterCredit( uint32 entry, uint64 guid ); void CastedCreatureOrGO( uint32 entry, uint64 guid, uint32 spell_id ); void TalkedToCreature( uint32 entry, uint64 guid ); void MoneyChanged( uint32 value ); @@ -1234,6 +1284,7 @@ class TRINITY_DLL_SPEC Player : public Unit static uint32 GetUInt32ValueFromDB(uint16 index, uint64 guid); static float GetFloatValueFromDB(uint16 index, uint64 guid); static uint32 GetZoneIdFromDB(uint64 guid); + static uint32 GetLevelFromDB(uint64 guid); static bool LoadPositionFromDB(uint32& mapid, float& x,float& y,float& z,float& o, bool& in_flight, uint64 guid); /*********************************************************/ @@ -1242,6 +1293,7 @@ class TRINITY_DLL_SPEC Player : public Unit void SaveToDB(); void SaveInventoryAndGoldToDB(); // fast save function for item/money cheating preventing + void SaveGoldToDB(); void SaveDataFieldToDB(); static bool SaveValuesArrayInDB(Tokens const& data,uint64 guid); static void SetUInt32ValueInArray(Tokens& data,uint16 index, uint32 value); @@ -1256,7 +1308,6 @@ class TRINITY_DLL_SPEC Player : public Unit void SetBindPoint(uint64 guid); void SendTalentWipeConfirm(uint64 guid); - void RewardRage( uint32 damage, uint32 weaponSpeedHitFactor, bool attacker ); void SendPetSkillWipeConfirm(); void CalcRage( uint32 damage,bool attacker ); void RegenerateAll(); @@ -1292,7 +1343,8 @@ class TRINITY_DLL_SPEC Player : public Unit uint8 GetComboPoints() { return m_comboPoints; } const uint64& GetComboTarget() const { return m_comboTarget; } - void AddComboPoints(Unit* target, int8 count); + void AddComboPoints(Unit* target, int8 count, Spell * spell = NULL); + void GainSpellComboPoints(int8 count); void ClearComboPoints(); void SendComboPoints(); @@ -1356,7 +1408,7 @@ class TRINITY_DLL_SPEC Player : public Unit void SendInitialSpells(); bool addSpell(uint32 spell_id, bool active, bool learning, bool dependent, bool disabled); void learnSpell(uint32 spell_id, bool dependent); - void removeSpell(uint32 spell_id, bool disabled = false, bool update_action_bar_for_low_rank = false); + void removeSpell(uint32 spell_id, bool disabled = false, bool learn_low_rank = true); void resetSpells(); void learnDefaultSpells(); void learnQuestRewardedSpells(); @@ -1398,6 +1450,8 @@ class TRINITY_DLL_SPEC Player : public Unit PlayerSpellMap const& GetSpellMap() const { return m_spells; } PlayerSpellMap & GetSpellMap() { return m_spells; } + SpellCooldowns const& GetSpellCooldownMap() const { return m_spellCooldowns; } + void AddSpellMod(SpellModifier* mod, bool apply); bool IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mod, Spell * spell = NULL); template <class T> T ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell * spell = NULL); @@ -1406,6 +1460,8 @@ class TRINITY_DLL_SPEC Player : public Unit void DropModCharge(SpellModifier * mod, Spell * spell); void SetSpellModTakingSpell(Spell* spell, bool apply); + static uint32 const infinityCooldownDelay = MONTH; // used for set "infinity cooldowns" for spells and check + static uint32 const infinityCooldownDelayCheck = MONTH/2; bool HasSpellCooldown(uint32 spell_id) const { SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id); @@ -1422,6 +1478,9 @@ class TRINITY_DLL_SPEC Player : public Unit void SendCooldownEvent(SpellEntry const *spellInfo, uint32 itemId = 0, Spell* spell = NULL); void ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs ); void RemoveSpellCooldown(uint32 spell_id, bool update = false); + void RemoveSpellCategoryCooldown(uint32 cat, bool update = false); + void SendClearCooldown( uint32 spell_id, Unit* target ); + void RemoveCategoryCooldown(uint32 cat); void RemoveArenaSpellCooldowns(); void RemoveAllSpellCooldown(); @@ -1454,7 +1513,7 @@ class TRINITY_DLL_SPEC Player : public Unit m_cinematic = cine; } - bool addActionButton(uint8 button, uint16 action, uint8 type, uint8 misc); + ActionButton* addActionButton(uint8 button, uint32 action, uint8 type); void removeActionButton(uint8 button); void SendInitialActionButtons() const; @@ -1597,7 +1656,7 @@ class TRINITY_DLL_SPEC Player : public Unit void SetSession(WorldSession *s) { m_session = s; } void BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) const; - void DestroyForPlayer( Player *target ) const; + void DestroyForPlayer( Player *target, bool anim = false ) const; void SendDelayResponse(const uint32); void SendLogXPGain(uint32 GivenXP,Unit* victim,uint32 RestXP); @@ -1607,7 +1666,7 @@ class TRINITY_DLL_SPEC Player : public Unit void SendAttackSwingDeadTarget(); void SendAttackSwingNotInRange(); void SendAttackSwingBadFacingAttack(); - void SendAutoRepeatCancel(); + void SendAutoRepeatCancel(Unit *target); void SendExplorationExperience(uint32 Area, uint32 Experience); void SendDungeonDifficulty(bool IsInGroup); @@ -1681,6 +1740,7 @@ class TRINITY_DLL_SPEC Player : public Unit bool IsBeingTeleportedFar() const { return mSemaphoreTeleport_Far; } void SetSemaphoreTeleportNear(bool semphsetting) { mSemaphoreTeleport_Near = semphsetting; } void SetSemaphoreTeleportFar(bool semphsetting) { mSemaphoreTeleport_Far = semphsetting; } + void ProcessDelayedOperations(); void CheckExploreSystem(void); @@ -1690,6 +1750,8 @@ class TRINITY_DLL_SPEC Player : public Unit static uint32 getFactionForRace(uint8 race); void setFactionForRace(uint8 race); + void InitDisplayIds(); + bool IsAtGroupRewardDistance(WorldObject const* pRewardSource) const; bool RewardPlayerAndGroupAtKill(Unit* pVictim); void RewardPlayerAndGroupAtEvent(uint32 creature_id,WorldObject* pRewardSource); @@ -1755,7 +1817,8 @@ class TRINITY_DLL_SPEC Player : public Unit void _ApplyItemMods(Item *item,uint8 slot,bool apply); void _RemoveAllItemMods(); void _ApplyAllItemMods(); - void _ApplyItemBonuses(ItemPrototype const *proto,uint8 slot,bool apply); + void _ApplyAllLevelScaleItemMods(bool apply); + void _ApplyItemBonuses(ItemPrototype const *proto,uint8 slot,bool apply, bool only_level_scale = false); void _ApplyAmmoBonuses(); bool EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot); void ToggleMetaGemsActive(uint8 exceptslot, bool apply); @@ -1765,8 +1828,9 @@ class TRINITY_DLL_SPEC Player : public Unit void ApplyItemEquipSpell(Item *item, bool apply, bool form_change = false); void ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply, bool form_change = false); void UpdateEquipSpellsAtFormChange(); - void CastItemCombatSpell(Item *item, CalcDamageInfo *damageInfo, ItemPrototype const * proto); + void CastItemCombatSpell(Unit *target, WeaponAttackType attType, uint32 procVictim, uint32 procEx); void CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 cast_count, uint32 glyphIndex); + void CastItemCombatSpell(Unit *target, WeaponAttackType attType, uint32 procVictim, uint32 procEx, Item *item, ItemPrototype const * proto); void SendEquipmentSetList(); void SetEquipmentSet(uint32 index, EquipmentSet eqset); @@ -1942,7 +2006,7 @@ class TRINITY_DLL_SPEC Player : public Unit } void HandleFall(MovementInfo const& movementInfo); - bool IsAllowUseFlyMountsHere() const; + bool IsKnowHowFlyIn(uint32 mapid, uint32 zone) const; void SetClientControl(Unit* target, uint8 allowMove); @@ -1970,7 +2034,7 @@ class TRINITY_DLL_SPEC Player : public Unit float m_homebindX; float m_homebindY; float m_homebindZ; - void RelocateToHomebind() { SetMapId(m_homebindMapId); Relocate(m_homebindX,m_homebindY,m_homebindZ); } + void RelocateToHomebind(uint32 & newMap) { newMap = m_homebindMapId; Relocate(m_homebindX,m_homebindY,m_homebindZ); } // currently visible objects at player client typedef std::set<uint64> ClientGUIDs; @@ -1989,15 +2053,13 @@ class TRINITY_DLL_SPEC Player : public Unit void UpdateVisibilityOf(T* target, UpdateData& data, std::set<WorldObject*>& visibleNow); // Stealth detection system - uint32 m_DetectInvTimer; void HandleStealthedUnitsDetection(); uint8 m_forced_speed_changes[MAX_MOVE_TYPE]; bool HasAtLoginFlag(AtLoginFlags f) const { return m_atLoginFlags & f; } void SetAtLoginFlag(AtLoginFlags f) { m_atLoginFlags |= f; } - uint32 GetAtLoginFlag() { return m_atLoginFlags; } - void SetPetAtLoginFlag(uint8 f) { m_atLoginFlags |= uint32(f<<AT_LOAD_PET_FLAGS); } + void RemoveAtLoginFlag(AtLoginFlags f, bool in_db_also = false); LookingForGroup m_lookingForGroup; @@ -2025,6 +2087,7 @@ class TRINITY_DLL_SPEC Player : public Unit BoundInstancesMap m_boundInstances[TOTAL_DIFFICULTIES]; InstancePlayerBind* GetBoundInstance(uint32 mapid, uint8 difficulty); BoundInstancesMap& GetBoundInstances(uint8 difficulty) { return m_boundInstances[difficulty]; } + InstanceSave * GetInstanceSave(uint32 mapid); void UnbindInstance(uint32 mapid, uint8 difficulty, bool unload = false); void UnbindInstance(BoundInstancesMap::iterator &itr, uint8 difficulty, bool unload = false); InstancePlayerBind* BindToInstance(InstanceSave *save, bool permanent, bool load = false); @@ -2061,6 +2124,10 @@ class TRINITY_DLL_SPEC Player : public Unit GridReference<Player> &GetGridRef() { return m_gridRef; } MapReference &GetMapRef() { return m_mapRef; } + // Set map to player and add reference + void SetMap(Map * map); + void ResetMap(); + bool isAllowedToLoot(Creature* creature); DeclinedName const* GetDeclinedNames() const { return m_declinedname; } @@ -2068,6 +2135,8 @@ class TRINITY_DLL_SPEC Player : public Unit uint8 GetBaseRune(uint8 index) const { return m_runes->runes[index].BaseRune; } uint8 GetCurrentRune(uint8 index) const { return m_runes->runes[index].CurrentRune; } uint8 GetRuneCooldown(uint8 index) const { return m_runes->runes[index].Cooldown; } + RuneType GetLastUsedRune() { return m_runes->lastUsedRune; } + void SetLastUsedRune(RuneType type) { m_runes->lastUsedRune = type; } void SetBaseRune(uint8 index, uint8 baseRune) { m_runes->runes[index].BaseRune = baseRune; } void SetCurrentRune(uint8 index, uint8 currentRune) { m_runes->runes[index].CurrentRune = currentRune; } void SetRuneCooldown(uint8 index, uint8 cooldown) { m_runes->runes[index].Cooldown = cooldown; m_runes->SetRuneState(index, (cooldown == 0) ? true : false); } @@ -2075,11 +2144,14 @@ class TRINITY_DLL_SPEC Player : public Unit void ResyncRunes(uint8 count); void AddRunePower(uint8 index); void InitRunes(); + AchievementMgr& GetAchievementMgr() { return m_achievementMgr; } - void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0, Unit *unit=NULL, uint32 time=0); + void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1 = 0, uint32 miscvalue2 = 0, Unit *unit = NULL, uint32 time = 0); + void CompletedAchievement(AchievementEntry const* entry); + bool HasTitle(uint32 bitIndex); bool HasTitle(CharTitlesEntry const* title) { return HasTitle(title->bit_index); } - void SetTitle(CharTitlesEntry const* title); + void SetTitle(CharTitlesEntry const* title, bool lost = false); //bool isActiveObject() const { return true; } bool canSeeSpellClickOn(Creature const* creature) const; @@ -2175,8 +2247,6 @@ class TRINITY_DLL_SPEC Player : public Unit void outDebugValues() const; uint64 m_lootGuid; - uint32 m_race; - uint32 m_class; uint32 m_team; uint32 m_nextSave; time_t m_speakTime; @@ -2313,6 +2383,13 @@ class TRINITY_DLL_SPEC Player : public Unit int32 CalculateReputationGain(uint32 creatureOrQuestLevel, int32 rep, int32 faction, bool for_quest); void AdjustQuestReqItemCount( Quest const* pQuest, QuestStatusData& questStatusData ); + bool IsCanDelayTeleport() const { return m_bCanDelayTeleport; } + void SetCanDelayTeleport(bool setting) { m_bCanDelayTeleport = setting; } + bool IsHasDelayedTeleport() const { return m_bHasDelayedTeleport; } + void SetDelayedTeleportFlag(bool setting) { m_bHasDelayedTeleport = setting; } + + void ScheduleDelayedOperation(uint32 operation); + GridReference<Player> m_gridRef; MapReference m_mapRef; @@ -2329,9 +2406,16 @@ class TRINITY_DLL_SPEC Player : public Unit // Current teleport data WorldLocation m_teleport_dest; + uint32 m_teleport_options; bool mSemaphoreTeleport_Near; bool mSemaphoreTeleport_Far; + uint32 m_DelayedOperations; + bool m_bCanDelayTeleport; + bool m_bHasDelayedTeleport; + + uint32 m_DetectInvTimer; + // Temporary removed pet cache uint32 m_temporaryUnsummonedPetNumber; uint32 m_oldpetspell; diff --git a/src/game/PlayerDump.cpp b/src/game/PlayerDump.cpp index 73939115211..06550269856 100644 --- a/src/game/PlayerDump.cpp +++ b/src/game/PlayerDump.cpp @@ -304,19 +304,19 @@ void PlayerDumpWriter::DumpTable(std::string& dump, uint32 guid, char const*tabl // collect guids switch ( type ) { - case DTT_INVENTORY: - StoreGUID(result,3,items); break; // item guid collection - case DTT_ITEM: - StoreGUID(result,0,ITEM_FIELD_ITEM_TEXT_ID,texts); break; - // item text id collection - case DTT_PET: - StoreGUID(result,0,pets); break; // pet guid collection - case DTT_MAIL: - StoreGUID(result,0,mails); // mail id collection - StoreGUID(result,6,texts); break; // item text id collection - case DTT_MAIL_ITEM: - StoreGUID(result,1,items); break; // item guid collection - default: break; + case DTT_INVENTORY: + StoreGUID(result,3,items); break; // item guid collection + case DTT_ITEM: + StoreGUID(result,0,ITEM_FIELD_ITEM_TEXT_ID,texts); break; + // item text id collection + case DTT_PET: + StoreGUID(result,0,pets); break; // pet guid collection + case DTT_MAIL: + StoreGUID(result,0,mails); // mail id collection + StoreGUID(result,7,texts); break; // item text id collection + case DTT_MAIL_ITEM: + StoreGUID(result,1,items); break; // item guid collection + default: break; } dump += CreateDumpString(tableTo, result); @@ -332,7 +332,7 @@ void PlayerDumpWriter::DumpTable(std::string& dump, uint32 guid, char const*tabl std::string PlayerDumpWriter::GetDump(uint32 guid) { std::string dump; - + dump += "IMPORTANT NOTE: This sql queries not created for apply directly, use '.pdump load' command in console or client chat instead.\n"; dump += "IMPORTANT NOTE: NOT APPLY ITS DIRECTLY to character DB or you will DAMAGE and CORRUPT character DB\n\n"; @@ -395,7 +395,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s { QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", account); uint8 charcount = 0; - if ( result ) + if (result) { Field *fields=result->Fetch(); charcount = fields[0].GetUInt8(); @@ -407,7 +407,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s } FILE *fin = fopen(file.c_str(), "r"); - if(!fin) + if (!fin) return DUMP_FILE_OPEN_ERROR; QueryResult * result = NULL; @@ -415,7 +415,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s // make sure the same guid doesn't already exist and is safe to use bool incHighest = true; - if(guid != 0 && guid < objmgr.m_hiCharGuid) + if (guid != 0 && guid < objmgr.m_hiCharGuid) { result = CharacterDatabase.PQuery("SELECT * FROM characters WHERE guid = '%d'", guid); if (result) @@ -429,10 +429,10 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s guid = objmgr.m_hiCharGuid; // normalize the name if specified and check if it exists - if(!normalizePlayerName(name)) + if (!normalizePlayerName(name)) name = ""; - if(ObjectMgr::IsValidName(name,true)) + if (ObjectMgr::CheckPlayerName(name,true) == CHAR_NAME_SUCCESS) { CharacterDatabase.escape_string(name); // for safe, we use name only for sql quearies anyway result = CharacterDatabase.PQuery("SELECT * FROM characters WHERE name = '%s'", name.c_str()); @@ -442,7 +442,8 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s delete result; } } - else name = ""; + else + name = ""; // name encoded or empty @@ -453,6 +454,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s std::map<uint32,uint32> items; std::map<uint32,uint32> mails; + std::map<uint32,uint32> itemTexts; char buf[32000] = ""; typedef std::map<uint32, uint32> PetIds; // old->new petid relation @@ -547,8 +549,8 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s if (result) { delete result; - // rename on login: `at_login` field 30 in raw field list - if(!changenth(line, 30, "1")) + + if(!changenth(line, 37, "1")) // rename on login: `at_login` field 37 in raw field list ROLLBACK(DUMP_FILE_BROKEN); } } @@ -581,6 +583,8 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s ROLLBACK(DUMP_FILE_BROKEN); if(!changetoknth(vals, ITEM_FIELD_OWNER+1, newguid)) ROLLBACK(DUMP_FILE_BROKEN); + if(!changetokGuid(vals, ITEM_FIELD_ITEM_TEXT_ID+1, itemTexts, objmgr.m_ItemTextId,true)) + ROLLBACK(DUMP_FILE_BROKEN); if(!changenth(line, 3, vals.c_str())) ROLLBACK(DUMP_FILE_BROKEN); break; @@ -638,10 +642,12 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s } case DTT_MAIL: // mail { - // id,messageType,stationery,sender,receiver + // id,messageType,stationery,mailtemplate,sender,receiver,subject,itemText if(!changeGuid(line, 1, mails, objmgr.m_mailid)) ROLLBACK(DUMP_FILE_BROKEN); - if(!changenth(line, 5, newguid)) + if(!changenth(line, 6, newguid)) + ROLLBACK(DUMP_FILE_BROKEN); + if(!changeGuid(line, 8, itemTexts, objmgr.m_ItemTextId)) ROLLBACK(DUMP_FILE_BROKEN); break; } @@ -656,6 +662,18 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s ROLLBACK(DUMP_FILE_BROKEN); break; } + case DTT_ITEM_TEXT: // item_text + { + // id + if(!changeGuid(line, 1, itemTexts, objmgr.m_ItemTextId)) + ROLLBACK(DUMP_FILE_BROKEN); + + // add it to cache + uint32 id= atoi(getnth(line,1).c_str()); + std::string text = getnth(line,2); + objmgr.AddItemText(id,text); + break; + } default: sLog.outError("Unknown dump table type: %u",type); break; @@ -669,6 +687,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s objmgr.m_hiItemGuid += items.size(); objmgr.m_mailid += mails.size(); + objmgr.m_ItemTextId += itemTexts.size(); if(incHighest) ++objmgr.m_hiCharGuid; diff --git a/src/game/PointMovementGenerator.cpp b/src/game/PointMovementGenerator.cpp index ed057854aaa..ffd036e9c21 100644 --- a/src/game/PointMovementGenerator.cpp +++ b/src/game/PointMovementGenerator.cpp @@ -30,9 +30,9 @@ template<class T> void PointMovementGenerator<T>::Initialize(T &unit) { unit.StopMoving(); - unit.clearUnitState(UNIT_STAT_MOVING); Traveller<T> traveller(unit); - i_destinationHolder.SetDestination(traveller,i_x,i_y,i_z, !unit.hasUnitState(UNIT_STAT_JUMPING)); + // knockback effect has UNIT_STAT_JUMPING set,so if here we disable sentmonstermove there will be creature position sync problem between client and server + i_destinationHolder.SetDestination(traveller,i_x,i_y,i_z, true /* !unit.hasUnitState(UNIT_STAT_JUMPING)*/); if (unit.GetTypeId() == TYPEID_UNIT && ((Creature*)&unit)->canFly()) unit.AddUnitMovementFlag(MOVEMENTFLAG_FLYING); diff --git a/src/game/PoolHandler.cpp b/src/game/PoolHandler.cpp index 09a1d4293b7..dd0834b80f4 100644 --- a/src/game/PoolHandler.cpp +++ b/src/game/PoolHandler.cpp @@ -127,10 +127,7 @@ void PoolGroup<Creature>::Despawn1Object(uint32 guid) objmgr.RemoveCreatureFromGrid(guid, data); if (Creature* pCreature = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(guid, data->id, HIGHGUID_UNIT), (Creature*)NULL)) - { - pCreature->CleanupsBeforeDelete(); pCreature->AddObjectToRemoveList(); - } } } diff --git a/src/game/QueryHandler.cpp b/src/game/QueryHandler.cpp index 9da24d404f9..b70a85c58e2 100644 --- a/src/game/QueryHandler.cpp +++ b/src/game/QueryHandler.cpp @@ -65,17 +65,17 @@ void WorldSession::SendNameQueryOpcodeFromDB(uint64 guid) CharacterDatabase.AsyncPQuery(&WorldSession::SendNameQueryOpcodeFromDBCallBack, GetAccountId(), !sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) ? // ------- Query Without Declined Names -------- - // 0 1 2 - "SELECT guid, name, SUBSTRING(data, LENGTH(SUBSTRING_INDEX(data, ' ', '%u'))+2, LENGTH(SUBSTRING_INDEX(data, ' ', '%u')) - LENGTH(SUBSTRING_INDEX(data, ' ', '%u'))-1) " + // 0 1 2 3 4 + "SELECT guid, name, race, gender, class " "FROM characters WHERE guid = '%u'" : // --------- Query With Declined Names --------- - // 0 1 2 - "SELECT characters.guid, name, SUBSTRING(data, LENGTH(SUBSTRING_INDEX(data, ' ', '%u'))+2, LENGTH(SUBSTRING_INDEX(data, ' ', '%u')) - LENGTH(SUBSTRING_INDEX(data, ' ', '%u'))-1), " - // 3 4 5 6 7 + // 0 1 2 3 4 + "SELECT characters.guid, name, race, gender, class, " + // 5 6 7 8 9 "genitive, dative, accusative, instrumental, prepositional " "FROM characters LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid WHERE characters.guid = '%u'", - UNIT_FIELD_BYTES_0, UNIT_FIELD_BYTES_0+1, UNIT_FIELD_BYTES_0, GUID_LOPART(guid)); + GUID_LOPART(guid)); } void WorldSession::SendNameQueryOpcodeFromDBCallBack(QueryResult *result, uint32 accountId) @@ -93,31 +93,35 @@ void WorldSession::SendNameQueryOpcodeFromDBCallBack(QueryResult *result, uint32 Field *fields = result->Fetch(); uint32 guid = fields[0].GetUInt32(); std::string name = fields[1].GetCppString(); - uint32 field = 0; + uint8 pRace = 0, pGender = 0, pClass = 0; if(name == "") name = session->GetTrinityString(LANG_NON_EXIST_CHARACTER); else - field = fields[2].GetUInt32(); + { + pRace = fields[2].GetUInt8(); + pGender = fields[3].GetUInt8(); + pClass = fields[4].GetUInt8(); + } // guess size WorldPacket data( SMSG_NAME_QUERY_RESPONSE, (8+1+1+1+1+1+1+10) ); data.appendPackGUID(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER)); data << uint8(0); // added in 3.1 data << name; - data << uint8(0); - data << uint8(field & 0xFF); - data << uint8((field >> 16) & 0xFF); - data << uint8((field >> 8) & 0xFF); + data << uint8(0); // realm name for cross realm BG usage + data << uint8(pRace); // race + data << uint8(pGender); // gender + data << uint8(pClass); // class - // if the first declined name field (3) is empty, the rest must be too - if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) && fields[3].GetCppString() != "") + // if the first declined name field (5) is empty, the rest must be too + if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) && fields[5].GetCppString() != "") { data << uint8(1); // is declined - for(int i = 3; i < MAX_DECLINED_NAME_CASES+3; ++i) + for(int i = 5; i < MAX_DECLINED_NAME_CASES+5; ++i) data << fields[i].GetCppString(); } else - data << uint8(0); // is declined + data << uint8(0); // is not declined session->SendPacket( &data ); delete result; @@ -187,8 +191,8 @@ void WorldSession::HandleCreatureQueryOpcode( WorldPacket & recv_data ) data << uint32(ci->type); // CreatureType.dbc data << uint32(ci->family); // CreatureFamily.dbc data << uint32(ci->rank); // Creature Rank (elite, boss, etc) - data << uint32(ci->unk1); // new in 3.1, creature entry? - data << uint32(ci->unk2); // new in 3.1, creature entry? + data << uint32(ci->KillCredit[0]); // new in 3.1, kill credit + data << uint32(ci->KillCredit[1]); // new in 3.1, kill credit data << uint32(ci->DisplayID_A[0]); // modelid_male1 data << uint32(ci->DisplayID_H[0]); // modelid_female1 ? data << uint32(ci->DisplayID_A[1]); // modelid_male2 ? diff --git a/src/game/QuestDef.h b/src/game/QuestDef.h index c3285ec816c..412c66cf9fd 100644 --- a/src/game/QuestDef.h +++ b/src/game/QuestDef.h @@ -96,9 +96,9 @@ enum QuestStatus { QUEST_STATUS_NONE = 0, QUEST_STATUS_COMPLETE = 1, - QUEST_STATUS_UNAVAILABLE = 2, + //QUEST_STATUS_UNAVAILABLE = 2, QUEST_STATUS_INCOMPLETE = 3, - QUEST_STATUS_AVAILABLE = 4, + //QUEST_STATUS_AVAILABLE = 4, MAX_QUEST_STATUS }; diff --git a/src/game/QuestHandler.cpp b/src/game/QuestHandler.cpp index b2478860c61..52d2ee6d77a 100644 --- a/src/game/QuestHandler.cpp +++ b/src/game/QuestHandler.cpp @@ -439,60 +439,56 @@ void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket) { CHECK_PACKET_SIZE(recvPacket,4); - uint32 quest; - recvPacket >> quest; + uint32 questId; + recvPacket >> questId; - sLog.outDebug( "WORLD: Received CMSG_PUSHQUESTTOPARTY quest = %u", quest ); + sLog.outDebug("WORLD: Received CMSG_PUSHQUESTTOPARTY quest = %u", questId); - Quest const *pQuest = objmgr.GetQuestTemplate(quest); - if( pQuest ) + if (Quest const *pQuest = objmgr.GetQuestTemplate(questId)) { - if( _player->GetGroup() ) + if (Group* pGroup = _player->GetGroup()) { - Group *pGroup = _player->GetGroup(); - if( pGroup ) + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) { - for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *pPlayer = itr->getSource(); - if (!pPlayer || pPlayer == _player) // skip self - continue; + Player *pPlayer = itr->getSource(); - _player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_SHARING_QUEST); + if (!pPlayer || pPlayer == _player) // skip self + continue; - if( !pPlayer->SatisfyQuestStatus( pQuest, false ) ) - { - _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_HAVE_QUEST ); - continue; - } + _player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_SHARING_QUEST); - if( pPlayer->GetQuestStatus( quest ) == QUEST_STATUS_COMPLETE ) - { - _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_FINISH_QUEST ); - continue; - } + if (!pPlayer->SatisfyQuestStatus(pQuest, false)) + { + _player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_HAVE_QUEST); + continue; + } - if( !pPlayer->CanTakeQuest( pQuest, false ) ) - { - _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_CANT_TAKE_QUEST ); - continue; - } + if (pPlayer->GetQuestStatus(questId) == QUEST_STATUS_COMPLETE) + { + _player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_FINISH_QUEST); + continue; + } - if( !pPlayer->SatisfyQuestLog( false ) ) - { - _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_LOG_FULL ); - continue; - } + if (!pPlayer->CanTakeQuest(pQuest, false)) + { + _player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_CANT_TAKE_QUEST); + continue; + } - if( pPlayer->GetDivider() != 0 ) - { - _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_BUSY ); - continue; - } + if (!pPlayer->SatisfyQuestLog(false)) + { + _player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_LOG_FULL); + continue; + } - pPlayer->PlayerTalkClass->SendQuestGiverQuestDetails( pQuest, _player->GetGUID(), true ); - pPlayer->SetDivider( _player->GetGUID() ); + if (pPlayer->GetDivider() != 0) + { + _player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_BUSY); + continue; } + + pPlayer->PlayerTalkClass->SendQuestGiverQuestDetails(pQuest, _player->GetGUID(), true); + pPlayer->SetDivider(_player->GetGUID()); } } } diff --git a/src/game/ReputationMgr.cpp b/src/game/ReputationMgr.cpp index ee7ca8d44a4..e4ed5b0db2e 100644 --- a/src/game/ReputationMgr.cpp +++ b/src/game/ReputationMgr.cpp @@ -255,7 +255,7 @@ bool ReputationMgr::SetOneFactionReputation(FactionEntry const* factionEntry, in if(incremental) { // int32 *= float cause one point loss? - standing = (int32)((float)standing * sWorld.getRate(RATE_REPUTATION_GAIN)); + standing = floor( (float)standing * sWorld.getRate(RATE_REPUTATION_GAIN) + 0.5 ); standing += itr->second.Standing + BaseRep; } @@ -475,4 +475,4 @@ void ReputationMgr::UpdateRankCounters( ReputationRank old_rank, ReputationRank ++m_reveredFactionCount; if(new_rank >= REP_HONORED) ++m_honoredFactionCount; -}
\ No newline at end of file +} diff --git a/src/game/ReputationMgr.h b/src/game/ReputationMgr.h index b81634119df..d63c518eb82 100644 --- a/src/game/ReputationMgr.h +++ b/src/game/ReputationMgr.h @@ -21,9 +21,16 @@ #include "Common.h" #include "SharedDefines.h" +#include "Language.h" #include "DBCStructure.h" #include <map> +static uint32 ReputationRankStrIndex[MAX_REPUTATION_RANK] = +{ + LANG_REP_HATED, LANG_REP_HOSTILE, LANG_REP_UNFRIENDLY, LANG_REP_NEUTRAL, + LANG_REP_FRIENDLY, LANG_REP_HONORED, LANG_REP_REVERED, LANG_REP_EXALTED +}; + enum FactionFlags { FACTION_FLAG_VISIBLE = 0x01, // makes visible in client (set or can be set at interaction with target of this faction) diff --git a/src/game/ScriptCalls.cpp b/src/game/ScriptCalls.cpp index fec1afb08ae..b8870c2a1dd 100644 --- a/src/game/ScriptCalls.cpp +++ b/src/game/ScriptCalls.cpp @@ -79,6 +79,7 @@ bool LoadScriptingModule(char const* libName) ||!(testScript->ItemQuestAccept =(scriptCallItemQuestAccept )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"ItemQuestAccept" )) ||!(testScript->GOQuestAccept =(scriptCallGOQuestAccept )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"GOQuestAccept" )) ||!(testScript->ItemUse =(scriptCallItemUse )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"ItemUse" )) + ||!(testScript->ItemExpire =(scriptCallItemExpire )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"ItemExpire" )) ||!(testScript->EffectDummyGameObj =(scriptCallEffectDummyGameObj )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"EffectDummyGameObj" )) ||!(testScript->EffectDummyCreature =(scriptCallEffectDummyCreature )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"EffectDummyCreature" )) ||!(testScript->EffectDummyItem =(scriptCallEffectDummyItem )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"EffectDummyItem" )) diff --git a/src/game/ScriptCalls.h b/src/game/ScriptCalls.h index 4e23f9f576d..0eae69cb505 100644 --- a/src/game/ScriptCalls.h +++ b/src/game/ScriptCalls.h @@ -59,6 +59,7 @@ typedef bool(TRINITY_IMPORT * scriptCallItemQuestAccept)(Player *player, Item *, typedef bool(TRINITY_IMPORT * scriptCallGOQuestAccept)(Player *player, GameObject *, Quest const*); typedef bool(TRINITY_IMPORT * scriptCallGOChooseReward)(Player *player, GameObject *, Quest const*, uint32 opt ); typedef bool(TRINITY_IMPORT * scriptCallItemUse) (Player *player, Item *_Item, SpellCastTargets const& targets); +typedef bool(TRINITY_IMPORT * scriptCallItemExpire) (Player *player, ItemPrototype const *_ItemProto); typedef bool(TRINITY_IMPORT * scriptCallEffectDummyGameObj) (Unit *caster, uint32 spellId, uint32 effIndex, GameObject *gameObjTarget); typedef bool(TRINITY_IMPORT * scriptCallEffectDummyCreature) (Unit *caster, uint32 spellId, uint32 effIndex, Creature *crTarget); typedef bool(TRINITY_IMPORT * scriptCallEffectDummyItem) (Unit *caster, uint32 spellId, uint32 effIndex, Item *itemTarget); @@ -89,6 +90,7 @@ typedef struct scriptCallItemQuestAccept ItemQuestAccept; scriptCallGOQuestAccept GOQuestAccept; scriptCallItemUse ItemUse; + scriptCallItemExpire ItemExpire; scriptCallEffectDummyGameObj EffectDummyGameObj; scriptCallEffectDummyCreature EffectDummyCreature; scriptCallEffectDummyItem EffectDummyItem; diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index e958f911440..987eea769f5 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -249,8 +249,9 @@ enum SpellCategory #define SPELL_ATTR_CASTABLE_WHILE_SITTING 0x08000000 // 27 castable while sitting #define SPELL_ATTR_CANT_USED_IN_COMBAT 0x10000000 // 28 Cannot be used in combat #define SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY 0x20000000 // 29 unaffected by invulnerability (hmm possible not...) -#define SPELL_ATTR_BREAKABLE_BY_DAMAGE 0x40000000 // 30 breakable by damage? +#define SPELL_ATTR_BREAKABLE_BY_DAMAGE 0x40000000 // 30 #define SPELL_ATTR_CANT_CANCEL 0x80000000 // 31 positive aura can't be canceled + #define SPELL_ATTR_EX_DISMISS_PET 0x00000001 // 0 dismiss pet and not allow to summon new one? #define SPELL_ATTR_EX_DRAIN_ALL_POWER 0x00000002 // 1 use all power (Only paladin Lay of Hands and Bunyanize) #define SPELL_ATTR_EX_CHANNELED_1 0x00000004 // 2 channeled target @@ -270,7 +271,7 @@ enum SpellCategory #define SPELL_ATTR_EX_UNK16 0x00010000 // 16 on immuniy #define SPELL_ATTR_EX_UNAUTOCASTABLE_BY_PET 0x00020000 // 17 #define SPELL_ATTR_EX_UNK18 0x00040000 // 18 -#define SPELL_ATTR_EX_UNK19 0x00080000 // 19 +#define SPELL_ATTR_EX_CANT_TARGET_SELF 0x00080000 // 19 Applies only to unit target - for example Divine Intervention (19752) #define SPELL_ATTR_EX_REQ_COMBO_POINTS1 0x00100000 // 20 Req combo points on target #define SPELL_ATTR_EX_UNK21 0x00200000 // 21 #define SPELL_ATTR_EX_REQ_COMBO_POINTS2 0x00400000 // 22 Req combo points on target @@ -320,11 +321,11 @@ enum SpellCategory #define SPELL_ATTR_EX3_UNK0 0x00000001 // 0 #define SPELL_ATTR_EX3_UNK1 0x00000002 // 1 #define SPELL_ATTR_EX3_UNK2 0x00000004 // 2 -#define SPELL_ATTR_EX3_UNK3 0x00000008 // 3 +#define SPELL_ATTR_EX3_BLOCKABLE_SPELL 0x00000008 // 3 Only dmg class melee in 3.1.3 #define SPELL_ATTR_EX3_UNK4 0x00000010 // 4 Druid Rebirth only this spell have this flag #define SPELL_ATTR_EX3_UNK5 0x00000020 // 5 #define SPELL_ATTR_EX3_UNK6 0x00000040 // 6 -#define SPELL_ATTR_EX3_UNK7 0x00000080 // 7 separate stack for every caster +#define SPELL_ATTR_EX3_STACKS_FOR_DIFFERENT_CASTERS 0x00000080 // 7 separate stack for every caster #define SPELL_ATTR_EX3_PLAYERS_ONLY 0x00000100 // 8 Player only? #define SPELL_ATTR_EX3_TRIGGERED_CAN_TRIGGER_2 0x00000200 // 9 triggered from effect? #define SPELL_ATTR_EX3_MAIN_HAND 0x00000400 // 10 Main hand weapon required @@ -336,7 +337,7 @@ enum SpellCategory #define SPELL_ATTR_EX3_UNK16 0x00010000 // 16 no triggers effects that trigger on casting a spell?? #define SPELL_ATTR_EX3_NO_INITIAL_AGGRO 0x00020000 // 17 Soothe Animal, 39758, Mind Soothe #define SPELL_ATTR_EX3_UNK18 0x00040000 // 18 -#define SPELL_ATTR_EX3_UNK19 0x00080000 // 19 spells triggered by spell with this flag can't proc caster auras and can proc from triggered (swings too - 20178) +#define SPELL_ATTR_EX3_DISABLE_PROC 0x00080000 // 19 during aura proc no spells can trigger (20178, 20375) #define SPELL_ATTR_EX3_DEATH_PERSISTENT 0x00100000 // 20 Death persistent spells #define SPELL_ATTR_EX3_UNK21 0x00200000 // 21 #define SPELL_ATTR_EX3_REQ_WAND 0x00400000 // 22 Req wand @@ -358,7 +359,7 @@ enum SpellCategory #define SPELL_ATTR_EX4_UNK5 0x00000020 // 5 #define SPELL_ATTR_EX4_NOT_STEALABLE 0x00000040 // 6 although such auras might be dispellable, they cannot be stolen #define SPELL_ATTR_EX4_UNK7 0x00000080 // 7 -#define SPELL_ATTR_EX4_UNK8 0x00000100 // 8 +#define SPELL_ATTR_EX4_FIXED_DAMAGE 0x00000100 // 8 decimate, share damage? #define SPELL_ATTR_EX4_UNK9 0x00000200 // 9 #define SPELL_ATTR_EX4_SPELL_VS_EXTEND_COST 0x00000400 // 10 Rogue Shiv have this flag #define SPELL_ATTR_EX4_UNK11 0x00000800 // 11 @@ -593,7 +594,7 @@ enum SpellEffects SPELL_EFFECT_SUMMON_PET = 56, SPELL_EFFECT_LEARN_PET_SPELL = 57, SPELL_EFFECT_WEAPON_DAMAGE = 58, - SPELL_EFFECT_OPEN_LOCK_ITEM = 59, + SPELL_EFFECT_CREATE_RANDOM_ITEM = 59, SPELL_EFFECT_PROFICIENCY = 60, SPELL_EFFECT_SEND_EVENT = 61, SPELL_EFFECT_POWER_BURN = 62, @@ -666,10 +667,10 @@ enum SpellEffects SPELL_EFFECT_APPLY_AREA_AURA_ENEMY = 129, SPELL_EFFECT_REDIRECT_THREAT = 130, SPELL_EFFECT_131 = 131, - SPELL_EFFECT_132 = 132, + SPELL_EFFECT_PLAY_MUSIC = 132, SPELL_EFFECT_UNLEARN_SPECIALIZATION = 133, SPELL_EFFECT_KILL_CREDIT2 = 134, - SPELL_EFFECT_135 = 135, + SPELL_EFFECT_CALL_PET = 135, SPELL_EFFECT_HEAL_PCT = 136, SPELL_EFFECT_ENERGIZE_PCT = 137, SPELL_EFFECT_138 = 138, @@ -702,196 +703,201 @@ enum SpellEffects enum SpellCastResult { - SPELL_FAILED_AFFECTING_COMBAT = 0, - SPELL_FAILED_ALREADY_AT_FULL_HEALTH = 1, - SPELL_FAILED_ALREADY_AT_FULL_MANA = 2, - SPELL_FAILED_ALREADY_AT_FULL_POWER = 3, - SPELL_FAILED_ALREADY_BEING_TAMED = 4, - SPELL_FAILED_ALREADY_HAVE_CHARM = 5, - SPELL_FAILED_ALREADY_HAVE_SUMMON = 6, - SPELL_FAILED_ALREADY_OPEN = 7, - SPELL_FAILED_AURA_BOUNCED = 8, - SPELL_FAILED_AUTOTRACK_INTERRUPTED = 9, - SPELL_FAILED_BAD_IMPLICIT_TARGETS = 10, - SPELL_FAILED_BAD_TARGETS = 11, - SPELL_FAILED_CANT_BE_CHARMED = 12, - SPELL_FAILED_CANT_BE_DISENCHANTED = 13, - SPELL_FAILED_CANT_BE_DISENCHANTED_SKILL = 14, - SPELL_FAILED_CANT_BE_MILLED = 15, - SPELL_FAILED_CANT_BE_PROSPECTED = 16, - SPELL_FAILED_CANT_CAST_ON_TAPPED = 17, - SPELL_FAILED_CANT_DUEL_WHILE_INVISIBLE = 18, - SPELL_FAILED_CANT_DUEL_WHILE_STEALTHED = 19, - SPELL_FAILED_CANT_STEALTH = 20, - SPELL_FAILED_CASTER_AURASTATE = 21, - SPELL_FAILED_CASTER_DEAD = 22, - SPELL_FAILED_CHARMED = 23, - SPELL_FAILED_CHEST_IN_USE = 24, - SPELL_FAILED_CONFUSED = 25, - SPELL_FAILED_DONT_REPORT = 26, - SPELL_FAILED_EQUIPPED_ITEM = 27, - SPELL_FAILED_EQUIPPED_ITEM_CLASS = 28, - SPELL_FAILED_EQUIPPED_ITEM_CLASS_MAINHAND = 29, - SPELL_FAILED_EQUIPPED_ITEM_CLASS_OFFHAND = 30, - SPELL_FAILED_ERROR = 31, - SPELL_FAILED_FIZZLE = 32, - SPELL_FAILED_FLEEING = 33, - SPELL_FAILED_FOOD_LOWLEVEL = 34, - SPELL_FAILED_HIGHLEVEL = 35, - SPELL_FAILED_HUNGER_SATIATED = 36, - SPELL_FAILED_IMMUNE = 37, - SPELL_FAILED_INCORRECT_AREA = 38, - SPELL_FAILED_INTERRUPTED = 39, - SPELL_FAILED_INTERRUPTED_COMBAT = 40, - SPELL_FAILED_ITEM_ALREADY_ENCHANTED = 41, - SPELL_FAILED_ITEM_GONE = 42, - SPELL_FAILED_ITEM_NOT_FOUND = 43, - SPELL_FAILED_ITEM_NOT_READY = 44, - SPELL_FAILED_LEVEL_REQUIREMENT = 45, - SPELL_FAILED_LINE_OF_SIGHT = 46, - SPELL_FAILED_LOWLEVEL = 47, - SPELL_FAILED_LOW_CASTLEVEL = 48, - SPELL_FAILED_MAINHAND_EMPTY = 49, - SPELL_FAILED_MOVING = 50, - SPELL_FAILED_NEED_AMMO = 51, - SPELL_FAILED_NEED_AMMO_POUCH = 52, - SPELL_FAILED_NEED_EXOTIC_AMMO = 53, - SPELL_FAILED_NEED_MORE_ITEMS = 54, - SPELL_FAILED_NOPATH = 55, - SPELL_FAILED_NOT_BEHIND = 56, - SPELL_FAILED_NOT_FISHABLE = 57, - SPELL_FAILED_NOT_FLYING = 58, - SPELL_FAILED_NOT_HERE = 59, - SPELL_FAILED_NOT_INFRONT = 60, - SPELL_FAILED_NOT_IN_CONTROL = 61, - SPELL_FAILED_NOT_KNOWN = 62, - SPELL_FAILED_NOT_MOUNTED = 63, - SPELL_FAILED_NOT_ON_TAXI = 64, - SPELL_FAILED_NOT_ON_TRANSPORT = 65, - SPELL_FAILED_NOT_READY = 66, - SPELL_FAILED_NOT_SHAPESHIFT = 67, - SPELL_FAILED_NOT_STANDING = 68, - SPELL_FAILED_NOT_TRADEABLE = 69, - SPELL_FAILED_NOT_TRADING = 70, - SPELL_FAILED_NOT_UNSHEATHED = 71, - SPELL_FAILED_NOT_WHILE_GHOST = 72, - SPELL_FAILED_NOT_WHILE_LOOTING = 73, - SPELL_FAILED_NO_AMMO = 74, - SPELL_FAILED_NO_CHARGES_REMAIN = 75, - SPELL_FAILED_NO_CHAMPION = 76, - SPELL_FAILED_NO_COMBO_POINTS = 77, - SPELL_FAILED_NO_DUELING = 78, - SPELL_FAILED_NO_ENDURANCE = 79, - SPELL_FAILED_NO_FISH = 80, - SPELL_FAILED_NO_ITEMS_WHILE_SHAPESHIFTED = 81, - SPELL_FAILED_NO_MOUNTS_ALLOWED = 82, - SPELL_FAILED_NO_PET = 83, - SPELL_FAILED_NO_POWER = 84, - SPELL_FAILED_NOTHING_TO_DISPEL = 85, - SPELL_FAILED_NOTHING_TO_STEAL = 86, - SPELL_FAILED_ONLY_ABOVEWATER = 87, - SPELL_FAILED_ONLY_DAYTIME = 88, - SPELL_FAILED_ONLY_INDOORS = 89, - SPELL_FAILED_ONLY_MOUNTED = 90, - SPELL_FAILED_ONLY_NIGHTTIME = 91, - SPELL_FAILED_ONLY_OUTDOORS = 92, - SPELL_FAILED_ONLY_SHAPESHIFT = 93, - SPELL_FAILED_ONLY_STEALTHED = 94, - SPELL_FAILED_ONLY_UNDERWATER = 95, - SPELL_FAILED_OUT_OF_RANGE = 96, - SPELL_FAILED_PACIFIED = 97, - SPELL_FAILED_POSSESSED = 98, - SPELL_FAILED_REAGENTS = 99, - SPELL_FAILED_REQUIRES_AREA = 100, - SPELL_FAILED_REQUIRES_SPELL_FOCUS = 101, - SPELL_FAILED_ROOTED = 102, - SPELL_FAILED_SILENCED = 103, - SPELL_FAILED_SPELL_IN_PROGRESS = 104, - SPELL_FAILED_SPELL_LEARNED = 105, - SPELL_FAILED_SPELL_UNAVAILABLE = 106, - SPELL_FAILED_STUNNED = 107, - SPELL_FAILED_TARGETS_DEAD = 108, - SPELL_FAILED_TARGET_AFFECTING_COMBAT = 109, - SPELL_FAILED_TARGET_AURASTATE = 110, - SPELL_FAILED_TARGET_DUELING = 111, - SPELL_FAILED_TARGET_ENEMY = 112, - SPELL_FAILED_TARGET_ENRAGED = 113, - SPELL_FAILED_TARGET_FRIENDLY = 114, - SPELL_FAILED_TARGET_IN_COMBAT = 115, - SPELL_FAILED_TARGET_IS_PLAYER = 116, - SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED = 117, - SPELL_FAILED_TARGET_NOT_DEAD = 118, - SPELL_FAILED_TARGET_NOT_IN_PARTY = 119, - SPELL_FAILED_TARGET_NOT_LOOTED = 120, - SPELL_FAILED_TARGET_NOT_PLAYER = 121, - SPELL_FAILED_TARGET_NO_POCKETS = 122, - SPELL_FAILED_TARGET_NO_WEAPONS = 123, - SPELL_FAILED_TARGET_NO_RANGED_WEAPONS = 124, - SPELL_FAILED_TARGET_UNSKINNABLE = 125, - SPELL_FAILED_THIRST_SATIATED = 126, - SPELL_FAILED_TOO_CLOSE = 127, - SPELL_FAILED_TOO_MANY_OF_ITEM = 128, - SPELL_FAILED_TOTEM_CATEGORY = 129, - SPELL_FAILED_TOTEMS = 130, - SPELL_FAILED_TRY_AGAIN = 131, - SPELL_FAILED_UNIT_NOT_BEHIND = 132, - SPELL_FAILED_UNIT_NOT_INFRONT = 133, - SPELL_FAILED_WRONG_PET_FOOD = 134, - SPELL_FAILED_NOT_WHILE_FATIGUED = 135, - SPELL_FAILED_TARGET_NOT_IN_INSTANCE = 136, - SPELL_FAILED_NOT_WHILE_TRADING = 137, - SPELL_FAILED_TARGET_NOT_IN_RAID = 138, - SPELL_FAILED_TARGET_FREEFORALL = 139, - SPELL_FAILED_NO_EDIBLE_CORPSES = 140, - SPELL_FAILED_ONLY_BATTLEGROUNDS = 141, - SPELL_FAILED_TARGET_NOT_GHOST = 142, - SPELL_FAILED_TRANSFORM_UNUSABLE = 143, - SPELL_FAILED_WRONG_WEATHER = 144, - SPELL_FAILED_DAMAGE_IMMUNE = 145, - SPELL_FAILED_PREVENTED_BY_MECHANIC = 146, - SPELL_FAILED_PLAY_TIME = 147, - SPELL_FAILED_REPUTATION = 148, - SPELL_FAILED_MIN_SKILL = 149, - SPELL_FAILED_NOT_IN_ARENA = 150, - SPELL_FAILED_NOT_ON_SHAPESHIFT = 151, - SPELL_FAILED_NOT_ON_STEALTHED = 152, - SPELL_FAILED_NOT_ON_DAMAGE_IMMUNE = 153, - SPELL_FAILED_NOT_ON_MOUNTED = 154, - SPELL_FAILED_TOO_SHALLOW = 155, - SPELL_FAILED_TARGET_NOT_IN_SANCTUARY = 156, - SPELL_FAILED_TARGET_IS_TRIVIAL = 157, - SPELL_FAILED_BM_OR_INVISGOD = 158, - SPELL_FAILED_EXPERT_RIDING_REQUIREMENT = 159, - SPELL_FAILED_ARTISAN_RIDING_REQUIREMENT = 160, - SPELL_FAILED_NOT_IDLE = 161, - SPELL_FAILED_NOT_INACTIVE = 162, - SPELL_FAILED_PARTIAL_PLAYTIME = 163, - SPELL_FAILED_NO_PLAYTIME = 164, - SPELL_FAILED_NOT_IN_BATTLEGROUND = 165, - SPELL_FAILED_NOT_IN_RAID_INSTANCE = 166, - SPELL_FAILED_ONLY_IN_ARENA = 167, - SPELL_FAILED_TARGET_LOCKED_TO_RAID_INSTANCE = 168, - SPELL_FAILED_ON_USE_ENCHANT = 169, - SPELL_FAILED_NOT_ON_GROUND = 170, - SPELL_FAILED_CUSTOM_ERROR = 171, - SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW = 172, - SPELL_FAILED_TOO_MANY_SOCKETS = 173, - SPELL_FAILED_INVALID_GLYPH = 174, - SPELL_FAILED_UNIQUE_GLYPH = 175, - SPELL_FAILED_GLYPH_SOCKET_LOCKED = 176, - SPELL_FAILED_NO_VALID_TARGETS = 177, - SPELL_FAILED_ITEM_AT_MAX_CHARGES = 178, - SPELL_FAILED_NOT_IN_BARBERSHOP = 179, - SPELL_FAILED_FISHING_TOO_LOW = 180, - SPELL_FAILED_UNKNOWN = 181, - - SPELL_CAST_OK = 255 //custom value, don't must be send to client + SPELL_FAILED_AFFECTING_COMBAT = 0x00, + SPELL_FAILED_ALREADY_AT_FULL_HEALTH = 0x01, + SPELL_FAILED_ALREADY_AT_FULL_MANA = 0x02, + SPELL_FAILED_ALREADY_AT_FULL_POWER = 0x03, + SPELL_FAILED_ALREADY_BEING_TAMED = 0x04, + SPELL_FAILED_ALREADY_HAVE_CHARM = 0x05, + SPELL_FAILED_ALREADY_HAVE_SUMMON = 0x06, + SPELL_FAILED_ALREADY_OPEN = 0x07, + SPELL_FAILED_AURA_BOUNCED = 0x08, + SPELL_FAILED_AUTOTRACK_INTERRUPTED = 0x09, + SPELL_FAILED_BAD_IMPLICIT_TARGETS = 0x0A, + SPELL_FAILED_BAD_TARGETS = 0x0B, + SPELL_FAILED_CANT_BE_CHARMED = 0x0C, + SPELL_FAILED_CANT_BE_DISENCHANTED = 0x0D, + SPELL_FAILED_CANT_BE_DISENCHANTED_SKILL = 0x0E, + SPELL_FAILED_CANT_BE_MILLED = 0x0F, + SPELL_FAILED_CANT_BE_PROSPECTED = 0x10, + SPELL_FAILED_CANT_CAST_ON_TAPPED = 0x11, + SPELL_FAILED_CANT_DUEL_WHILE_INVISIBLE = 0x12, + SPELL_FAILED_CANT_DUEL_WHILE_STEALTHED = 0x13, + SPELL_FAILED_CANT_STEALTH = 0x14, + SPELL_FAILED_CASTER_AURASTATE = 0x15, + SPELL_FAILED_CASTER_DEAD = 0x16, + SPELL_FAILED_CHARMED = 0x17, + SPELL_FAILED_CHEST_IN_USE = 0x18, + SPELL_FAILED_CONFUSED = 0x19, + SPELL_FAILED_DONT_REPORT = 0x1A, + SPELL_FAILED_EQUIPPED_ITEM = 0x1B, + SPELL_FAILED_EQUIPPED_ITEM_CLASS = 0x1C, + SPELL_FAILED_EQUIPPED_ITEM_CLASS_MAINHAND = 0x1D, + SPELL_FAILED_EQUIPPED_ITEM_CLASS_OFFHAND = 0x1E, + SPELL_FAILED_ERROR = 0x1F, + SPELL_FAILED_FIZZLE = 0x20, + SPELL_FAILED_FLEEING = 0x21, + SPELL_FAILED_FOOD_LOWLEVEL = 0x22, + SPELL_FAILED_HIGHLEVEL = 0x23, + SPELL_FAILED_HUNGER_SATIATED = 0x24, + SPELL_FAILED_IMMUNE = 0x25, + SPELL_FAILED_INCORRECT_AREA = 0x26, + SPELL_FAILED_INTERRUPTED = 0x27, + SPELL_FAILED_INTERRUPTED_COMBAT = 0x28, + SPELL_FAILED_ITEM_ALREADY_ENCHANTED = 0x29, + SPELL_FAILED_ITEM_GONE = 0x2A, + SPELL_FAILED_ITEM_NOT_FOUND = 0x2B, + SPELL_FAILED_ITEM_NOT_READY = 0x2C, + SPELL_FAILED_LEVEL_REQUIREMENT = 0x2D, + SPELL_FAILED_LINE_OF_SIGHT = 0x2E, + SPELL_FAILED_LOWLEVEL = 0x2F, + SPELL_FAILED_LOW_CASTLEVEL = 0x30, + SPELL_FAILED_MAINHAND_EMPTY = 0x31, + SPELL_FAILED_MOVING = 0x32, + SPELL_FAILED_NEED_AMMO = 0x33, + SPELL_FAILED_NEED_AMMO_POUCH = 0x34, + SPELL_FAILED_NEED_EXOTIC_AMMO = 0x35, + SPELL_FAILED_NEED_MORE_ITEMS = 0x36, + SPELL_FAILED_NOPATH = 0x37, + SPELL_FAILED_NOT_BEHIND = 0x38, + SPELL_FAILED_NOT_FISHABLE = 0x39, + SPELL_FAILED_NOT_FLYING = 0x3A, + SPELL_FAILED_NOT_HERE = 0x3B, + SPELL_FAILED_NOT_INFRONT = 0x3C, + SPELL_FAILED_NOT_IN_CONTROL = 0x3D, + SPELL_FAILED_NOT_KNOWN = 0x3E, + SPELL_FAILED_NOT_MOUNTED = 0x3F, + SPELL_FAILED_NOT_ON_TAXI = 0x40, + SPELL_FAILED_NOT_ON_TRANSPORT = 0x41, + SPELL_FAILED_NOT_READY = 0x42, + SPELL_FAILED_NOT_SHAPESHIFT = 0x43, + SPELL_FAILED_NOT_STANDING = 0x44, + SPELL_FAILED_NOT_TRADEABLE = 0x45, + SPELL_FAILED_NOT_TRADING = 0x46, + SPELL_FAILED_NOT_UNSHEATHED = 0x47, + SPELL_FAILED_NOT_WHILE_GHOST = 0x48, + SPELL_FAILED_NOT_WHILE_LOOTING = 0x49, + SPELL_FAILED_NO_AMMO = 0x4A, + SPELL_FAILED_NO_CHARGES_REMAIN = 0x4B, + SPELL_FAILED_NO_CHAMPION = 0x4C, + SPELL_FAILED_NO_COMBO_POINTS = 0x4D, + SPELL_FAILED_NO_DUELING = 0x4E, + SPELL_FAILED_NO_ENDURANCE = 0x4F, + SPELL_FAILED_NO_FISH = 0x50, + SPELL_FAILED_NO_ITEMS_WHILE_SHAPESHIFTED = 0x51, + SPELL_FAILED_NO_MOUNTS_ALLOWED = 0x52, + SPELL_FAILED_NO_PET = 0x53, + SPELL_FAILED_NO_POWER = 0x54, + SPELL_FAILED_NOTHING_TO_DISPEL = 0x55, + SPELL_FAILED_NOTHING_TO_STEAL = 0x56, + SPELL_FAILED_ONLY_ABOVEWATER = 0x57, + SPELL_FAILED_ONLY_DAYTIME = 0x58, + SPELL_FAILED_ONLY_INDOORS = 0x59, + SPELL_FAILED_ONLY_MOUNTED = 0x5A, + SPELL_FAILED_ONLY_NIGHTTIME = 0x5B, + SPELL_FAILED_ONLY_OUTDOORS = 0x5C, + SPELL_FAILED_ONLY_SHAPESHIFT = 0x5D, + SPELL_FAILED_ONLY_STEALTHED = 0x5E, + SPELL_FAILED_ONLY_UNDERWATER = 0x5F, + SPELL_FAILED_OUT_OF_RANGE = 0x60, + SPELL_FAILED_PACIFIED = 0x61, + SPELL_FAILED_POSSESSED = 0x62, + SPELL_FAILED_REAGENTS = 0x63, + SPELL_FAILED_REQUIRES_AREA = 0x64, + SPELL_FAILED_REQUIRES_SPELL_FOCUS = 0x65, + SPELL_FAILED_ROOTED = 0x66, + SPELL_FAILED_SILENCED = 0x67, + SPELL_FAILED_SPELL_IN_PROGRESS = 0x68, + SPELL_FAILED_SPELL_LEARNED = 0x69, + SPELL_FAILED_SPELL_UNAVAILABLE = 0x6A, + SPELL_FAILED_STUNNED = 0x6B, + SPELL_FAILED_TARGETS_DEAD = 0x6C, + SPELL_FAILED_TARGET_AFFECTING_COMBAT = 0x6D, + SPELL_FAILED_TARGET_AURASTATE = 0x6E, + SPELL_FAILED_TARGET_DUELING = 0x6F, + SPELL_FAILED_TARGET_ENEMY = 0x70, + SPELL_FAILED_TARGET_ENRAGED = 0x71, + SPELL_FAILED_TARGET_FRIENDLY = 0x72, + SPELL_FAILED_TARGET_IN_COMBAT = 0x73, + SPELL_FAILED_TARGET_IS_PLAYER = 0x74, + SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED = 0x75, + SPELL_FAILED_TARGET_NOT_DEAD = 0x76, + SPELL_FAILED_TARGET_NOT_IN_PARTY = 0x77, + SPELL_FAILED_TARGET_NOT_LOOTED = 0x78, + SPELL_FAILED_TARGET_NOT_PLAYER = 0x79, + SPELL_FAILED_TARGET_NO_POCKETS = 0x7A, + SPELL_FAILED_TARGET_NO_WEAPONS = 0x7B, + SPELL_FAILED_TARGET_NO_RANGED_WEAPONS = 0x7C, + SPELL_FAILED_TARGET_UNSKINNABLE = 0x7D, + SPELL_FAILED_THIRST_SATIATED = 0x7E, + SPELL_FAILED_TOO_CLOSE = 0x7F, + SPELL_FAILED_TOO_MANY_OF_ITEM = 0x80, + SPELL_FAILED_TOTEM_CATEGORY = 0x81, + SPELL_FAILED_TOTEMS = 0x82, + SPELL_FAILED_TRY_AGAIN = 0x83, + SPELL_FAILED_UNIT_NOT_BEHIND = 0x84, + SPELL_FAILED_UNIT_NOT_INFRONT = 0x85, + SPELL_FAILED_WRONG_PET_FOOD = 0x86, + SPELL_FAILED_NOT_WHILE_FATIGUED = 0x87, + SPELL_FAILED_TARGET_NOT_IN_INSTANCE = 0x88, + SPELL_FAILED_NOT_WHILE_TRADING = 0x89, + SPELL_FAILED_TARGET_NOT_IN_RAID = 0x8A, + SPELL_FAILED_TARGET_FREEFORALL = 0x8B, + SPELL_FAILED_NO_EDIBLE_CORPSES = 0x8C, + SPELL_FAILED_ONLY_BATTLEGROUNDS = 0x8D, + SPELL_FAILED_TARGET_NOT_GHOST = 0x8E, + SPELL_FAILED_TRANSFORM_UNUSABLE = 0x8F, + SPELL_FAILED_WRONG_WEATHER = 0x90, + SPELL_FAILED_DAMAGE_IMMUNE = 0x91, + SPELL_FAILED_PREVENTED_BY_MECHANIC = 0x92, + SPELL_FAILED_PLAY_TIME = 0x93, + SPELL_FAILED_REPUTATION = 0x94, + SPELL_FAILED_MIN_SKILL = 0x95, + SPELL_FAILED_NOT_IN_ARENA = 0x96, + SPELL_FAILED_NOT_ON_SHAPESHIFT = 0x97, + SPELL_FAILED_NOT_ON_STEALTHED = 0x98, + SPELL_FAILED_NOT_ON_DAMAGE_IMMUNE = 0x99, + SPELL_FAILED_NOT_ON_MOUNTED = 0x9A, + SPELL_FAILED_TOO_SHALLOW = 0x9B, + SPELL_FAILED_TARGET_NOT_IN_SANCTUARY = 0x9C, + SPELL_FAILED_TARGET_IS_TRIVIAL = 0x9D, + SPELL_FAILED_BM_OR_INVISGOD = 0x9E, + SPELL_FAILED_EXPERT_RIDING_REQUIREMENT = 0x9F, + SPELL_FAILED_ARTISAN_RIDING_REQUIREMENT = 0xA0, + SPELL_FAILED_NOT_IDLE = 0xA1, + SPELL_FAILED_NOT_INACTIVE = 0xA2, + SPELL_FAILED_PARTIAL_PLAYTIME = 0xA3, + SPELL_FAILED_NO_PLAYTIME = 0xA4, + SPELL_FAILED_NOT_IN_BATTLEGROUND = 0xA5, + SPELL_FAILED_NOT_IN_RAID_INSTANCE = 0xA6, + SPELL_FAILED_ONLY_IN_ARENA = 0xA7, + SPELL_FAILED_TARGET_LOCKED_TO_RAID_INSTANCE = 0xA8, + SPELL_FAILED_ON_USE_ENCHANT = 0xA9, + SPELL_FAILED_NOT_ON_GROUND = 0xAA, + SPELL_FAILED_CUSTOM_ERROR = 0xAB, + SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW = 0xAC, + SPELL_FAILED_TOO_MANY_SOCKETS = 0xAD, + SPELL_FAILED_INVALID_GLYPH = 0xAE, + SPELL_FAILED_UNIQUE_GLYPH = 0xAF, + SPELL_FAILED_GLYPH_SOCKET_LOCKED = 0xB0, + SPELL_FAILED_NO_VALID_TARGETS = 0xB1, + SPELL_FAILED_ITEM_AT_MAX_CHARGES = 0xB2, + SPELL_FAILED_NOT_IN_BARBERSHOP = 0xB3, + SPELL_FAILED_FISHING_TOO_LOW = 0xB4, + SPELL_FAILED_ITEM_ENCHANT_TRADE_WINDOW = 0xB5, + SPELL_FAILED_SUMMON_PENDING = 0xB6, + SPELL_FAILED_MAX_SOCKETS = 0xB7, + SPELL_FAILED_PET_CAN_RENAME = 0xB8, + SPELL_FAILED_UNKNOWN = 0xB9, + + SPELL_CAST_OK = 0xFF // custom value, don't must be send to client }; // Spell aura states enum AuraState { // (C) used in caster aura state (T) used in target aura state // (c) used in caster aura state-not (t) used in target aura state-not + AURA_STATE_NONE = 0, // C | AURA_STATE_DEFENSE = 1, // C | AURA_STATE_HEALTHLESS_20_PERCENT = 2, // CcT | AURA_STATE_BERSERKING = 3, // C T | @@ -907,7 +913,7 @@ enum AuraState //AURA_STATE_UNKNOWN11 = 11, // t| AURA_STATE_FAERIE_FIRE = 12, // c t| AURA_STATE_HEALTHLESS_35_PERCENT = 13, // C T | - AURA_STATE_IMMOLATE = 14, // T | + AURA_STATE_CONFLAGRATE = 14, // T | AURA_STATE_SWIFTMEND = 15, // T | AURA_STATE_DEADLY_POISON = 16, // T | AURA_STATE_ENRAGE = 17, // C | @@ -919,6 +925,9 @@ enum AuraState AURA_STATE_HEALTH_ABOVE_75_PERCENT = 23, // C | }; +#define PER_CASTER_AURA_STATE_MASK ( \ + (1<<(AURA_STATE_CONFLAGRATE-1))|(1<<(AURA_STATE_DEADLY_POISON-1))) + // Spell mechanics enum Mechanics { @@ -965,7 +974,13 @@ enum Mechanics (1<<MECHANIC_SHACKLE )|(1<<MECHANIC_TURN )|(1<<MECHANIC_HORROR)| \ (1<<MECHANIC_DAZE )|(1<<MECHANIC_SAPPED ) ) -// Spell dispel type +// Daze and all croud control spells except polymorph are not removed +#define MECHANIC_NOT_REMOVED_BY_SHAPESHIFT ( \ + (1<<MECHANIC_CHARM )|(1<<MECHANIC_DISORIENTED)|(1<<MECHANIC_FEAR )|(1<<MECHANIC_PACIFY )| \ + (1<<MECHANIC_STUN )|(1<<MECHANIC_FREEZE )|(1<<MECHANIC_BANISH)|(1<<MECHANIC_SHACKLE)| \ + (1<<MECHANIC_HORROR)|(1<<MECHANIC_TURN )|(1<<MECHANIC_DAZE )|(1<<MECHANIC_SAPPED ) ) + +// Spell dispell type enum DispelType { DISPEL_NONE = 0, @@ -1835,6 +1850,7 @@ enum CreatureType CREATURE_TYPE_GAS_CLOUD = 13 }; +uint32 const CREATURE_TYPEMASK_DEMON_OR_UNDEAD = (1 << (CREATURE_TYPE_DEMON-1)) | (1 << (CREATURE_TYPE_UNDEAD-1)); uint32 const CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD = (1 << (CREATURE_TYPE_HUMANOID-1)) | (1 << (CREATURE_TYPE_UNDEAD-1)); uint32 const CREATURE_TYPEMASK_MECHANICAL_OR_ELEMENTAL = (1 << (CREATURE_TYPE_MECHANICAL-1)) | (1 << (CREATURE_TYPE_ELEMENTAL-1)); @@ -2269,7 +2285,9 @@ enum CorpseDynFlags #define SPELL_ID_WEAPON_SWITCH_COOLDOWN_1_0s 6123 #define SPELL_ID_AUTOSHOT 75 // used for checks in other spells interruption #define SPELL_ID_SHADOWMELD 58984 // used for check ignore stealth stance state - +#define SPELL_ID_BLOOD_PRESENCE 48266 // Blood Presence +#define SPELL_ID_FROST_PRESENCE 48263 // Frost Presence +#define SPELL_ID_UNHOLY_PRESENCE 48265 // Unholy Presence enum WeatherType { WEATHER_TYPE_FINE = 0, @@ -2384,20 +2402,23 @@ enum DiminishingGroup { // Common Groups DIMINISHING_NONE, - DIMINISHING_CONTROL_STUN, // Player Controlled stuns - DIMINISHING_TRIGGER_STUN, // By aura proced stuns, usualy chance on hit talents - DIMINISHING_CONTROL_ROOT, // Immobilizing effects from casted spells - DIMINISHING_TRIGGER_ROOT, // Immobilizing effects from triggered spells like Frostbite + DIMINISHING_CONTROL_STUN, // Player Controlled stuns + DIMINISHING_TRIGGER_STUN, // By aura proced stuns, usualy chance on hit talents + DIMINISHING_CONTROL_ROOT, // Immobilizing effects from casted spells + DIMINISHING_TRIGGER_ROOT, // Immobilizing effects from triggered spells like Frostbite DIMINISHING_CHARM, - DIMINISHING_SLEEP_FREEZE, DIMINISHING_POLYMORPH, // Also: Gouge, Sap, Repentance, Hungering Cold - DIMINISHING_CHEAPSHOT_POUNCE, - DIMINISHING_DEATHCOIL, // Death Coil Diminish only with another Death Coil - DIMINISHING_SILENCE, DIMINISHING_KNOCKOUT, // Sap, Knockout mechanics - DIMINISHING_DISARM, DIMINISHING_FEAR_BLIND, // Intimidating Shout, Howl of Terror, Blind - DIMINISHING_FEAR, + // Warlock Specific + DIMINISHING_DEATHCOIL, // Death Coil Diminish only with another Death Coil + // Druid Specific + DIMINISHING_CYCLONE, // From 2.3.0 + // Shared Class Specific + DIMINISHING_CHEAPSHOT_POUNCE, + DIMINISHING_DISARM, // From 2.3.0 + DIMINISHING_SILENCE, // From 2.3.0 + DIMINISHING_FREEZE_SLEEP, // Hunter's Freezing Trap DIMINISHING_BANISH, DIMINISHING_TAUNT, DIMINISHING_LIMITONLY // Don't Diminish, but limit duration to 10s diff --git a/src/game/SkillDiscovery.cpp b/src/game/SkillDiscovery.cpp index 2116e554a9f..686fcf889b9 100644 --- a/src/game/SkillDiscovery.cpp +++ b/src/game/SkillDiscovery.cpp @@ -26,6 +26,7 @@ #include "Util.h" #include "SkillDiscovery.h" #include "SpellMgr.h" +#include "Player.h" #include <map> struct SkillDiscoveryEntry @@ -37,7 +38,7 @@ struct SkillDiscoveryEntry SkillDiscoveryEntry() : spellId(0), reqSkillValue(0), chance(0) {} - SkillDiscoveryEntry(uint16 _spellId, uint32 req_skill_val, float _chance) + SkillDiscoveryEntry(uint32 _spellId, uint32 req_skill_val, float _chance) : spellId(_spellId), reqSkillValue(req_skill_val), chance(_chance) {} }; @@ -105,7 +106,9 @@ void LoadSkillDiscoveryTable() { if (reportedReqSpells.count(reqSkillOrSpell)==0) { - sLog.outErrorDb("Spell (ID: %u) not have have MECHANIC_DISCOVERY (28) value in Mechanic field in spell.dbc and not 100%% chance random discovery ability but listed for spellId %u (and maybe more) in `skill_discovery_template` table",reqSkillOrSpell,spellId); + sLog.outErrorDb("Spell (ID: %u) not have MECHANIC_DISCOVERY (28) value in Mechanic field in spell.dbc" + " and not 100%% chance random discovery ability but listed for spellId %u (and maybe more) in `skill_discovery_template` table", + reqSkillOrSpell,spellId); reportedReqSpells.insert(reqSkillOrSpell); } continue; @@ -144,6 +147,21 @@ void LoadSkillDiscoveryTable() sLog.outString( ">> Loaded %u skill discovery definitions", count ); if(!ssNonDiscoverableEntries.str().empty()) sLog.outErrorDb("Some items can't be successfully discovered: have in chance field value < 0.000001 in `skill_discovery_template` DB table . List:\n%s",ssNonDiscoverableEntries.str().c_str()); + + // report about empty data for explicit discovery spells + for(uint32 spell_id = 1; spell_id < sSpellStore.GetNumRows(); ++spell_id) + { + SpellEntry const* spellEntry = sSpellStore.LookupEntry(spell_id); + if(!spellEntry) + continue; + + // skip not explicit discovery spells + if (!IsExplicitDiscoverySpell(spellEntry)) + continue; + + if(SkillDiscoveryStore.find(spell_id)==SkillDiscoveryStore.end()) + sLog.outErrorDb("Spell (ID: %u) is 100%% chance random discovery ability but not have data in `skill_discovery_template` table",spell_id); + } } uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player) diff --git a/src/game/SocialMgr.cpp b/src/game/SocialMgr.cpp index fb0e1574d08..d94cd422f11 100644 --- a/src/game/SocialMgr.cpp +++ b/src/game/SocialMgr.cpp @@ -198,7 +198,7 @@ void SocialMgr::GetFriendInfo(Player *player, uint32 friendGUID, FriendInfo &fri uint32 team = player->GetTeam(); AccountTypes security = player->GetSession()->GetSecurity(); bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST); - bool gmInWhoList = sWorld.getConfig(CONFIG_GM_IN_WHO_LIST) || security > SEC_PLAYER; + AccountTypes gmLevelInWhoList = AccountTypes (sWorld.getConfig(CONFIG_GM_LEVEL_IN_WHO_LIST)); PlayerSocialMap::iterator itr = player->GetSocial()->m_playerSocialMap.find(friendGUID); if(itr != player->GetSocial()->m_playerSocialMap.end()) @@ -206,10 +206,10 @@ void SocialMgr::GetFriendInfo(Player *player, uint32 friendGUID, FriendInfo &fri // PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters // MODERATOR, GAME MASTER, ADMINISTRATOR can see all - if( pFriend && pFriend->GetName() && - ( security > SEC_PLAYER || - ( pFriend->GetTeam() == team || allowTwoSideWhoList ) && - ( pFriend->GetSession()->GetSecurity() == SEC_PLAYER || gmInWhoList && pFriend->IsVisibleGloballyFor(player) ))) + if (pFriend && pFriend->GetName() && + (security > SEC_PLAYER || + (pFriend->GetTeam() == team || allowTwoSideWhoList) && (pFriend->GetSession()->GetSecurity() <= gmLevelInWhoList)) && + pFriend->IsVisibleGloballyFor(player)) { friendInfo.Status = FRIEND_STATUS_ONLINE; if(pFriend->isAFK()) @@ -273,7 +273,7 @@ void SocialMgr::BroadcastToFriendListers(Player *player, WorldPacket *packet) uint32 team = player->GetTeam(); AccountTypes security = player->GetSession()->GetSecurity(); uint32 guid = player->GetGUIDLow(); - bool gmInWhoList = sWorld.getConfig(CONFIG_GM_IN_WHO_LIST); + AccountTypes gmLevelInWhoList = AccountTypes(sWorld.getConfig(CONFIG_GM_LEVEL_IN_WHO_LIST)); bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST); for(SocialMap::const_iterator itr = m_socialMap.begin(); itr != m_socialMap.end(); ++itr) @@ -285,10 +285,10 @@ void SocialMgr::BroadcastToFriendListers(Player *player, WorldPacket *packet) // PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters // MODERATOR, GAME MASTER, ADMINISTRATOR can see all - if( pFriend && pFriend->IsInWorld() && - ( pFriend->GetSession()->GetSecurity() > SEC_PLAYER || - ( pFriend->GetTeam() == team || allowTwoSideWhoList ) && - (security == SEC_PLAYER || gmInWhoList && player->IsVisibleGloballyFor(pFriend) ))) + if (pFriend && pFriend->IsInWorld() && + (pFriend->GetSession()->GetSecurity() > SEC_PLAYER || + (pFriend->GetTeam() == team || allowTwoSideWhoList) && security <= gmLevelInWhoList) && + player->IsVisibleGloballyFor(pFriend)) { pFriend->GetSession()->SendPacket(packet); } diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index ed818d95570..991b525afa7 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -82,7 +82,7 @@ struct PrioritizeMana { int operator()( PrioritizeManaUnitWraper const& x, PrioritizeManaUnitWraper const& y ) const { - return x.getPercent() < y.getPercent(); + return x.getPercent() > y.getPercent(); } }; @@ -106,7 +106,7 @@ struct PrioritizeHealth { int operator()( PrioritizeHealthUnitWraper const& x, PrioritizeHealthUnitWraper const& y ) const { - return x.getPercent() < y.getPercent(); + return x.getPercent() > y.getPercent(); } }; @@ -363,6 +363,7 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi m_referencedFromCurrentSpell = false; m_executedCurrently = false; m_needComboPoints = NeedsComboPoints(m_spellInfo); + m_comboPointGain = 0; m_delayStart = 0; m_delayAtDamageCount = 0; @@ -370,6 +371,7 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi m_applyMultiplierMask = 0; m_effectMask = 0; + m_auraScaleMask = 0; // Get data for type of attack switch (m_spellInfo->DmgClass) @@ -700,7 +702,6 @@ void Spell::FillTargetMap() break; } } - if(IsChanneledSpell(m_spellInfo)) { uint8 mask = (1<<i); @@ -713,6 +714,29 @@ void Spell::FillTargetMap() } } } + else if (m_auraScaleMask) + { + bool checkLvl = !m_UniqueTargetInfo.empty(); + for(std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end();) + { + // remove targets which did not pass min level check + if(m_auraScaleMask && ihit->effectMask == m_auraScaleMask) + { + // Do not check for selfcast + if (!ihit->scaleAura && ihit->targetGUID != m_caster->GetGUID()) + { + m_UniqueTargetInfo.erase(ihit++); + continue; + } + } + ++ihit; + } + if (checkLvl && m_UniqueTargetInfo.empty()) + { + SendCastResult(SPELL_FAILED_LOWLEVEL); + finish(false); + } + } } if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) @@ -734,6 +758,7 @@ void Spell::prepareDataForTriggerSystem(AuraEffect * triggeredByAura) // Create base triggers flags for Attacker and Victim ( m_procAttacker, m_procVictim and m_procEx) //========================================================================================== + m_procVictim = m_procAttacker = 0; // Get data for type of attack and fill base info for trigger switch (m_spellInfo->DmgClass) { @@ -755,37 +780,15 @@ void Spell::prepareDataForTriggerSystem(AuraEffect * triggeredByAura) } break; default: - if (IsPositiveSpell(m_spellInfo->Id)) // Check for positive spell - { - if(m_customAttr & SPELL_ATTR_CU_DIRECT_DAMAGE) - { - m_procAttacker = PROC_FLAG_SUCCESSFUL_HEALING_SPELL; - m_procVictim = PROC_FLAG_TAKEN_HEALING_SPELL; - } - else - { - m_procAttacker = PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL; - m_procVictim = PROC_FLAG_TAKEN_POSITIVE_SPELL; - } - } - else if (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG) // Wands auto attack + if (m_spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON && + m_spellInfo->EquippedItemSubClassMask & (1<<ITEM_SUBCLASS_WEAPON_WAND) + && m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG) // Wands auto attack { m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_HIT; m_procVictim = PROC_FLAG_TAKEN_RANGED_HIT; } - else // Negative spell - { - if(m_customAttr & SPELL_ATTR_CU_DIRECT_DAMAGE) - { - m_procAttacker = PROC_FLAG_SUCCESSFUL_DAMAGING_SPELL_HIT; - m_procVictim = PROC_FLAG_TAKEN_DAMAGING_SPELL_HIT; - } - else - { - m_procAttacker = PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT; - m_procVictim = PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT; - } - } + // For other spells trigger procflags are set in Spell::DoAllEffectOnTarget + // Because spell positivity is dependant on target } m_procEx= PROC_EX_NONE; @@ -794,40 +797,34 @@ void Spell::prepareDataForTriggerSystem(AuraEffect * triggeredByAura) if (m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && (m_spellInfo->SpellFamilyFlags[1] & 0x00002000 || m_spellInfo->SpellFamilyFlags[0] & 0x1C)) { m_procAttacker |= PROC_FLAG_ON_TRAP_ACTIVATION; + // Trigger only from spells originally casted by hunter(trap activation) to prevent multiple trigger from trap triggered spells + if (m_originalCasterGUID != m_caster->GetGUID() && m_originalCasterGUID) + return; } - else + /* + Effects which are result of aura proc from triggered spell cannot proc + to prevent chain proc of these spells + */ + if ((triggeredByAura && !triggeredByAura->GetParentAura()->GetTarget()->CanProc())) { - /* - Effects which are result of aura proc from triggered spell cannot proc - to prevent chain proc of these spells - */ - if ((triggeredByAura && !triggeredByAura->GetParentAura()->GetTarget()->CanProc()) || !m_caster->CanProc()) - { - m_canTrigger=false; - } + m_canTrigger=false; + } + // Ranged autorepeat attack is set as triggered spell - ignore it + if (!(m_procAttacker & PROC_FLAG_SUCCESSFUL_RANGED_HIT)) + { if (m_IsTriggeredSpell && (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_TRIGGERED_CAN_TRIGGER || m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_TRIGGERED_CAN_TRIGGER_2)) + m_procEx |= PROC_EX_INTERNAL_CANT_PROC; + else if (m_IsTriggeredSpell) m_procEx |= PROC_EX_INTERNAL_TRIGGERED; - - // Totem casts require spellfamilymask defined in spell_proc_event to proc - if (m_originalCaster && m_caster != m_originalCaster && m_caster->GetTypeId()==TYPEID_UNIT && ((Creature*)m_caster)->isTotem() && m_caster->IsControlledByPlayer()) - { - m_procEx |= PROC_EX_INTERNAL_REQ_FAMILY; - } - // Check done for judgements to make them not trigger seal effects - else if (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_UNK1) - { - // Rogue poisons - if (m_spellInfo->SpellFamilyName && m_spellInfo->SpellFamilyFlags) - m_procEx |= PROC_EX_INTERNAL_REQ_FAMILY; - else - m_canTrigger=false; - } } - if (m_IsTriggeredSpell || triggeredByAura) - m_procEx |= PROC_EX_INTERNAL_CANT_PROC; + // Totem casts require spellfamilymask defined in spell_proc_event to proc + if (m_originalCaster && m_caster != m_originalCaster && m_caster->GetTypeId()==TYPEID_UNIT && ((Creature*)m_caster)->isTotem() && m_caster->IsControlledByPlayer()) + { + m_procEx |= PROC_EX_INTERNAL_REQ_FAMILY; + } } void Spell::CleanupTargetList() @@ -858,6 +855,13 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) { if (!immuned) ihit->effectMask |= 1 << effIndex; // Add only effect mask if not immuned + ihit->scaleAura = false; + if (m_auraScaleMask && ihit->effectMask == m_auraScaleMask && m_caster != pVictim) + { + SpellEntry const * auraSpell = sSpellStore.LookupEntry(spellmgr.GetFirstSpellInChain(m_spellInfo->Id)); + if ((pVictim->getLevel() + 10) >= auraSpell->spellLevel) + ihit->scaleAura = true; + } return; } } @@ -872,6 +876,13 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) target.alive = pVictim->isAlive(); target.damage = 0; target.crit = false; + target.scaleAura = false; + if (m_auraScaleMask && target.effectMask == m_auraScaleMask && m_caster != pVictim) + { + SpellEntry const * auraSpell = sSpellStore.LookupEntry(spellmgr.GetFirstSpellInChain(m_spellInfo->Id)); + if ((pVictim->getLevel() + 10) >= auraSpell->spellLevel) + target.scaleAura = true; + } // Calculate hit result if(m_originalCaster) @@ -1052,7 +1063,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) if(spellHitTarget) { - SpellMissInfo missInfo = DoSpellHitOnUnit(spellHitTarget, mask); + SpellMissInfo missInfo = DoSpellHitOnUnit(spellHitTarget, mask, target->scaleAura); if(missInfo != SPELL_MISS_NONE) { if(missInfo != SPELL_MISS_MISS) @@ -1067,6 +1078,50 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) if( missInfo != SPELL_MISS_NONE && missInfo != SPELL_MISS_MISS) m_needComboPoints = false; + // Trigger info was not filled in spell::preparedatafortriggersystem - we do it now + if (canEffectTrigger && !procAttacker && !procVictim) + { + bool positive = true; + if (m_damage > 0) + positive = false; + else if (!m_healing) + { + for (uint8 i = 0; i< MAX_SPELL_EFFECTS; ++i) + // If at least one effect negative spell is negative hit + if (mask & (1<<i) && !IsPositiveEffect(m_spellInfo->Id, i)) + { + positive = false; + break; + } + } + switch(m_spellInfo->DmgClass) + { + case SPELL_DAMAGE_CLASS_MAGIC: + if (positive) + { + procAttacker |= PROC_FLAG_SUCCESSFUL_POSITIVE_MAGIC_SPELL; + procVictim |= PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL; + } + else + { + procAttacker |= PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL; + procVictim |= PROC_FLAG_TAKEN_NEGATIVE_MAGIC_SPELL; + } + break; + case SPELL_DAMAGE_CLASS_NONE: + if (positive) + { + procAttacker |= PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL_HIT; + procVictim |= PROC_FLAG_TAKEN_POSITIVE_SPELL; + } + else + { + procAttacker |= PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT; + procVictim |= PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT; + } + break; + } + } // All calculated do it! // Do healing and triggers if (m_healing > 0) @@ -1109,11 +1164,15 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT) + { caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell); + if(caster->GetTypeId() == TYPEID_PLAYER && (m_spellInfo->Attributes & SPELL_ATTR_STOP_ATTACK_TARGET) == 0 && + (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) + ((Player *)caster)->CastItemCombatSpell(unitTarget, m_attackType, procVictim, procEx); + } if (m_spellAura) m_spellAura->SetProcDamage(damageInfo.damage); - caster->DealSpellDamage(&damageInfo, true); // Judgement of Blood @@ -1136,11 +1195,9 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) if( !m_caster->IsFriendlyTo(unit) && !IsPositiveSpell(m_spellInfo->Id)) { - if( !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) ) - { - m_caster->CombatStart(unit); - } - else if(m_customAttr & SPELL_ATTR_CU_AURA_CC) + m_caster->CombatStart(unit, !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO)); + + if(m_customAttr & SPELL_ATTR_CU_AURA_CC) { if(!unit->IsStandState()) unit->SetStandState(UNIT_STAND_STATE_STAND); @@ -1174,7 +1231,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) } } -SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) +SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask, bool scaleAura) { if(!unit || !effectMask) return SPELL_MISS_EVADE; @@ -1242,7 +1299,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) } if( unit->isInCombat() && !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) ) { - m_caster->SetInCombatState(unit->GetCombatTimer() > 0); + m_caster->SetInCombatState(unit->GetCombatTimer() > 0, unit); unit->getHostilRefManager().threatAssist(m_caster, 0.0f); } } @@ -1265,21 +1322,43 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) if (aura_effmask) { + // Select rank for aura with level requirements only in specific cases + // Unit has to be target only of aura effect, both caster and target have to be players, target has to be other than unit target + SpellEntry const * aurSpellInfo = m_spellInfo; + int32 basePoints[3]; + if (scaleAura) + { + aurSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(m_spellInfo,unitTarget->getLevel()); + assert (aurSpellInfo); + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + basePoints[i] = aurSpellInfo->EffectBasePoints[i]; + } + } + else + { + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + basePoints[i] = m_currentBasePoints[i]; + } + } + Unit * caster = m_originalCaster ? m_originalCaster : m_caster; - Aura * Aur = new Aura(m_spellInfo, aura_effmask, m_currentBasePoints, unit, m_caster, caster, m_CastItem); + Aura * Aur = new Aura(aurSpellInfo, aura_effmask, basePoints, unit, m_caster, caster, m_CastItem); if (!Aur->IsAreaAura()) { // Now Reduce spell duration using data received at spell hit int32 duration = Aur->GetAuraMaxDuration(); - unit->ApplyDiminishingToDuration(m_diminishGroup,duration,caster,m_diminishLevel); + int32 limitduration = GetDiminishingReturnsLimitDuration(m_diminishGroup,aurSpellInfo); + unitTarget->ApplyDiminishingToDuration(m_diminishGroup, duration, caster, m_diminishLevel,limitduration); Aur->setDiminishGroup(m_diminishGroup); - duration = caster->ModSpellDuration(m_spellInfo, unit, duration, Aur->IsPositive()); + duration = caster->ModSpellDuration(aurSpellInfo, unit, duration, Aur->IsPositive()); //mod duration of channeled aura by spell haste if (IsChanneledSpell(m_spellInfo)) - caster->ModSpellCastTime(m_spellInfo, duration, this); + caster->ModSpellCastTime(aurSpellInfo, duration, this); if(duration != Aur->GetAuraMaxDuration()) { @@ -1288,9 +1367,9 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) } // Prayer of Mending (jump animation), we need formal caster instead original for correct animation - if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST) + if( aurSpellInfo->SpellFamilyName == SPELLFAMILY_PRIEST) { - if(m_spellInfo->SpellFamilyFlags[1] & 0x000020) + if(aurSpellInfo->SpellFamilyFlags[1] & 0x000020) m_caster->CastSpell(unit, 41637, true, NULL, NULL, m_originalCasterGUID); } } @@ -1337,7 +1416,7 @@ void Spell::DoTriggersOnSpellHit(Unit *unit) if(roll_chance_i(i->second)) { m_caster->CastSpell(unit, i->first, true); - sLog.outDebug("Spell %d triggered spell %d by SPELL_AURA_ADD_TARGET_TRIGGER aura", m_spellInfo->Id, i->first); + sLog.outDebug("Spell %d triggered spell %d by SPELL_AURA_ADD_TARGET_TRIGGER aura", m_spellInfo->Id, i->first->Id); } if (GetSpellDuration(i->first)==-1) { @@ -1651,6 +1730,15 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType) { switch(i_spellST->second.type) { + case SPELL_TARGET_TYPE_CONTROLLED: + for(Unit::ControlList::iterator itr = m_caster->m_Controlled.begin(); itr != m_caster->m_Controlled.end(); ++itr) + if ((*itr)->GetEntry() == i_spellST->second.targetEntry && (*itr)->IsWithinDistInMap(m_caster, range)) + { + goScriptTarget = NULL; + range = m_caster->GetDistance(creatureScriptTarget); + creatureScriptTarget = ((Creature *)*itr); + } + break; case SPELL_TARGET_TYPE_GAMEOBJECT: if(i_spellST->second.targetEntry) { @@ -2143,6 +2231,7 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) case TARGET_UNIT_AREA_ENEMY_DST: case TARGET_UNIT_CONE_ENEMY: case TARGET_UNIT_CONE_ENEMY_UNKNOWN: + case TARGET_UNIT_AREA_PATH: radius = GetSpellRadius(m_spellInfo, i, false); targetType = SPELL_TARGETS_ENEMY; break; @@ -2181,8 +2270,6 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) { case 46584: // Raise Dead { - // TODO: change visual of corpses which gave ghoul? - // Allow corpses to be ghouled only once? m_targets.m_targetMask &= ~TARGET_FLAG_DEST_LOCATION; WorldObject* result = FindCorpseUsing<MaNGOS::RaiseDeadObjectCheck> (); if(result) @@ -2195,6 +2282,45 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) } break; } + // Corpse Explosion + case 49158: + case 51325: + case 51326: + case 51327: + case 51328: + // Search for ghoul if our ghoul or dead body not valid unit target + if (!(m_targets.getUnitTarget() && (m_targets.getUnitTarget()->GetEntry() == 26125 && m_targets.getUnitTarget()->GetOwnerGUID() == m_caster->GetGUID() + || (m_targets.getUnitTarget()->getDeathState() == CORPSE + && m_targets.getUnitTarget()->GetDisplayId() == m_targets.getUnitTarget()->GetNativeDisplayId() + && m_targets.getUnitTarget()->GetTypeId()== TYPEID_UNIT + && !((Creature*)m_targets.getUnitTarget())->isDeadByDefault() + && !(m_targets.getUnitTarget()->GetCreatureTypeMask() & CREATURE_TYPEMASK_MECHANICAL_OR_ELEMENTAL)) + && m_targets.getUnitTarget()->GetDisplayId() == m_targets.getUnitTarget()->GetNativeDisplayId()))) + { + CleanupTargetList(); + + WorldObject* result = FindCorpseUsing <Trinity::ExplodeCorpseObjectCheck> (); + + if(result) + { + switch(result->GetTypeId()) + { + case TYPEID_UNIT: + case TYPEID_PLAYER: + m_targets.setUnitTarget((Unit*)result); + break; + } + } + else + { + if (m_caster->GetTypeId()==TYPEID_PLAYER) + ((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id,true); + SendCastResult(SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW); + finish(false); + } + } + break; + default: sLog.outDebug("Spell (ID: %u) (caster Entry: %u) does not have record in `spell_script_target`", m_spellInfo->Id, m_caster->GetEntry()); @@ -2213,6 +2339,13 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) { if(i_spellST->second.type == SPELL_TARGET_TYPE_CREATURE) SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ENTRY, i_spellST->second.targetEntry); + else if (i_spellST->second.type == SPELL_TARGET_TYPE_CONTROLLED) + { + for(Unit::ControlList::iterator itr = m_caster->m_Controlled.begin(); itr != m_caster->m_Controlled.end(); ++itr) + if ((*itr)->GetEntry() == i_spellST->second.targetEntry && + /*(*itr)->IsWithinDistInMap(m_caster, radius)*/ (*itr)->IsInMap(m_caster)) // For 60243 and 52173 need skip radius check or use range (no radius entry for effect) + unitList.push_back(*itr); + } } } } @@ -2319,7 +2452,7 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) case 59725: // Improved Spell Reflection - aoe aura unitList.remove(m_caster); break; - case 57699: //Replenishment (special target selection) 10 targets with lowest mana + case 57669: //Replenishment (special target selection) 10 targets with lowest mana { typedef std::priority_queue<PrioritizeManaUnitWraper, std::vector<PrioritizeManaUnitWraper>, PrioritizeMana> TopMana; TopMana manaUsers; @@ -2380,6 +2513,34 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) healedMembers.pop(); } } + // Death Pact + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && m_spellInfo->SpellFamilyFlags[0] & 0x00080000) + { + Unit * unit_to_add = NULL; + for (std::list<Unit*>::iterator itr = unitList.begin() ; itr != unitList.end();++itr) + { + if ((*itr)->GetTypeId() == TYPEID_UNIT + && (*itr)->GetOwnerGUID() == m_caster->GetGUID() + && ((Creature*)(*itr))->GetCreatureInfo()->type == CREATURE_TYPE_UNDEAD) + { + unit_to_add = (*itr); + break; + } + } + if (unit_to_add) + { + unitList.clear(); + unitList.push_back(unit_to_add); + } + // Pet not found - remove cooldown + else + { + if (modOwner->GetTypeId()==TYPEID_PLAYER) + modOwner->RemoveSpellCooldown(m_spellInfo->Id,true); + SendCastResult(SPELL_FAILED_NO_PET); + finish(false); + } + } } for(std::list<Unit*>::iterator itr = unitList.begin(); itr != unitList.end(); ++itr) AddUnitTarget(*itr, i); @@ -2414,6 +2575,27 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura } } + // Fill aura scaling information + if (m_caster->IsControlledByPlayer() && !IsPassiveSpell(m_spellInfo->Id) && m_spellInfo->spellLevel && !IsChanneledSpell(m_spellInfo) && !m_IsTriggeredSpell) + { + for (uint8 i = 0; i< MAX_SPELL_EFFECTS; ++i) + { + if (m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA) + { + // Change aura with ranks only if basepoints are taken from spellInfo and aura is positive + if (IsPositiveEffect(m_spellInfo->Id, i)) + { + m_auraScaleMask |= (1<<i); + if (m_currentBasePoints[i] != m_spellInfo->EffectBasePoints[i]) + { + m_auraScaleMask = 0; + break; + } + } + } + } + } + m_spellState = SPELL_STATE_PREPARING; if(triggeredByAura) @@ -2458,8 +2640,12 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura } } - // Fill cost data - m_powerCost = CalculatePowerCost(); + if (m_caster->GetTypeId()==TYPEID_PLAYER) + ((Player*)m_caster)->SetSpellModTakingSpell(this, true); + // Fill cost data (not use power for item casts + m_powerCost = m_CastItem ? 0 : CalculatePowerCost(m_spellInfo, m_caster, m_spellSchoolMask); + if (m_caster->GetTypeId()==TYPEID_PLAYER) + ((Player*)m_caster)->SetSpellModTakingSpell(this, false); SpellCastResult result = CheckCast(true); if(result != SPELL_CAST_OK && !IsAutoRepeat()) //always cast autorepeat dummy for triggering @@ -2500,7 +2686,7 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura { // stealth must be removed at cast starting (at show channel bar) // skip triggered spell (item equip spell casting and other not explicit character casts/item uses) - if(isSpellBreakStealth(m_spellInfo) ) + if(!m_IsTriggeredSpell && isSpellBreakStealth(m_spellInfo) ) { m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CAST); for(uint32 i = 0; i < 3; ++i) @@ -2593,6 +2779,7 @@ void Spell::cast(bool skipCheck) && !target->IsFriendlyTo(m_caster) && !m_caster->canSeeOrDetect(target, true)) { SendCastResult(SPELL_FAILED_BAD_TARGETS); + SendInterrupted(0); finish(false); return; } @@ -2626,6 +2813,7 @@ void Spell::cast(bool skipCheck) if(castResult != SPELL_CAST_OK) { SendCastResult(castResult); + SendInterrupted(0); finish(false); SetExecutedCurrently(false); return; @@ -2634,6 +2822,14 @@ void Spell::cast(bool skipCheck) FillTargetMap(); + // Spell may be finished after target map check + if(m_spellState == SPELL_STATE_FINISHED) + { + SendInterrupted(0); + SetExecutedCurrently(false); + return; + } + if(m_spellInfo->SpellFamilyName) { if (m_spellInfo->excludeCasterAuraSpell && !IsPositiveSpell(m_spellInfo->excludeCasterAuraSpell)) @@ -2647,8 +2843,18 @@ void Spell::cast(bool skipCheck) { if (m_spellInfo->Mechanic == MECHANIC_BANDAGE) // Bandages m_preCastSpell = 11196; // Recently Bandaged - else if(m_spellInfo->SpellIconID == 1662 && m_spellInfo->AttributesEx & 0x20) - m_preCastSpell = 23230; // Blood Fury - Healing Reduction + break; + } + case SPELLFAMILY_DRUID: + { + // Faerie Fire (Feral) + if (m_spellInfo->SpellFamilyFlags[0] & 0x00000400) + { + // Trigger only if has correct shapeshift for triggered spell + SpellEntry const * spellInfo = sSpellStore.LookupEntry(60089); + if (GetErrorAtShapeshiftedCast(spellInfo, m_caster->m_form) == SPELL_CAST_OK) + m_preCastSpell = 60089; + } break; } } @@ -2664,7 +2870,13 @@ void Spell::cast(bool skipCheck) ((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL, m_spellInfo->Id); } - // this is related to combo points so must be done before takepower + if(!m_IsTriggeredSpell) + { + // Powers have to be taken before SendSpellGo + TakePower(); + TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot + } + // are there any spells need to be triggered after hit? // handle SPELL_AURA_ADD_TARGET_TRIGGER auras Unit::AuraEffectList const& targetTriggers = m_caster->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER); @@ -2682,17 +2894,9 @@ void Spell::cast(bool skipCheck) } } - // this is related to combo points so must be done before takepower if(m_customAttr & SPELL_ATTR_CU_DIRECT_DAMAGE) CalculateDamageDoneForAllTargets(); - if(!m_IsTriggeredSpell) - { - // Powers have to be taken before SendSpellGo - TakePower(); - TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot - } - // CAST SPELL SendSpellCooldown(); //SendCastResult(castResult); @@ -2726,6 +2930,9 @@ void Spell::cast(bool skipCheck) m_immediateHandled = false; m_spellState = SPELL_STATE_DELAYED; SetDelayStart(0); + + if(m_caster->hasUnitState(UNIT_STAT_CASTING) && !m_caster->IsNonMeleeSpellCasted(false, false, true)) + m_caster->clearUnitState(UNIT_STAT_CASTING); } else { @@ -2882,6 +3089,7 @@ void Spell::_handle_immediate_phase() if(!m_originalCaster) return; + uint8 oldEffMask = m_effectMask; // process ground for(uint32 j = 0; j < 3; ++j) { @@ -2898,6 +3106,38 @@ void Spell::_handle_immediate_phase() m_effectMask |= (1<<j); } } + if (oldEffMask != m_effectMask && m_UniqueTargetInfo.empty()) + { + uint32 procAttacker = m_procAttacker; + if (!procAttacker) + { + bool positive = true; + for (uint8 i = 0; i< MAX_SPELL_EFFECTS; ++i) + // If at least one effect negative spell is negative hit + if (m_effectMask & (1<<i) && !IsPositiveEffect(m_spellInfo->Id, i)) + { + positive = false; + break; + } + switch(m_spellInfo->DmgClass) + { + case SPELL_DAMAGE_CLASS_MAGIC: + if (positive) + procAttacker |= PROC_FLAG_SUCCESSFUL_POSITIVE_MAGIC_SPELL; + else + procAttacker |= PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL; + break; + case SPELL_DAMAGE_CLASS_NONE: + if (positive) + procAttacker |= PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL_HIT; + else + procAttacker |= PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT; + break; + } + } + // Proc damage for spells which have only dest targets (2484 should proc 51486 for example) + m_originalCaster->ProcDamageAndSpell(0, procAttacker, 0, m_procEx | PROC_EX_NORMAL_HIT, 0, BASE_ATTACK, m_spellInfo, m_triggeredByAuraSpell); + } } void Spell::_handle_finish_phase() @@ -2906,6 +3146,10 @@ void Spell::_handle_finish_phase() if (m_needComboPoints) ((Player*)m_caster)->ClearComboPoints(); + // Real add combo points from effects + if (m_caster->GetTypeId()==TYPEID_PLAYER) + ((Player*)m_caster)->GainSpellComboPoints(m_comboPointGain); + // spell log if(m_needSpellLog) SendLogExecute(); @@ -3046,7 +3290,7 @@ void Spell::finish(bool ok) if(IsChanneledSpell(m_spellInfo)) m_caster->UpdateInterruptMask(); - if(!m_caster->IsNonMeleeSpellCasted(false, false, true)) + if(m_caster->hasUnitState(UNIT_STAT_CASTING) && !m_caster->IsNonMeleeSpellCasted(false, false, true)) m_caster->clearUnitState(UNIT_STAT_CASTING); // Unsummon summon as possessed creatures on spell cancel @@ -3103,9 +3347,10 @@ void Spell::finish(bool ok) } // potions disabled by client, send event "not in combat" if need - if (!m_triggeredByAuraSpell && m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->GetTypeId() == TYPEID_PLAYER) { - ((Player*)m_caster)->UpdatePotionCooldown(this); + if (!m_triggeredByAuraSpell) + ((Player*)m_caster)->UpdatePotionCooldown(this); // triggered spell pointer can be not set in some cases // this is needed for proper apply of triggered spell mods @@ -3251,11 +3496,11 @@ void Spell::SendSpellGo() //sLog.outDebug("Sending SMSG_SPELL_GO id=%u", m_spellInfo->Id); uint32 castFlags = CAST_FLAG_UNKNOWN3; - + // triggered spells with spell visual != 0 - if(m_IsTriggeredSpell || m_triggeredByAuraSpell) + if((m_IsTriggeredSpell && !IsAutoRepeatRangedSpell(m_spellInfo)) || m_triggeredByAuraSpell) castFlags |= CAST_FLAG_UNKNOWN0; - + if(m_spellInfo->Attributes & SPELL_ATTR_REQ_AMMO) castFlags |= CAST_FLAG_AMMO; // arrows/bullets visual if ((m_caster->GetTypeId() == TYPEID_PLAYER || @@ -3352,7 +3597,7 @@ void Spell::WriteAmmoToPacket( WorldPacket * data ) ammoInventoryType = pProto->InventoryType; } } - else if(m_caster->GetDummyAura(46699)) // Requires No Ammo + else if(m_caster->HasAura(46699)) // Requires No Ammo { ammoDisplayID = 5996; // normal arrow ammoInventoryType = INVTYPE_AMMO; @@ -3887,6 +4132,7 @@ void Spell::TakeRunePower() if((plr->GetRuneCooldown(i) == 0) && (runeCost[rune] > 0)) { plr->SetRuneCooldown(i, RUNE_COOLDOWN); // 5*2=10 sec + plr->SetLastUsedRune(RuneType(rune)); runeCost[rune]--; } } @@ -3901,8 +4147,52 @@ void Spell::TakeRunePower() if((plr->GetRuneCooldown(i) == 0) && (rune == RUNE_DEATH)) { plr->SetRuneCooldown(i, RUNE_COOLDOWN); // 5*2=10 sec + plr->SetLastUsedRune(RuneType(rune)); runeCost[rune]--; + bool auraFound = false; plr->ConvertRune(i, plr->GetBaseRune(i)); + // * * * * * * * * * * * + // update convert rune auras + // * * * * * * * * * * * + // Remove rune from SPELL_AURA_CONVERT_RUNE when rune is used + // To prevent overriding other rune convert effects + Unit::AuraEffectList const& runeconvert = m_caster->GetAurasByType(SPELL_AURA_CONVERT_RUNE); + for(Unit::AuraEffectList::const_iterator itr = runeconvert.begin(); itr != runeconvert.end(); ++itr) + { + // Remove rune of aura if avalible + if ((*itr)->GetAmount() & (1<<i)) + { + (*itr)->SetAmount((*itr)->GetAmount() & ~(1<<i)); + auraFound = true; + } + // All runes from aura used - remove aura + if (!(*itr)->GetAmount()) + plr->RemoveAura((*itr)->GetParentAura(), AURA_REMOVE_BY_EXPIRE); + break; + } + if (!auraFound) + { + // Decrease used rune count for dk talent auras + // To prevent overriding other rune convert effects + Unit::AuraEffectList const& runeconvert = m_caster->GetAurasByType(SPELL_AURA_CONVERT_RUNE); + for(Unit::AuraEffectList::const_iterator itr = runeconvert.begin(); itr != runeconvert.end(); ++itr) + { + if (plr->GetBaseRune(i) != RUNE_DEATH) + { + if ((*itr)->GetSpellProto()->SpellIconID != 2622) + continue; + } + else if ((*itr)->GetSpellProto()->SpellIconID != 3041 && + (*itr)->GetSpellProto()->SpellIconID != 22) + continue; + + // Remove rune of aura if avalible + if ((*itr)->GetAmount() & (1<<i)) + (*itr)->SetAmount((*itr)->GetAmount() & ~(1<<i)); + break; + } + } + if(runeCost[RUNE_DEATH] == 0) break; } @@ -3976,13 +4266,14 @@ void Spell::HandleThreatSpells(uint32 spellId) if(!m_targets.getUnitTarget()->CanHaveThreatList()) return; - SpellThreatEntry const *threatSpell = sSpellThreatStore.LookupEntry<SpellThreatEntry>(spellId); - if(!threatSpell) + uint16 threat = spellmgr.GetSpellThreat(spellId); + + if(!threat) return; - m_targets.getUnitTarget()->AddThreat(m_caster, float(threatSpell->threat)); + m_targets.getUnitTarget()->AddThreat(m_caster, float(threat)); - DEBUG_LOG("Spell %u, rank %u, added an additional %i threat", spellId, spellmgr.GetSpellRank(spellId), threatSpell->threat); + DEBUG_LOG("Spell %u, rank %u, added an additional %i threat", spellId, spellmgr.GetSpellRank(spellId), threat); } void Spell::HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTarget,uint32 i) @@ -4021,15 +4312,19 @@ void Spell::TriggerSpell() SpellCastResult Spell::CheckCast(bool strict) { // check cooldowns to prevent cheating - if(m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->HasSpellCooldown(m_spellInfo->Id)) + if(m_caster->GetTypeId()==TYPEID_PLAYER) { //can cast triggered (by aura only?) spells while have this flag if (!m_IsTriggeredSpell && ((Player*)m_caster)->HasFlag(PLAYER_FLAGS, PLAYER_ALLOW_ONLY_ABILITY)) return SPELL_FAILED_SPELL_IN_PROGRESS; - if(m_triggeredByAuraSpell) - return SPELL_FAILED_DONT_REPORT; - else - return SPELL_FAILED_NOT_READY; + + if (((Player*)m_caster)->HasSpellCooldown(m_spellInfo->Id)) + { + if(m_triggeredByAuraSpell) + return SPELL_FAILED_DONT_REPORT; + else + return SPELL_FAILED_NOT_READY; + } } // only allow triggered spells if at an ended battleground @@ -4118,7 +4413,12 @@ SpellCastResult Spell::CheckCast(bool strict) if(m_spellInfo->excludeTargetAuraSpell && target->HasAura(m_spellInfo->excludeTargetAuraSpell)) return SPELL_FAILED_TARGET_AURASTATE; - if(target != m_caster) + if(!m_IsTriggeredSpell && target == m_caster && m_spellInfo->AttributesEx & SPELL_ATTR_EX_CANT_TARGET_SELF) + return SPELL_FAILED_BAD_TARGETS; + + bool non_caster_target = target != m_caster && !spellmgr.IsSpellWithCasterSourceTargetsOnly(m_spellInfo); + + if(non_caster_target) { // target state requirements (apply to non-self only), to allow cast affects to self like Dirty Deeds if(!m_IsTriggeredSpell && m_spellInfo->TargetAuraState && !target->HasAuraState(AuraState(m_spellInfo->TargetAuraState), m_spellInfo, m_caster)) @@ -4131,13 +4431,6 @@ SpellCastResult Spell::CheckCast(bool strict) if(!m_IsTriggeredSpell && VMAP::VMapFactory::checkSpellForLoS(m_spellInfo->Id) && !m_caster->IsWithinLOSInMap(target)) return SPELL_FAILED_LINE_OF_SIGHT; - // auto selection spell rank implemented in WorldSession::HandleCastSpellOpcode - // this case can be triggered if rank not found (too low-level target for first rank) - if(m_caster->GetTypeId() == TYPEID_PLAYER && !IsPassiveSpell(m_spellInfo->Id) && !m_CastItem) - for(int i=0;i<3;i++) - if(IsPositiveEffect(m_spellInfo->Id, i) && m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA) - if(target->getLevel() + 10 < m_spellInfo->spellLevel) - return SPELL_FAILED_LOWLEVEL; } else if (m_caster->GetTypeId() == TYPEID_PLAYER) // Target - is player caster { @@ -4173,7 +4466,7 @@ SpellCastResult Spell::CheckCast(bool strict) //check creature type //ignore self casts (including area casts when caster selected as target) - if(target != m_caster) + if(non_caster_target) { if(!CheckTargetCreatureType(target)) { @@ -4186,7 +4479,7 @@ SpellCastResult Spell::CheckCast(bool strict) // TODO: this check can be applied and for player to prevent cheating when IsPositiveSpell will return always correct result. // check target for pet/charmed casts (not self targeted), self targeted cast used for area effects and etc - if(m_caster != target && m_caster->GetTypeId() == TYPEID_UNIT && m_caster->GetCharmerOrOwnerGUID()) + if(non_caster_target && m_caster->GetTypeId() == TYPEID_UNIT && m_caster->GetCharmerOrOwnerGUID()) { // check correctness positive/negative cast target (pet cast real check and cheating check) if(IsPositiveSpell(m_spellInfo->Id)) @@ -4225,7 +4518,7 @@ SpellCastResult Spell::CheckCast(bool strict) } // check if target is in combat - if (target != m_caster && (m_spellInfo->AttributesEx & SPELL_ATTR_EX_NOT_IN_COMBAT_TARGET) && target->isInCombat()) + if (non_caster_target && (m_spellInfo->AttributesEx & SPELL_ATTR_EX_NOT_IN_COMBAT_TARGET) && target->isInCombat()) return SPELL_FAILED_TARGET_AFFECTING_COMBAT; } @@ -4442,12 +4735,23 @@ SpellCastResult Spell::CheckCast(bool strict) if(m_targets.getUnitTarget() && !m_caster->IsFriendlyTo(m_targets.getUnitTarget()) && !m_caster->HasInArc( M_PI, m_targets.getUnitTarget() )) return SPELL_FAILED_UNIT_NOT_INFRONT; } + else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && m_spellInfo->SpellFamilyFlags[0] == 0x2000) // Death Coil (DeathKnight) + { + Unit* target = m_targets.getUnitTarget(); + if (!target || (target->IsFriendlyTo(m_caster) && target->GetCreatureType() != CREATURE_TYPE_UNDEAD)) + return SPELL_FAILED_BAD_TARGETS; + } else if (m_spellInfo->Id == 19938) // Awaken Peon { Unit *unit = m_targets.getUnitTarget(); if(!unit || !unit->HasAura(17743, 0)) return SPELL_FAILED_BAD_TARGETS; } + else if (m_spellInfo->Id == 52264) // Deliver Stolen Horse + { + if(!m_caster->FindNearestCreature(28653,5)) + return SPELL_FAILED_OUT_OF_RANGE; + } break; } case SPELL_EFFECT_LEARN_SPELL: @@ -4492,6 +4796,14 @@ SpellCastResult Spell::CheckCast(bool strict) break; } + case SPELL_EFFECT_APPLY_GLYPH: + { + uint32 glyphId = m_spellInfo->EffectMiscValue[i]; + if(GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyphId)) + if(m_caster->HasAura(gp->SpellId)) + return SPELL_FAILED_UNIQUE_GLYPH; + break; + } case SPELL_EFFECT_FEED_PET: { if (m_caster->GetTypeId() != TYPEID_PLAYER) @@ -4529,9 +4841,14 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_EFFECT_CHARGE: { + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR) + { + // Warbringer - can't be handled in proc system - should be done before checkcast root check and charge effect process + if (strict && m_caster->IsScriptOverriden(m_spellInfo, 6953)) + m_caster->RemoveMovementImpairingAuras(); + } if (m_caster->hasUnitState(UNIT_STAT_ROOT)) return SPELL_FAILED_ROOTED; - break; } case SPELL_EFFECT_SKINNING: @@ -4564,7 +4881,6 @@ SpellCastResult Spell::CheckCast(bool strict) break; } - case SPELL_EFFECT_OPEN_LOCK_ITEM: case SPELL_EFFECT_OPEN_LOCK: { if( m_spellInfo->EffectImplicitTargetA[i] != TARGET_GAMEOBJECT && @@ -4588,7 +4904,7 @@ SpellCastResult Spell::CheckCast(bool strict) uint32 lockId = 0; if (GameObject* go = m_targets.getGOTarget()) { - lockId = go->GetLockId(); + lockId = go->GetGOInfo()->GetLockId(); if (!lockId) return SPELL_FAILED_BAD_TARGETS; } @@ -4762,27 +5078,34 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_AURA_MOD_POSSESS: case SPELL_AURA_MOD_CHARM: - //case SPELL_AURA_MOD_POSSESS_PET: + case SPELL_AURA_MOD_POSSESS_PET: + case SPELL_AURA_AOE_CHARM: { - if(m_caster->GetPetGUID()) - return SPELL_FAILED_ALREADY_HAVE_SUMMON; - - if(m_caster->GetCharmGUID()) - return SPELL_FAILED_ALREADY_HAVE_CHARM; - if(m_caster->GetCharmerGUID()) return SPELL_FAILED_CHARMED; - Unit *target = m_targets.getUnitTarget(); - if(!target || target->GetTypeId() == TYPEID_UNIT - && ((Creature*)target)->isVehicle()) - return SPELL_FAILED_BAD_IMPLICIT_TARGETS; + if(m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_CHARM + || m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_POSSESS) + { + if(m_caster->GetPetGUID()) + return SPELL_FAILED_ALREADY_HAVE_SUMMON; - if(target->GetCharmerGUID()) - return SPELL_FAILED_CHARMED; + if(m_caster->GetCharmGUID()) + return SPELL_FAILED_ALREADY_HAVE_CHARM; + } + + if(Unit *target = m_targets.getUnitTarget()) + { + if(target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->isVehicle()) + return SPELL_FAILED_BAD_IMPLICIT_TARGETS; + + if(target->GetCharmerGUID()) + return SPELL_FAILED_CHARMED; - if(int32(target->getLevel()) > CalculateDamage(i, target)) - return SPELL_FAILED_HIGHLEVEL; + int32 damage = CalculateDamage(i, target); + if(damage && int32(target->getLevel()) > damage) + return SPELL_FAILED_HIGHLEVEL; + } break; } @@ -4818,14 +5141,15 @@ SpellCastResult Spell::CheckCast(bool strict) break; } - case SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED: case SPELL_AURA_FLY: + case SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED: { - // not allow cast fly spells at old maps by players (all spells is self target) - if(m_originalCaster && m_originalCaster->GetTypeId()==TYPEID_PLAYER) + // not allow cast fly spells if not have req. skills (all spells is self target) + // allow always ghost flight spells + if (m_originalCaster && m_originalCaster->GetTypeId() == TYPEID_PLAYER && m_originalCaster->isAlive()) { - if( !((Player*)m_originalCaster)->IsAllowUseFlyMountsHere() ) - return SPELL_FAILED_NOT_HERE; + if (!((Player*)m_originalCaster)->IsKnowHowFlyIn(m_originalCaster->GetMapId(),m_originalCaster->GetZoneId())) + return m_IsTriggeredSpell ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_NOT_HERE; } break; } @@ -5092,81 +5416,13 @@ SpellCastResult Spell::CheckRange(bool strict) { if(!m_caster->IsWithinDist3d(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, max_range)) return SPELL_FAILED_OUT_OF_RANGE; - if(m_caster->IsWithinDist3d(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, min_range)) + if(min_range && m_caster->IsWithinDist3d(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, min_range)) return SPELL_FAILED_TOO_CLOSE; } return SPELL_CAST_OK; } -int32 Spell::CalculatePowerCost() -{ - // item cast not used power - if(m_CastItem) - return 0; - - // Spell drain all exist power on cast (Only paladin lay of Hands) - if (m_spellInfo->AttributesEx & SPELL_ATTR_EX_DRAIN_ALL_POWER) - { - // If power type - health drain all - if (m_spellInfo->powerType == POWER_HEALTH) - return m_caster->GetHealth(); - // Else drain all power - if (m_spellInfo->powerType < MAX_POWERS) - return m_caster->GetPower(Powers(m_spellInfo->powerType)); - sLog.outError("Spell::CalculateManaCost: Unknown power type '%d' in spell %d", m_spellInfo->powerType, m_spellInfo->Id); - return 0; - } - - // Base powerCost - int32 powerCost = m_spellInfo->manaCost; - // PCT cost from total amount - if (m_spellInfo->ManaCostPercentage) - { - switch (m_spellInfo->powerType) - { - // health as power used - case POWER_HEALTH: - powerCost += m_spellInfo->ManaCostPercentage * m_caster->GetCreateHealth() / 100; - break; - case POWER_MANA: - powerCost += m_spellInfo->ManaCostPercentage * m_caster->GetCreateMana() / 100; - break; - case POWER_RAGE: - case POWER_FOCUS: - case POWER_ENERGY: - case POWER_HAPPINESS: - powerCost += m_spellInfo->ManaCostPercentage * m_caster->GetMaxPower(Powers(m_spellInfo->powerType)) / 100; - break; - case POWER_RUNE: - case POWER_RUNIC_POWER: - sLog.outDebug("Spell::CalculateManaCost: Not implemented yet!"); - break; - default: - sLog.outError("Spell::CalculateManaCost: Unknown power type '%d' in spell %d", m_spellInfo->powerType, m_spellInfo->Id); - return 0; - } - } - SpellSchools school = GetFirstSchoolInMask(m_spellSchoolMask); - // Flat mod from caster auras by spell school - powerCost += m_caster->GetInt32Value(UNIT_FIELD_POWER_COST_MODIFIER + school); - // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost) - if ( m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_SPELL_VS_EXTEND_COST ) - powerCost += m_caster->GetAttackTime(OFF_ATTACK)/100; - // Apply cost mod by spell - if(Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, powerCost, this); - - if(m_spellInfo->Attributes & SPELL_ATTR_LEVEL_DAMAGE_CALCULATION) - powerCost = int32(powerCost/ (1.117f* m_spellInfo->spellLevel / m_caster->getLevel() -0.1327f)); - - // PCT mod from user auras by school - powerCost = int32(powerCost * (1.0f+m_caster->GetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+school))); - if (powerCost < 0) - powerCost = 0; - return powerCost; -} - SpellCastResult Spell::CheckPower() { // item cast not used power @@ -5310,7 +5566,7 @@ SpellCastResult Spell::CheckItems() TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck>, GridTypeMapContainer > object_checker(checker); CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster)); + cell_lock->Visit(cell_lock, object_checker, *m_caster->GetMap()); if(!ok) return SPELL_FAILED_REQUIRES_SPELL_FOCUS; @@ -5567,7 +5823,7 @@ SpellCastResult Spell::CheckItems() if(!ammo) { // Requires No Ammo - if(m_caster->GetDummyAura(46699)) + if(m_caster->HasAura(46699)) break; // skip other checks return SPELL_FAILED_NO_AMMO; @@ -5758,8 +6014,10 @@ bool Spell::CheckTargetCreatureType(Unit* target) const { uint32 spellCreatureTargetMask = m_spellInfo->TargetCreatureType; - // Curse of Doom : not find another way to fix spell target check :/ - if(m_spellInfo->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags.IsEqual(0,0x02,0)) + // Curse of Doom & Exorcism: not find another way to fix spell target check :/ + if (m_spellInfo->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellInfo->Category == 1179 || + // TODO: will be removed in 3.2.x + m_spellInfo->SpellFamilyName==SPELLFAMILY_PALADIN && m_spellInfo->Category == 19) { // not allow cast at player if(target->GetTypeId()==TYPEID_PLAYER) @@ -5834,7 +6092,26 @@ bool Spell::CheckTarget(Unit* target, uint32 eff) return false; } - //Do not check LOS for triggered spells + switch(m_spellInfo->EffectApplyAuraName[eff]) + { + case SPELL_AURA_NONE: + default: + break; + case SPELL_AURA_MOD_POSSESS: + case SPELL_AURA_MOD_CHARM: + case SPELL_AURA_MOD_POSSESS_PET: + case SPELL_AURA_AOE_CHARM: + if(target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->isVehicle()) + return false; + if(target->GetCharmerGUID()) + return false; + if(int32 damage = CalculateDamage(eff, target)) + if((int32)target->getLevel() > damage) + return false; + break; + } + + //Do not do further checks for triggered spells if(m_IsTriggeredSpell) return true; @@ -6115,7 +6392,7 @@ void Spell::CalculateDamageDoneForAllTargets() if (target.missCondition==SPELL_MISS_NONE) // In case spell hit target, do all effect on that target { - target.damage += CalculateDamageDone(unit, mask, multiplier); + target.damage += CalculateDamageDone(unit, mask, multiplier); target.crit = m_caster->isSpellCrit(unit, m_spellInfo, m_spellSchoolMask, m_attackType); } else if (target.missCondition == SPELL_MISS_REFLECT) // In case spell reflect from target, do all effect on caster (if hit) diff --git a/src/game/Spell.h b/src/game/Spell.h index 6aa539808df..b593bf79881 100644 --- a/src/game/Spell.h +++ b/src/game/Spell.h @@ -263,11 +263,11 @@ class Spell void EffectQuestComplete(uint32 i); void EffectCreateItem(uint32 i); void EffectCreateItem2(uint32 i); + void EffectCreateRandomItem(uint32 i); void EffectPersistentAA(uint32 i); void EffectEnergize(uint32 i); void EffectOpenLock(uint32 i); void EffectSummonChangeItem(uint32 i); - void EffectOpenSecretSafe(uint32 i); void EffectProficiency(uint32 i); void EffectApplyAreaAura(uint32 i); void EffectSummonType(uint32 i); @@ -319,6 +319,7 @@ class Spell void EffectSelfResurrect(uint32 i); void EffectSkinning(uint32 i); void EffectCharge(uint32 i); + void EffectCharge2(uint32 i); void EffectProspecting(uint32 i); void EffectMilling(uint32 i); void EffectRenamePet(uint32 i); @@ -353,6 +354,7 @@ class Spell void EffectActivateRune(uint32 i); void EffectTitanGrip(uint32 i); void EffectEnchantItemPrismatic(uint32 i); + void EffectPlayMusic(uint32 i); typedef std::set<Aura *> UsedSpellMods; @@ -389,7 +391,6 @@ class Spell SpellCastResult CheckCasterAuras() const; int32 CalculateDamage(uint8 i, Unit* target) { return m_caster->CalculateSpellDamage(m_spellInfo,i,m_currentBasePoints[i],target); } - int32 CalculatePowerCost(); bool HaveTargetsForEffect(uint8 effect) const; void Delayed(); @@ -438,6 +439,7 @@ class Spell uint32 m_glyphIndex; uint32 m_preCastSpell; SpellCastTargets m_targets; + int8 m_comboPointGain; UsedSpellMods m_appliedMods; @@ -567,9 +569,10 @@ class Spell SpellMissInfo reflectResult:8; uint8 effectMask:8; bool processed:1; - bool alive:1; + bool alive:1; + bool crit:1; + bool scaleAura:1; int32 damage; - bool crit; }; std::list<TargetInfo> m_UniqueTargetInfo; uint8 m_needAliveTargetMask; // Mask req. alive targets @@ -596,7 +599,7 @@ class Spell void AddGOTarget(uint64 goGUID, uint32 effIndex); void AddItemTarget(Item* target, uint32 effIndex); void DoAllEffectOnTarget(TargetInfo *target); - SpellMissInfo DoSpellHitOnUnit(Unit *unit, uint32 effectMask); + SpellMissInfo DoSpellHitOnUnit(Unit *unit, uint32 effectMask, bool scaleAura); void DoTriggersOnSpellHit(Unit *unit); void DoAllEffectOnTarget(GOTargetInfo *target); void DoAllEffectOnTarget(ItemTargetInfo *target); @@ -637,6 +640,7 @@ class Spell uint32 m_customAttr; bool m_skipCheck; uint32 m_effectMask; + uint8 m_auraScaleMask; #ifdef MAP_BASED_RAND_GEN int32 irand(int32 min, int32 max) { return int32 (m_caster->GetMap()->mtRand.randInt(max - min)) + min; } diff --git a/src/game/SpellAuraDefines.h b/src/game/SpellAuraDefines.h index f38bd455896..1a116173135 100644 --- a/src/game/SpellAuraDefines.h +++ b/src/game/SpellAuraDefines.h @@ -250,7 +250,7 @@ enum AuraType SPELL_AURA_IGNORE_COMBAT_RESULT = 202, SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE = 203, SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE = 204, - SPELL_AURA_205 = 205, // school vunderability? + SPELL_AURA_MOD_SCHOOL_CRIT_DMG_TAKEN = 205, SPELL_AURA_MOD_INCREASE_VEHICLE_FLIGHT_SPEED = 206, SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED = 207, SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED = 208, @@ -266,7 +266,7 @@ enum AuraType SPELL_AURA_HASTE_RANGED = 218, SPELL_AURA_MOD_MANA_REGEN_FROM_STAT = 219, SPELL_AURA_MOD_RATING_FROM_STAT = 220, - SPELL_AURA_221 = 221, + SPELL_AURA_MOD_DETAUNT = 221, SPELL_AURA_222 = 222, SPELL_AURA_RAID_PROC_FROM_CHARGE = 223, SPELL_AURA_224 = 224, @@ -292,7 +292,7 @@ enum AuraType SPELL_AURA_COMPREHEND_LANGUAGE = 244, SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL = 245, SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL_NOT_STACK = 246, - SPELL_AURA_247 = 247, + SPELL_AURA_CLONE_CASTER = 247, SPELL_AURA_MOD_COMBAT_RESULT_CHANCE = 248, SPELL_AURA_CONVERT_RUNE = 249, SPELL_AURA_MOD_INCREASE_HEALTH_2 = 250, @@ -324,14 +324,14 @@ enum AuraType SPELL_AURA_276 = 276, // Only "Test Mod Damage % Mechanic" spell, possible mod damage done SPELL_AURA_MOD_MAX_AFFECTED_TARGETS = 277, SPELL_AURA_MOD_DISARM_RANGED = 278, - SPELL_AURA_279 = 279, + SPELL_AURA_INITIALIZE_IMAGES = 279, SPELL_AURA_MOD_ARMOR_PENETRATION_PCT = 280, SPELL_AURA_MOD_HONOR_GAIN_PCT = 281, SPELL_AURA_MOD_BASE_HEALTH_PCT = 282, SPELL_AURA_MOD_HEALING_RECEIVED = 283, // Possibly only for some spell family class spells SPELL_AURA_LINKED = 284, SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR = 285, - SPELL_AURA_286, + SPELL_AURA_ABILITY_PERIODIC_CRIT = 286, SPELL_AURA_DEFLECT_SPELLS, SPELL_AURA_288, SPELL_AURA_289, diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 9c54bf7e27b..c303e5cdb11 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -74,7 +74,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleInvisibility, // 18 SPELL_AURA_MOD_INVISIBILITY &Aura::HandleInvisibilityDetect, // 19 SPELL_AURA_MOD_INVISIBILITY_DETECTION &Aura::HandleAuraModTotalHealthPercentRegen, // 20 SPELL_AURA_OBS_MOD_HEALTH - &Aura::HandleAuraModTotalEnergyPercentRegen, // 21 SPELL_AURA_OBS_MOD_ENERGY + &Aura::HandleAuraModTotalEnergyPercentRegen, // 21 SPELL_AURA_OBS_MOD_ENERGY &Aura::HandleAuraModResistance, // 22 SPELL_AURA_MOD_RESISTANCE &Aura::HandlePeriodicTriggerSpell, // 23 SPELL_AURA_PERIODIC_TRIGGER_SPELL &Aura::HandlePeriodicEnergize, // 24 SPELL_AURA_PERIODIC_ENERGIZE @@ -223,7 +223,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleAuraModRangedAttackPowerPercent, //167 SPELL_AURA_MOD_RANGED_ATTACK_POWER_PCT &Aura::HandleNoImmediateEffect, //168 SPELL_AURA_MOD_DAMAGE_DONE_VERSUS implemented in Unit::SpellDamageBonus, Unit::MeleeDamageBonus &Aura::HandleNoImmediateEffect, //169 SPELL_AURA_MOD_CRIT_PERCENT_VERSUS implemented in Unit::DealDamageBySchool, Unit::DoAttackDamage, Unit::SpellCriticalBonus - &Aura::HandleNULL, //170 SPELL_AURA_DETECT_AMORE different spells that ignore transformation effects + &Aura::HandleNULL, //170 SPELL_AURA_DETECT_AMORE various spells that change visual of units for aura target (clientside?) &Aura::HandleAuraModIncreaseSpeed, //171 SPELL_AURA_MOD_SPEED_NOT_STACK &Aura::HandleAuraModIncreaseMountedSpeed, //172 SPELL_AURA_MOD_MOUNTED_SPEED_NOT_STACK &Aura::HandleUnused, //173 unused (3.0.8a) no spells, old SPELL_AURA_ALLOW_CHAMPION_SPELLS only for Proclaim Champion spell @@ -236,7 +236,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleNoImmediateEffect, //180 SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS implemented in Unit::SpellDamageBonus &Aura::HandleUnused, //181 unused (3.0.8a) old SPELL_AURA_MOD_FLAT_SPELL_CRIT_DAMAGE_VERSUS &Aura::HandleAuraModResistenceOfStatPercent, //182 SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT - &Aura::HandleNULL, //183 SPELL_AURA_MOD_CRITICAL_THREAT only used in 28746 + &Aura::HandleNULL, //183 SPELL_AURA_MOD_CRITICAL_THREAT only used in 28746 - miscvalue - spell school &Aura::HandleNoImmediateEffect, //184 SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst &Aura::HandleNoImmediateEffect, //185 SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst &Aura::HandleNoImmediateEffect, //186 SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE implemented in Unit::MagicSpellHitResult @@ -247,9 +247,9 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleAuraModUseNormalSpeed, //191 SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED &Aura::HandleModMeleeRangedSpeedPct, //192 SPELL_AURA_HASTE_MELEE &Aura::HandleModCombatSpeedPct, //193 SPELL_AURA_MELEE_SLOW (in fact combat (any type attack) speed pct) - &Aura::HandleNULL, //194 SPELL_AURA_MOD_TARGET_ABSORB_SCHOOL implemented in Unit::CalcAbsorbResist + &Aura::HandleNoImmediateEffect, //194 SPELL_AURA_MOD_TARGET_ABSORB_SCHOOL implemented in Unit::CalcAbsorbResist &Aura::HandleNoImmediateEffect, //195 SPELL_AURA_MOD_TARGET_ABILITY_ABSORB_SCHOOL implemented in Unit::CalcAbsorbResist - &Aura::HandleNULL, //196 SPELL_AURA_MOD_COOLDOWN + &Aura::HandleNULL, //196 SPELL_AURA_MOD_COOLDOWN - flat mod of spell cooldowns &Aura::HandleNoImmediateEffect, //197 SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE implemented in Unit::SpellCriticalBonus Unit::GetUnitCriticalChance &Aura::HandleUnused, //198 unused (3.0.8a) old SPELL_AURA_MOD_ALL_WEAPON_SKILLS &Aura::HandleNoImmediateEffect, //199 SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT implemented in Unit::MagicSpellHitResult @@ -258,7 +258,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleNoImmediateEffect, //202 SPELL_AURA_CANNOT_BE_DODGED implemented in Unit::RollPhysicalOutcomeAgainst &Aura::HandleNoImmediateEffect, //203 SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE implemented in Unit::CalculateMeleeDamage and Unit::CalculateSpellDamage &Aura::HandleNoImmediateEffect, //204 SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE implemented in Unit::CalculateMeleeDamage and Unit::CalculateSpellDamage - &Aura::HandleNULL, //205 vulnerable to school dmg? + &Aura::HandleNULL, //205 SPELL_AURA_MOD_SCHOOL_CRIT_DMG_TAKEN &Aura::HandleAuraModIncreaseFlightSpeed, //206 SPELL_AURA_MOD_INCREASE_VEHICLE_FLIGHT_SPEED &Aura::HandleAuraModIncreaseFlightSpeed, //207 SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED &Aura::HandleAuraModIncreaseFlightSpeed, //208 SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED @@ -274,19 +274,19 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleAuraModRangedHaste, //218 SPELL_AURA_HASTE_RANGED &Aura::HandleModManaRegen, //219 SPELL_AURA_MOD_MANA_REGEN_FROM_STAT &Aura::HandleModRatingFromStat, //220 SPELL_AURA_MOD_RATING_FROM_STAT - &Aura::HandleNULL, //221 ignored + &Aura::HandleNULL, //221 SPELL_AURA_MOD_DETAUNT &Aura::HandleUnused, //222 unused (3.0.8a) only for spell 44586 that not used in real spell cast &Aura::HandleNoImmediateEffect, //223 SPELL_AURA_RAID_PROC_FROM_CHARGE &Aura::HandleUnused, //224 unused (3.0.8a) &Aura::HandleNoImmediateEffect, //225 SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE &Aura::HandleAuraPeriodicDummy, //226 SPELL_AURA_PERIODIC_DUMMY &Aura::HandlePeriodicTriggerSpellWithValue, //227 SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE - &Aura::HandleNoImmediateEffect, //228 stealth detection - &Aura::HandleNULL, //229 SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE + &Aura::HandleNoImmediateEffect, //228 SPELL_AURA_DETECT_STEALTH stealth detection + &Aura::HandleNoImmediateEffect, //229 SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE &Aura::HandleAuraModIncreaseHealth, //230 SPELL_AURA_MOD_INCREASE_HEALTH_2 &Aura::HandleNoImmediateEffect, //231 SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE &Aura::HandleNoImmediateEffect, //232 SPELL_AURA_MECHANIC_DURATION_MOD implement in Unit::CalculateSpellDuration - &Aura::HandleNULL, //233 set model id to the one of the creature with id GetMiscValue() + &Aura::HandleUnused, //233 set model id to the one of the creature with id GetMiscValue() - clientside &Aura::HandleNoImmediateEffect, //234 SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK implement in Unit::CalculateSpellDuration &Aura::HandleNoImmediateEffect, //235 SPELL_AURA_MOD_DISPEL_RESIST implement in Unit::MagicSpellHitResult &Aura::HandleAuraControlVehicle, //236 SPELL_AURA_CONTROL_VEHICLE @@ -294,18 +294,18 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleModSpellHealingPercentFromAttackPower, //238 SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER implemented in Unit::SpellBaseHealingBonus &Aura::HandleAuraModScale, //239 SPELL_AURA_MOD_SCALE_2 only in Noggenfogger Elixir (16595) before 2.3.0 aura 61 &Aura::HandleAuraModExpertise, //240 SPELL_AURA_MOD_EXPERTISE - &Aura::HandleForceMoveForward, //241 Forces the player to move forward - &Aura::HandleUnused, //242 SPELL_AURA_MOD_SPELL_DAMAGE_FROM_HEALING + &Aura::HandleForceMoveForward, //241 SPELL_AURA_FORCE_MOVE_FORWARD Forces the player to move forward + &Aura::HandleUnused, //242 SPELL_AURA_MOD_SPELL_DAMAGE_FROM_HEALING - 2 test spells: 44183 and 44182 &Aura::HandleNULL, //243 faction reaction override spells - &Aura::HandleComprehendLanguage, //244 Comprehend language + &Aura::HandleComprehendLanguage, //244 SPELL_AURA_COMPREHEND_LANGUAGE &Aura::HandleNoImmediateEffect, //245 SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL &Aura::HandleNoImmediateEffect, //246 SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL_NOT_STACK implemented in Spell::EffectApplyAura - &Aura::HandleNULL, //247 target to become a clone of the caster + &Aura::HandleAuraCloneCaster, //247 SPELL_AURA_CLONE_CASTER &Aura::HandleNoImmediateEffect, //248 SPELL_AURA_MOD_COMBAT_RESULT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst &Aura::HandleAuraConvertRune, //249 SPELL_AURA_CONVERT_RUNE &Aura::HandleAuraModIncreaseHealth, //250 SPELL_AURA_MOD_INCREASE_HEALTH_2 &Aura::HandleNoImmediateEffect, //251 SPELL_AURA_MOD_ENEMY_DODGE - &Aura::HandleNULL, //252 haste all? + &Aura::HandleModCombatSpeedPct, //252 SPELL_AURA_252 Is there any difference between this and SPELL_AURA_MELEE_SLOW ? maybe not stacking mod? &Aura::HandleNoImmediateEffect, //253 SPELL_AURA_MOD_BLOCK_CRIT_CHANCE implemented in Unit::isBlockCritical &Aura::HandleAuraModDisarm, //254 SPELL_AURA_MOD_DISARM_OFFHAND &Aura::HandleNoImmediateEffect, //255 SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT implemented in Unit::SpellDamageBonus @@ -325,26 +325,26 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleNoImmediateEffect, //269 SPELL_AURA_MOD_IGNORE_TARGET_RESIST implemented in Unit::CalcAbsorbResist and CalcArmorReducedDamage &Aura::HandleNoImmediateEffect, //270 SPELL_AURA_MOD_ABILITY_IGNORE_TARGET_RESIST implemented in Unit::CalcAbsorbResist and CalcArmorReducedDamage &Aura::HandleNoImmediateEffect, //271 SPELL_AURA_MOD_DAMAGE_FROM_CASTER implemented in Unit::SpellDamageBonus - &Aura::HandleNULL, //272 reduce spell cast time? + &Aura::HandleNULL, //272 unknown &Aura::HandleUnused, //273 clientside &Aura::HandleNoImmediateEffect, //274 SPELL_AURA_CONSUME_NO_AMMO implemented in spell::CalculateDamageDoneForAllTargets &Aura::HandleNoImmediateEffect, //275 SPELL_AURA_MOD_IGNORE_SHAPESHIFT Use SpellClassMask for spell select &Aura::HandleNULL, //276 mod damage % mechanic? &Aura::HandleNoImmediateEffect, //277 SPELL_AURA_MOD_ABILITY_AFFECTED_TARGETS implemented in spell::settargetmap &Aura::HandleAuraModDisarm, //278 SPELL_AURA_MOD_DISARM_RANGED disarm ranged weapon - &Aura::HandleNULL, //279 visual effects? 58836 and 57507 + &Aura::HandleAuraInitializeImages, //279 SPELL_AURA_INITIALIZE_IMAGES &Aura::HandleModArmorPenetrationPct, //280 SPELL_AURA_MOD_ARMOR_PENETRATION_PCT &Aura::HandleNoImmediateEffect, //281 SPELL_AURA_MOD_HONOR_GAIN_PCT implemented in Player::RewardHonor &Aura::HandleAuraIncreaseBaseHealthPercent, //282 SPELL_AURA_INCREASE_BASE_HEALTH_PERCENT &Aura::HandleNoImmediateEffect, //283 SPELL_AURA_MOD_HEALING_RECEIVED implemented in Unit::SpellHealingBonus - &Aura::HandleUnused, //284 not used by any spells (3.08a) + &Aura::HandleNULL, //284 SPELL_AURA_LINKED - probably not sent to client &Aura::HandleAuraModAttackPowerOfArmor, //285 SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR implemented in Player::UpdateAttackPowerAndDamage - &Aura::HandleUnused, //286 not used by any spells (3.08a) + &Aura::HandleNoImmediateEffect, //286 SPELL_AURA_ABILITY_PERIODIC_CRIT implemented in AuraEffect::PeriodicTick &Aura::HandleNoImmediateEffect, //287 SPELL_AURA_DEFLECT_SPELLS implemented in Unit::MagicSpellHitResult and Unit::MeleeSpellHitResult - &Aura::HandleUnused, //288 not used by any spells (3.09) except 1 test spell. + &Aura::HandleUnused, //288 unused &Aura::HandleUnused, //289 unused - &Aura::HandleUnused, //290 unused - &Aura::HandleUnused, //291 unused + &Aura::HandleNULL, //290 mod all critical hit chances? + &Aura::HandleNULL, //291 SPELL_AURA_MOD_XP_QUEST_PCT &Aura::HandleNULL, //292 call stabled pet &Aura::HandleNULL, //293 2 test spells &Aura::HandleNULL //294 2 spells, possible prevent mana regen @@ -354,7 +354,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= Aura::Aura(SpellEntry const* spellproto, uint32 effMask, int32 *currentBasePoints, Unit *target, WorldObject *source, Unit *caster, Item* castItem) : m_caster_guid(0), m_castItemGuid(castItem?castItem->GetGUID():0), m_target(target), m_timeCla(0), m_removeMode(AURA_REMOVE_BY_DEFAULT), m_AuraDRGroup(DIMINISHING_NONE), -m_auraSlot(MAX_AURAS), m_auraLevel(1), m_procCharges(0), m_stackAmount(1),m_auraStateMask(0), m_updated(false), m_isRemoved(false) +m_auraSlot(MAX_AURAS), m_auraLevel(1), m_procCharges(0), m_stackAmount(1), m_updated(false), m_isRemoved(false) { assert(target); @@ -369,8 +369,6 @@ m_auraSlot(MAX_AURAS), m_auraLevel(1), m_procCharges(0), m_stackAmount(1),m_aura m_isPassive = IsPassiveSpell(GetId()); - m_auraStateMask = 0; - m_isSingleTargetAura = IsSingleTargetSpell(m_spellProto); m_applyTime = time(NULL); @@ -400,7 +398,7 @@ m_auraSlot(MAX_AURAS), m_auraLevel(1), m_procCharges(0), m_stackAmount(1),m_aura { // Glyph of Thorns if (m_target == caster && m_spellProto->SpellFamilyName==SPELLFAMILY_DRUID && m_spellProto->SpellFamilyFlags[0] & 0x100) - if (AuraEffect * aurEff = m_target->GetDummyAura(57862)) + if (AuraEffect * aurEff = m_target->GetAuraEffect(57862, 0)) m_maxduration += aurEff->GetAmount() * MINUTE * IN_MILISECONDS; modOwner->ApplySpellMod(GetId(), SPELLMOD_DURATION, m_maxduration); @@ -486,6 +484,9 @@ m_target(parentAura->GetTarget()), m_tickNumber(0) else m_amount = m_currentBasePoints + 1; + if (int32 amount = CalculateCrowdControlAuraAmount(caster)) + m_amount = amount; + if (!m_amount && castItem && castItem->GetItemSuffixFactor()) { ItemRandomSuffixEntry const *item_rand_suffix = sItemRandomSuffixStore.LookupEntry(abs(castItem->GetItemRandomPropertyId())); @@ -766,7 +767,8 @@ void AreaAuraEffect::Update(uint32 diff) case AREA_AURA_PET: { if(Unit *owner = caster->GetCharmerOrOwner()) - targets.push_back(owner); + if (owner->IsWithinDistInMap(source, m_radius)) + targets.push_back(owner); break; } } @@ -836,7 +838,7 @@ void AreaAuraEffect::Update(uint32 diff) } else if (!source->IsWithinDistInMap(m_target, m_radius)) { - if (needFriendly) + if (needFriendly && source->isMoving()) { m_removeTime -= diff; if (m_removeTime < 0) @@ -940,6 +942,191 @@ void Aura::ApplyAllModifiers(bool apply, bool Real) m_partAuras[i]->ApplyModifier(apply, Real); } +void Aura::HandleAuraSpecificMods(bool apply) +{ + if (apply) + { + if(m_spellProto->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT) + { + // Icebound Fortitude + if (m_spellProto->SpellFamilyFlags[0] & 0x00100000) + { + Unit * caster = GetCaster(); + if (caster && caster->GetTypeId() == TYPEID_PLAYER) + { + if(AuraEffect *auraeff = GetPartAura(2)) + { + int32 value = int32((auraeff->GetAmount()*-1)-10); + uint32 defva = uint32(((Player*)caster)->GetSkillValue(SKILL_DEFENSE) + ((Player*)caster)->GetRatingBonusValue(CR_DEFENSE_SKILL)); + + if(defva > 400) + value += int32((defva-400)*0.15); + + // Glyph of Icebound Fortitude + if (AuraEffect *auradummy = caster->GetAuraEffect(58625,0)) + { + uint32 valmax = auradummy->GetAmount(); + if(value < valmax) + value = valmax; + } + auraeff->SetAmount(-value); + } + } + } + } + + if (m_spellProto->SpellFamilyName == SPELLFAMILY_MAGE) + { + if (m_spellProto->SpellFamilyFlags[0] & 0x00000001 && m_spellProto->SpellFamilyFlags[2] & 0x00000008) + { + // Glyph of Fireball + if (Unit * caster = GetCaster()) + if (caster->HasAura(56368)) + SetAuraDuration(0); + } + else if (m_spellProto->SpellFamilyFlags[0] & 0x00000020 && m_spellProto->SpellVisual[0] == 13) + { + // Glyph of Frostbolt + if (Unit * caster = GetCaster()) + if (caster->HasAura(56370)) + SetAuraDuration(0); + } + // Todo: This should be moved to similar function in spell::hit + else if (m_spellProto->SpellFamilyFlags[0] & 0x01000000) + { + Unit * caster = GetCaster(); + if (!caster) + return; + + // Polymorph Sound - Sheep && Penguin + if (m_spellProto->SpellIconID == 82 && m_spellProto->SpellVisual[0] == 12978) + { + // Glyph of the Penguin + if (caster->HasAura(52648)) + caster->CastSpell(m_target,61635,true); + else + caster->CastSpell(m_target,61634,true); + } + } + } + } + + // Aura Mastery Triggered Spell Handler + // If apply Concentration Aura -> trigger -> apply Aura Mastery Immunity + // If remove Concentration Aura -> trigger -> remove Aura Mastery Immunity + // If remove Aura Mastery -> trigger -> remove Aura Mastery Immunity + if (m_spellProto->Id == 19746 || m_spellProto->Id == 31821) + { + if (GetCasterGUID() != m_target->GetGUID()) + return; + if (apply) + { + if ((m_spellProto->Id == 31821 && m_target->HasAura(19746, GetCasterGUID())) || (m_spellProto->Id == 19746 && m_target->HasAura(31821))) + { + m_target->CastSpell(m_target,64364,true); + return; + } + } + else + { + m_target->RemoveAurasDueToSpell(64364, GetCasterGUID()); + return; + } + } + + if (GetSpellSpecific(m_spellProto->Id) == SPELL_PRESENCE) + { + AuraEffect *bloodPresenceAura=0; // healing by damage done + AuraEffect *frostPresenceAura=0; // increased health + AuraEffect *unholyPresenceAura=0; // increased movement speed, faster rune recovery + + // Improved Presences + Unit::AuraEffectList const& vDummyAuras = m_target->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraEffectList::const_iterator itr = vDummyAuras.begin(); itr != vDummyAuras.end(); ++itr) + { + switch((*itr)->GetId()) + { + // Improved Blood Presence + case 50365: + case 50371: + { + bloodPresenceAura = (*itr); + break; + } + // Improved Frost Presence + case 50384: + case 50385: + { + frostPresenceAura = (*itr); + break; + } + // Improved Unholy Presence + case 50391: + case 50392: + { + unholyPresenceAura = (*itr); + break; + } + } + } + + uint32 presence=GetId(); + if (apply) + { + // Blood Presence bonus + if (presence == SPELL_ID_BLOOD_PRESENCE) + m_target->CastSpell(m_target,63611,true); + else if (bloodPresenceAura) + { + int32 basePoints1=bloodPresenceAura->GetAmount(); + m_target->CastCustomSpell(m_target,63611,NULL,&basePoints1,NULL,true,0,bloodPresenceAura); + } + // Frost Presence bonus + if (presence == SPELL_ID_FROST_PRESENCE) + m_target->CastSpell(m_target,61261,true); + else if (frostPresenceAura) + { + int32 basePoints0=frostPresenceAura->GetAmount(); + m_target->CastCustomSpell(m_target,61261,&basePoints0,NULL,NULL,true,0,frostPresenceAura); + } + // Unholy Presence bonus + if (presence == SPELL_ID_UNHOLY_PRESENCE) + { + if(unholyPresenceAura) + { + // Not listed as any effect, only base points set + int32 basePoints0 = unholyPresenceAura->GetSpellProto()->EffectBasePoints[1]; + //m_target->CastCustomSpell(m_target,63622,&basePoints0 ,NULL,NULL,true,0,unholyPresenceAura); + m_target->CastCustomSpell(m_target,65095,&basePoints0 ,NULL,NULL,true,0,unholyPresenceAura); + } + m_target->CastSpell(m_target,49772, true); + } + else if (unholyPresenceAura) + { + int32 basePoints0=unholyPresenceAura->GetAmount(); + m_target->CastCustomSpell(m_target,49772,&basePoints0,NULL,NULL,true,0,unholyPresenceAura); + } + } + else + { + // Remove passive auras + if (presence == SPELL_ID_BLOOD_PRESENCE || bloodPresenceAura) + m_target->RemoveAurasDueToSpell(63611); + if (presence == SPELL_ID_FROST_PRESENCE || frostPresenceAura) + m_target->RemoveAurasDueToSpell(61261); + if (presence == SPELL_ID_UNHOLY_PRESENCE || unholyPresenceAura) + { + if(presence == SPELL_ID_UNHOLY_PRESENCE && unholyPresenceAura) + { + //m_target->RemoveAurasDueToSpell(63622); + m_target->RemoveAurasDueToSpell(65095); + } + m_target->RemoveAurasDueToSpell(49772); + } + } + } +} + void Aura::SendAuraUpdate() { if (m_auraSlot>=MAX_AURAS) @@ -960,7 +1147,7 @@ void Aura::SendAuraUpdate() data << uint32(GetId()); data << uint8(m_auraFlags); data << uint8(m_auraLevel); - data << uint8(m_stackAmount > 1 ? m_stackAmount : m_procCharges); + data << uint8(m_stackAmount > 1 ? m_stackAmount : (m_procCharges) ? m_procCharges : 1); if(!(m_auraFlags & AFLAG_CASTER)) { @@ -1096,57 +1283,7 @@ void Aura::_AddAura() } } - //***************************************************** - // Update target aura state flag - //***************************************************** - - // Update Seals information - if (IsSealSpell(m_spellProto)) - SetAuraState(AURA_STATE_JUDGEMENT); - - // Conflagrate aura state on Immolate or Shadowflame - if (m_spellProto->SpellFamilyName == SPELLFAMILY_WARLOCK && (m_spellProto->SpellFamilyFlags[0] & 4 - || m_spellProto->SpellFamilyFlags[2] & 2)) - SetAuraState(AURA_STATE_IMMOLATE); - - // Faerie Fire (druid versions) - if (m_spellProto->SpellFamilyName == SPELLFAMILY_DRUID && m_spellProto->SpellFamilyFlags[0] & 0x400) - SetAuraState(AURA_STATE_FAERIE_FIRE); - - // Victorious - if (m_spellProto->SpellFamilyName == SPELLFAMILY_WARRIOR && m_spellProto->SpellFamilyFlags[1] & 0x00040000) - SetAuraState(AURA_STATE_WARRIOR_VICTORY_RUSH); - - // Swiftmend state on Regrowth & Rejuvenation - if (m_spellProto->SpellFamilyName == SPELLFAMILY_DRUID && m_spellProto->SpellFamilyFlags[0] & 0x50 ) - SetAuraState(AURA_STATE_SWIFTMEND); - - // Deadly poison aura state - if(m_spellProto->SpellFamilyName == SPELLFAMILY_ROGUE && m_spellProto->SpellFamilyFlags[0] & 0x10000) - SetAuraState(AURA_STATE_DEADLY_POISON); - - // Enrage aura state - if(m_spellProto->Dispel == DISPEL_ENRAGE) - SetAuraState(AURA_STATE_ENRAGE); - - // Bleeding aura state - if (GetAllSpellMechanicMask(m_spellProto) & 1<<MECHANIC_BLEED) - SetAuraState(AURA_STATE_BLEEDING); - - if(GetSpellSchoolMask(m_spellProto) & SPELL_SCHOOL_MASK_FROST) - { - for (uint8 i = 0;i<MAX_SPELL_EFFECTS;++i) - { - if (m_spellProto->EffectApplyAuraName[i]==SPELL_AURA_MOD_STUN - || m_spellProto->EffectApplyAuraName[i]==SPELL_AURA_MOD_ROOT) - { - SetAuraState(AURA_STATE_FROZEN); - break; - } - } - } - - m_target->ApplyModFlag(UNIT_FIELD_AURASTATE, GetAuraStateMask(), true); + HandleAuraSpecificMods(true); } bool Aura::SetPartAura(AuraEffect* aurEff, uint8 effIndex) @@ -1192,22 +1329,6 @@ void Aura::_RemoveAura() if (getDiminishGroup() != DIMINISHING_NONE ) m_target->ApplyDiminishingAura(getDiminishGroup(),false); - // Check needed only if aura applies aurastate - if(GetAuraStateMask()) - { - uint32 foundMask = 0; - Unit::AuraMap& Auras = m_target->GetAuras(); - // Get mask of all aurastates from remaining auras - for(Unit::AuraMap::iterator i = Auras.begin(); i != Auras.end(); ++i) - { - foundMask|=(*i).second->GetAuraStateMask(); - } - // Remove only aurastates which were not found - foundMask = GetAuraStateMask() &~foundMask; - if (foundMask) - m_target->ApplyModFlag(UNIT_FIELD_AURASTATE, foundMask, false); - } - // since now aura cannot apply/remove it's modifiers m_isRemoved = true; // disable client server communication for removed aura @@ -1262,23 +1383,25 @@ void Aura::_RemoveAura() if (procEx) { uint32 ProcCaster, ProcVictim; - if (IsPositiveSpell(GetId())) + if (IsPositive()) { - ProcCaster = PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL; - ProcVictim = PROC_FLAG_TAKEN_POSITIVE_SPELL; + ProcCaster = PROC_FLAG_SUCCESSFUL_POSITIVE_MAGIC_SPELL | PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL_HIT; + ProcVictim = PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL | PROC_FLAG_TAKEN_POSITIVE_SPELL; } else { - ProcCaster = PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT; - ProcVictim = PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT; + ProcCaster = PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL | PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT; + ProcVictim = PROC_FLAG_TAKEN_NEGATIVE_MAGIC_SPELL | PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT; } caster->ProcDamageAndSpell(m_target,ProcCaster, ProcVictim, procEx, m_procDamage, BASE_ATTACK, m_spellProto); } } + HandleAuraSpecificMods(false); } void Aura::SetStackAmount(uint8 stackAmount, bool applied) { + bool refresh = stackAmount >= m_stackAmount; if (stackAmount != m_stackAmount) { m_stackAmount = stackAmount; @@ -1290,7 +1413,11 @@ void Aura::SetStackAmount(uint8 stackAmount, bool applied) } } } - RefreshAura(); + + if (refresh) + RefreshAura(); + else + SendAuraUpdate(); } bool Aura::modStackAmount(int32 num) @@ -1398,6 +1525,7 @@ void AuraEffect::HandleShapeshiftBoosts(bool apply) { uint32 spellId = 0; uint32 spellId2 = 0; + uint32 spellId3 = 0; uint32 HotWSpellId = 0; switch(GetMiscValue()) @@ -1407,8 +1535,7 @@ void AuraEffect::HandleShapeshiftBoosts(bool apply) HotWSpellId = 24900; break; case FORM_TREE: - spellId = 5420; - spellId2 = 34123; + spellId = 34123; break; case FORM_TRAVEL: spellId = 5419; @@ -1456,14 +1583,15 @@ void AuraEffect::HandleShapeshiftBoosts(bool apply) spellId = 27792; spellId2 = 27795; // must be second, this important at aura remove to prevent to early iterator invalidation. break; + case FORM_SHADOW: + spellId = 49868; + break; case FORM_GHOUL: case FORM_GHOSTWOLF: case FORM_AMBIENT: - case FORM_SHADOW: case FORM_STEALTH: case FORM_CREATURECAT: case FORM_CREATUREBEAR: - spellId = 0; break; } @@ -1471,8 +1599,20 @@ void AuraEffect::HandleShapeshiftBoosts(bool apply) if(apply) { - if (spellId) m_target->CastSpell(m_target, spellId, true, NULL, this ); - if (spellId2) m_target->CastSpell(m_target, spellId2, true, NULL, this); + // Remove cooldown of spells triggered on stance change - they may share cooldown with stance spell + if (spellId) + { + if(m_target->GetTypeId() == TYPEID_PLAYER) + ((Player *)m_target)->RemoveSpellCooldown(spellId); + m_target->CastSpell(m_target, spellId, true, NULL, this ); + } + + if (spellId2) + { + if(m_target->GetTypeId() == TYPEID_PLAYER) + ((Player *)m_target)->RemoveSpellCooldown(spellId2); + m_target->CastSpell(m_target, spellId2, true, NULL, this); + } if(m_target->GetTypeId() == TYPEID_PLAYER) { @@ -1510,12 +1650,71 @@ void AuraEffect::HandleShapeshiftBoosts(bool apply) } } } + switch(GetMiscValue()) + { + case FORM_CAT: + // Nurturing Instinct + if (AuraEffect const * aurEff = m_target->GetAuraEffect(SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT, SPELLFAMILY_DRUID, 2254,0)) + { + uint32 spellId = 0; + switch (aurEff->GetId()) + { + case 33872: + spellId = 47179; + break; + case 33873: + spellId = 47180; + break; + } + m_target->CastSpell(m_target, spellId, true, NULL, this); + } + // Master Shapeshifter - Cat + if (AuraEffect const * aurEff = m_target->GetDummyAura(SPELLFAMILY_GENERIC, 2851, 0)) + { + int32 bp = aurEff->GetAmount(); + m_target->CastCustomSpell(m_target, 48420, &bp, NULL, NULL, true); + } + break; + case FORM_DIREBEAR: + case FORM_BEAR: + // Master Shapeshifter - Bear + if (AuraEffect const * aurEff = m_target->GetDummyAura(SPELLFAMILY_GENERIC, 2851, 0)) + { + int32 bp = aurEff->GetAmount(); + m_target->CastCustomSpell(m_target, 48418, &bp, NULL, NULL, true); + } + // Survival of the Fittest + if (AuraEffect const * aurEff = m_target->GetAuraEffect(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE,SPELLFAMILY_DRUID, 961, 0)) + { + int32 bp = m_target->CalculateSpellDamage(aurEff->GetSpellProto(),2,aurEff->GetSpellProto()->EffectBasePoints[2],m_target); + m_target->CastCustomSpell(m_target, 62069,&bp, NULL, NULL, true, 0, this); + } + break; + case FORM_MOONKIN: + // Master Shapeshifter - Moonkin + if (AuraEffect const * aurEff = m_target->GetDummyAura(SPELLFAMILY_GENERIC, 2851, 0)) + { + int32 bp = aurEff->GetAmount(); + m_target->CastCustomSpell(m_target, 48421, &bp, NULL, NULL, true); + } + break; + // Master Shapeshifter - Tree of Life + case FORM_TREE: + if (AuraEffect const * aurEff = m_target->GetDummyAura(SPELLFAMILY_GENERIC, 2851, 0)) + { + int32 bp = aurEff->GetAmount(); + m_target->CastCustomSpell(m_target, 48422, &bp, NULL, NULL, true); + } + break; + } } } else { - m_target->RemoveAurasDueToSpell(spellId); - m_target->RemoveAurasDueToSpell(spellId2); + if (spellId) + m_target->RemoveAurasDueToSpell(spellId); + if (spellId2) + m_target->RemoveAurasDueToSpell(spellId2); Unit::AuraMap& tAuras = m_target->GetAuras(); for (Unit::AuraMap::iterator itr = tAuras.begin(); itr != tAuras.end();) @@ -1540,12 +1739,8 @@ bool AuraEffect::isAffectedOnSpell(SpellEntry const *spell) const if (spell->SpellFamilyName != m_spellProto->SpellFamilyName) return false; - // Check EffectClassMask and Spell_Affect table - flag96 const *spellAffect = spellmgr.GetSpellAffect(GetId(), m_effIndex); - if (!spellAffect) - spellAffect = &m_spellProto->EffectSpellClassMask[m_effIndex]; - - if (*spellAffect & spell->SpellFamilyFlags) + // Check EffectClassMask + if (m_spellProto->EffectSpellClassMask[m_effIndex] & spell->SpellFamilyFlags) return true; return false; } @@ -1590,10 +1785,7 @@ void AuraEffect::HandleAddModifier(bool apply, bool Real, bool changeAmount) mod->type = SpellModType(m_auraName); // SpellModType value == spell aura types mod->spellId = GetId(); - flag96 const *spellAffect = spellmgr.GetSpellAffect(GetId(), m_effIndex); - if (!spellAffect) - spellAffect = &m_spellProto->EffectSpellClassMask[m_effIndex]; - mod->mask = *spellAffect; + mod->mask = m_spellProto->EffectSpellClassMask[m_effIndex]; mod->charges = GetParentAura()->GetAuraCharges(); m_spellmod = mod; @@ -1707,43 +1899,13 @@ void AuraEffect::TriggerSpell() ((Player*)caster)->ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,true); return; } -// // Periodic Mana Burn -// case 812: break; -// // Polymorphic Ray -// case 6965: break; -// // Fire Nova (1-7 ranks) -// case 8350: -// case 8508: -// case 8509: -// case 11312: -// case 11313: -// case 25540: -// case 25544: -// break; // Thaumaturgy Channel case 9712: trigger_spell_id = 21029; break; -// // Egan's Blaster -// case 17368: break; -// // Haunted -// case 18347: break; -// // Ranshalla Waiting -// case 18953: break; -// // Inferno -// case 19695: break; -// // Frostwolf Muzzle DND -// case 21794: break; -// // Alterac Ram Collar DND -// case 21866: break; -// // Celebras Waiting -// case 21916: break; - // Brood Affliction: Bronze case 23170: { m_target->CastSpell(m_target, 23171, true, 0, this); return; } -// // Mark of Frost -// case 23184: break; // Restoration case 23493: { @@ -1758,46 +1920,6 @@ void AuraEffect::TriggerSpell() } return; } -// // Stoneclaw Totem Passive TEST -// case 23792: break; -// // Axe Flurry -// case 24018: break; -// // Mark of Arlokk -// case 24210: break; -// // Restoration -// case 24379: break; -// // Happy Pet -// case 24716: break; -// // Dream Fog -// case 24780: break; -// // Cannon Prep -// case 24832: break; -// // Shadow Bolt Whirl -// case 24834: break; -// // Stink Trap -// case 24918: break; -// // Mark of Nature -// case 25041: break; -// // Agro Drones -// case 25152: break; -// // Consume -// case 25371: break; -// // Pain Spike -// case 25572: break; -// // Rotate 360 -// case 26009: break; -// // Rotate -360 -// case 26136: break; -// // Consume -// case 26196: break; -// // Berserk -// case 26615: break; -// // Defile -// case 27177: break; -// // Teleport: IF/UC -// case 27601: break; -// // Five Fat Finger Exploding Heart Technique -// case 27673: break; // Nitrous Boost case 27746: { @@ -1811,8 +1933,6 @@ void AuraEffect::TriggerSpell() return; } } break; -// // Steam Tank Passive -// case 27747: break; // Frost Blast case 27808: caster->CastCustomSpell(29879, SPELLVALUE_BASE_POINT0, (float)m_target->GetMaxHealth()*0.26f, m_target, true, NULL, this); @@ -1825,49 +1945,13 @@ void AuraEffect::TriggerSpell() m_target->CastCustomSpell(27820, SPELLVALUE_BASE_POINT0, -mana*4, NULL, true, NULL, this, caster->GetGUID()); } return; -// // Controller Timer -// case 28095: break; -// // Stalagg Chain -// case 28096: break; -// // Stalagg Tesla Passive -// case 28097: break; -// // Feugen Tesla Passive -// case 28109: break; -// // Feugen Chain -// case 28111: break; -// // Mark of Didier -// case 28114: break; -// // Communique Timer, camp -// case 28346: break; -// // Icebolt -// case 28522: break; -// // Silithyst -// case 29519: break; // Inoculate Nestlewood Owlkin case 29528: if(target->GetTypeId()!=TYPEID_UNIT)// prevent error reports in case ignored player target return; break; -// // Overload -// case 29768: break; -// // Return Fire -// case 29788: break; -// // Return Fire -// case 29793: break; -// // Return Fire -// case 29794: break; -// // Guardian of Icecrown Passive -// case 29897: break; // Feed Captured Animal case 29917: trigger_spell_id = 29916; break; -// // Flame Wreath -// case 29946: break; -// // Flame Wreath -// case 29947: break; -// // Mind Exhaustion Passive -// case 30025: break; -// // Nether Beam - Serenity -// case 30401: break; // Extract Gas case 30427: { @@ -1890,19 +1974,6 @@ void AuraEffect::TriggerSpell() } // Quake case 30576: trigger_spell_id = 30571; break; -// // Burning Maul -// case 30598: break; -// // Regeneration -// case 30799: -// case 30800: -// case 30801: -// break; -// // Despawn Self - Smoke cloud -// case 31269: break; -// // Time Rift Periodic -// case 31320: break; -// // Corrupt Medivh -// case 31326: break; // Doom case 31347: { @@ -1917,20 +1988,6 @@ void AuraEffect::TriggerSpell() caster->SummonCreature(17870, 0, 0, 0, caster->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 0); return; } -// // Bloodmyst Tesla -// case 31611: break; -// // Doomfire -// case 31944: break; -// // Teleport Test -// case 32236: break; -// // Earthquake -// case 32686: break; -// // Possess -// case 33401: break; -// // Draw Shadows -// case 33563: break; -// // Murmur's Touch -// case 33711: break; // Flame Quills case 34229: { @@ -1941,82 +1998,8 @@ void AuraEffect::TriggerSpell() caster->CastSpell(m_target,spell_id,true, NULL, this); return; } -// // Gravity Lapse -// case 34480: break; -// // Tornado -// case 34683: break; -// // Frostbite Rotate -// case 34748: break; -// // Arcane Flurry -// case 34821: break; -// // Interrupt Shutdown -// case 35016: break; -// // Interrupt Shutdown -// case 35176: break; -// // Inferno -// case 35268: break; -// // Salaadin's Tesla -// case 35515: break; -// // Ethereal Channel (Red) -// case 35518: break; -// // Nether Vapor -// case 35879: break; -// // Dark Portal Storm -// case 36018: break; -// // Burning Maul -// case 36056: break; -// // Living Grove Defender Lifespan -// case 36061: break; -// // Professor Dabiri Talks -// case 36064: break; -// // Kael Gaining Power -// case 36091: break; -// // They Must Burn Bomb Aura -// case 36344: break; -// // They Must Burn Bomb Aura (self) -// case 36350: break; -// // Stolen Ravenous Ravager Egg -// case 36401: break; -// // Activated Cannon -// case 36410: break; -// // Stolen Ravenous Ravager Egg -// case 36418: break; -// // Enchanted Weapons -// case 36510: break; -// // Cursed Scarab Periodic -// case 36556: break; -// // Cursed Scarab Despawn Periodic -// case 36561: break; -// // Vision Guide -// case 36573: break; -// // Cannon Charging (platform) -// case 36785: break; -// // Cannon Charging (self) -// case 36860: break; // Remote Toy case 37027: trigger_spell_id = 37029; break; -// // Mark of Death -// case 37125: break; -// // Arcane Flurry -// case 37268: break; -// // Spout -// case 37429: break; -// // Spout -// case 37430: break; -// // Karazhan - Chess NPC AI, Snapshot timer -// case 37440: break; -// // Karazhan - Chess NPC AI, action timer -// case 37504: break; -// // Karazhan - Chess: Is Square OCCUPIED aura (DND) -// case 39400: break; -// // Banish -// case 37546: break; -// // Shriveling Gaze -// case 37589: break; -// // Fake Aggro Radius (2 yd) -// case 37815: break; -// // Corrupt Medivh -// case 37853: break; // Eye of Grillok case 38495: { @@ -2036,93 +2019,18 @@ void AuraEffect::TriggerSpell() creatureTarget->ForcedDespawn(); return; } -// // Magic Sucker Device timer -// case 38672: break; -// // Tomb Guarding Charging -// case 38751: break; -// // Murmur's Touch -// case 38794: break; -// // Activate Nether-wraith Beacon (31742 Nether-wraith Beacon item) -// case 39105: break; -// // Drain World Tree Visual -// case 39140: break; -// // Quest - Dustin's Undead Dragon Visual aura -// case 39259: break; -// // Hellfire - The Exorcism, Jules releases darkness, aura -// case 39306: break; -// // Inferno -// case 39346: break; -// // Enchanted Weapons -// case 39489: break; -// // Shadow Bolt Whirl -// case 39630: break; -// // Shadow Bolt Whirl -// case 39634: break; -// // Shadow Inferno -// case 39645: break; // Tear of Azzinoth Summon Channel - it's not really supposed to do anything,and this only prevents the console spam case 39857: trigger_spell_id = 39856; break; -// // Soulgrinder Ritual Visual (Smashed) -// case 39974: break; -// // Simon Game Pre-game timer -// case 40041: break; -// // Knockdown Fel Cannon: The Aggro Check Aura -// case 40113: break; -// // Spirit Lance -// case 40157: break; -// // Demon Transform 2 -// case 40398: break; -// // Demon Transform 1 -// case 40511: break; -// // Ancient Flames -// case 40657: break; -// // Ethereal Ring Cannon: Cannon Aura -// case 40734: break; -// // Cage Trap -// case 40760: break; -// // Random Periodic -// case 40867: break; -// // Prismatic Shield -// case 40879: break; // Aura of Desire case 41350: { - Unit::AuraEffectList const& mMod = m_target->GetAurasByType(SPELL_AURA_MOD_INCREASE_ENERGY_PERCENT); - for(Unit::AuraEffectList::const_iterator i = mMod.begin(); i != mMod.end(); ++i) - { - if ((*i)->GetId() == 41350) - { - (*i)->ApplyModifier(false); - (*i)->SetAmount((*i)->GetAmount()-5); - (*i)->ApplyModifier(true); - break; - } - } - }break; -// // Dementia -// case 41404: break; -// // Chaos Form -// case 41629: break; -// // Alert Drums -// case 42177: break; -// // Spout -// case 42581: break; -// // Spout -// case 42582: break; -// // Return to the Spirit Realm -// case 44035: break; -// // Curse of Boundless Agony -// case 45050: break; -// // Earthquake -// case 46240: break; - // Personalized Weather + AuraEffect * aurEff = GetParentAura()->GetPartAura(1); + aurEff->ApplyModifier(false, false, true); + aurEff->SetAmount(aurEff->GetAmount()-5); + aurEff->ApplyModifier(true, false, true); + break; + } case 46736: trigger_spell_id = 46737; break; -// // Stay Submerged -// case 46981: break; -// // Dragonblight Ram -// case 47015: break; -// // Party G.R.E.N.A.D.E. -// case 51510: break; default: break; } @@ -2141,32 +2049,6 @@ void AuraEffect::TriggerSpell() } break; } -// case SPELLFAMILY_WARRIOR: -// { -// switch(auraId) -// { -// // Wild Magic -// case 23410: break; -// // Corrupted Totems -// case 23425: break; -// default: -// break; -// } -// break; -// } -// case SPELLFAMILY_PRIEST: -// { -// switch(auraId) -// { -// // Blue Beam -// case 32930: break; -// // Fury of the Dreghood Elders -// case 35460: break; -// default: -// break; -// } - // break; - // } case SPELLFAMILY_DRUID: { switch(auraId) @@ -2177,8 +2059,6 @@ void AuraEffect::TriggerSpell() return; // Frenzied Regeneration case 22842: - case 22895: - case 22896: case 26999: { int32 LifePerRage = GetAmount(); @@ -2196,42 +2076,42 @@ void AuraEffect::TriggerSpell() } break; } + case SPELLFAMILY_HUNTER: + { + switch (auraId) + { + // Sniper training + case 53302: + case 53303: + case 53304: + if (m_target->GetTypeId() != TYPEID_PLAYER) + return; + + if (((Player*)m_target)->isMoving()) + { + m_amount = m_target->CalculateSpellDamage(m_spellProto,m_effIndex,m_currentBasePoints,m_target); + return; + } + + // We are standing at the moment + if (m_amount > 0) + { + --m_amount; + return; + } -// case SPELLFAMILY_HUNTER: -// { -// switch(auraId) -// { -// //Frost Trap Aura -// case 13810: -// return; -// //Rizzle's Frost Trap -// case 39900: -// return; -// // Tame spells -// case 19597: // Tame Ice Claw Bear -// case 19676: // Tame Snow Leopard -// case 19677: // Tame Large Crag Boar -// case 19678: // Tame Adult Plainstrider -// case 19679: // Tame Prairie Stalker -// case 19680: // Tame Swoop -// case 19681: // Tame Dire Mottled Boar -// case 19682: // Tame Surf Crawler -// case 19683: // Tame Armored Scorpid -// case 19684: // Tame Webwood Lurker -// case 19685: // Tame Nightsaber Stalker -// case 19686: // Tame Strigid Screecher -// case 30100: // Tame Crazed Dragonhawk -// case 30103: // Tame Elder Springpaw -// case 30104: // Tame Mistbat -// case 30647: // Tame Barbed Crawler -// case 30648: // Tame Greater Timberstrider -// case 30652: // Tame Nightstalker -// return; -// default: -// break; -// } -// break; -// } + trigger_spell_id = 64418 + auraId - 53302; + + // If aura is active - no need to continue + if (target->HasAura(trigger_spell_id)) + return; + + break; + default: + break; + } + break; + } case SPELLFAMILY_SHAMAN: { switch(auraId) @@ -2240,15 +2120,8 @@ void AuraEffect::TriggerSpell() case 28820: { // Need remove self if Lightning Shield not active - Unit::AuraMap const& auras = target->GetAuras(); - for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - SpellEntry const* spell = itr->second->GetSpellProto(); - if( spell->SpellFamilyName == SPELLFAMILY_SHAMAN && - spell->SpellFamilyFlags[0] & 0x400) - return; - } - target->RemoveAurasDueToSpell(28820); + if (!target->GetAura(SPELL_AURA_PROC_TRIGGER_SPELL, SPELLFAMILY_SHAMAN,0x400)) + target->RemoveAurasDueToSpell(28820); return; } // Totemic Mastery (Skyshatter Regalia (Shaman Tier 6) - bonus) @@ -2287,43 +2160,6 @@ void AuraEffect::TriggerSpell() // Spell exist but require custom code switch(auraId) { - // Curse of Idiocy - case 1010: - { - // TODO: spell casted by result in correct way mostly - // BUT: - // 1) target show casting at each triggered cast: target don't must show casting animation for any triggered spell - // but must show affect apply like item casting - // 2) maybe aura must be replace by new with accumulative stat mods instead stacking - - // prevent cast by triggered auras - if(GetCasterGUID() == m_target->GetGUID()) - return; - - // stop triggering after each affected stats lost > 90 - int32 intellectLoss = 0; - int32 spiritLoss = 0; - - Unit::AuraEffectList const& mModStat = m_target->GetAurasByType(SPELL_AURA_MOD_STAT); - for(Unit::AuraEffectList::const_iterator i = mModStat.begin(); i != mModStat.end(); ++i) - { - if ((*i)->GetId() == 1010) - { - switch((*i)->GetMiscValue()) - { - case STAT_INTELLECT: intellectLoss += (*i)->GetAmount(); break; - case STAT_SPIRIT: spiritLoss += (*i)->GetAmount(); break; - default: break; - } - } - } - - if(intellectLoss <= -90 && spiritLoss <= -90) - return; - - caster = target; - break; - } // Mana Tide case 16191: { @@ -2347,6 +2183,10 @@ void AuraEffect::TriggerSpell() case 54835: caster->CastSpell(m_target, trigger_spell_id, true, NULL, this); return; + // Ground Slam + case 33525: + target->CastSpell(target, trigger_spell_id, true); + return; } } @@ -2395,6 +2235,29 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount) // AT APPLY if(apply) { + // Overpower + if (caster && m_spellProto->SpellFamilyName == SPELLFAMILY_WARRIOR && + m_spellProto->SpellFamilyFlags[0] & 0x4) + { + // Must be casting target + if (!m_target->IsNonMeleeSpellCasted(false, false, true)) + return; + if (AuraEffect * aurEff = caster->GetAuraEffect(SPELL_AURA_ADD_FLAT_MODIFIER, SPELLFAMILY_WARRIOR, 2775, 0)) + { + switch (aurEff->GetId()) + { + // Unrelenting Assault, rank 1 + case 46859: + m_target->CastSpell(m_target,64849,true,NULL,aurEff); + break; + // Unrelenting Assault, rank 2 + case 46860: + m_target->CastSpell(m_target,64850,true,NULL,aurEff); + break; + } + } + return; + } switch(GetId()) { // Haunting Spirits - perdiodic trigger demon @@ -2435,6 +2298,24 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount) } return; } + case 37096: // Blood Elf Illusion + { + if(caster) + { + switch(caster->getGender()) + { + case GENDER_FEMALE: + caster->CastSpell(m_target,37095,true,NULL,this); // Blood Elf Disguise + break; + case GENDER_MALE: + caster->CastSpell(m_target,37093,true,NULL,this); + break; + default: + break; + } + } + return; + } case 55198: // Tidal Force { m_target->CastSpell(m_target,55166,true,NULL,this); @@ -2479,8 +2360,9 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount) ((Player*)m_target)->RemoveAmmo(); // not use ammo and not allow use return; case 52916: // Honor Among Thieves - if (Unit * target = ObjectAccessor::GetUnit(*m_target, m_target->GetUInt64Value(UNIT_FIELD_TARGET))) - m_target->CastSpell(target, 51699, true); + if(m_target->GetTypeId()==TYPEID_PLAYER) + if (Unit * target = ObjectAccessor::GetUnit(*m_target,((Player*)m_target)->GetComboTarget())) + m_target->CastSpell(target, 51699, true); return; } @@ -2551,7 +2433,11 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount) return; case 46308: // Burning Winds casted only at creatures at spawn m_target->CastSpell(m_target,47287,true,NULL,this); - return; + return; + case 52173: // Coyote Spirit Despawn Aura + case 60244: // Blood Parrot Despawn Aura + m_target->CastSpell((Unit*)NULL, GetAmount(), true, NULL, this); + return; } break; case SPELLFAMILY_WARLOCK: @@ -2581,6 +2467,14 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount) return; } break; + case SPELLFAMILY_DEATHKNIGHT: + // Summon Gargoyle ( will start feeding gargoyle ) + if(GetId()==61777) + { + m_target->CastSpell(m_target,m_spellProto->EffectTriggerSpell[m_effIndex],true); + return; + } + break; default: break; } @@ -2597,6 +2491,10 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount) break; switch(GetId()) { + // Recently Bandaged + case 11196: + m_target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, GetMiscValue(), apply); + return; // Unstable Power case 24658: { @@ -2763,6 +2661,14 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount) return; } } + // Predatory Strikes + if(m_target->GetTypeId()==TYPEID_PLAYER && GetSpellProto()->SpellIconID == 1563) + { + ((Player*)m_target)->UpdateAttackPowerAndDamage(); + return; + } + if (!Real) + break; // Lifebloom if ( GetSpellProto()->SpellFamilyFlags[1] & 0x10 ) { @@ -2783,85 +2689,19 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount) if(m_target->IsInWorld()) m_target->CastCustomSpell(m_target,33778,&m_amount,NULL,NULL,true,NULL,this,GetCasterGUID()); - /*// have a look if there is still some other Lifebloom dummy aura - Unit::AuraList auras = m_target->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::iterator itr = auras.begin(); itr!=auras.end(); ++itr) - if((*itr)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID && - (*itr)->GetSpellProto()->SpellFamilyFlags & 0x1000000000LL) - return; - - // final heal - if(m_target->IsInWorld() && m_stackAmount > 0) + // restore mana + if (caster) { - int32 amount = m_amount / m_stackAmount; - m_target->CastCustomSpell(m_target,33778,&amount,NULL,NULL,true,NULL,this,GetCasterGUID()); - }*/ - } - return; - } - - // Predatory Strikes - if(m_target->GetTypeId()==TYPEID_PLAYER && GetSpellProto()->SpellIconID == 1563) - { - ((Player*)m_target)->UpdateAttackPowerAndDamage(); - return; - } - break; - } - case SPELLFAMILY_HUNTER: - { - if (!Real) - break; - // Glyph of Aspect of the Monkey - if(m_spellProto->Id==56833) - { - if(apply) - { - SpellModifier *mod = new SpellModifier; - mod->op = SPELLMOD_CHANCE_OF_SUCCESS; - mod->value = 100; - mod->type = SPELLMOD_FLAT; - mod->spellId = GetId(); - mod->mask[2] = 8192; - mod->mask[1] = 0x00000000; - mod->mask[0] = 524288; - m_spellmod = mod; + int32 returnmana = (GetSpellProto()->ManaCostPercentage * caster->GetCreateMana() / 100) * GetParentAura()->GetStackAmount() / 2; + caster->CastCustomSpell(caster, 64372, &returnmana, NULL, NULL, true, NULL, this, GetCasterGUID()); + } } - ((Player*)m_target)->AddSpellMod(m_spellmod, apply); return; } break; } case SPELLFAMILY_SHAMAN: { - if (!Real && !changeAmount) - break; - // Improved Weapon Totems - if( GetSpellProto()->SpellIconID == 57 && m_target->GetTypeId()==TYPEID_PLAYER ) - { - if(apply) - { - SpellModifier *mod = new SpellModifier; - mod->op = SPELLMOD_EFFECT1; - mod->value = m_amount; - mod->type = SPELLMOD_PCT; - mod->spellId = GetId(); - switch (m_effIndex) - { - case 0: - mod->mask[1] = 0x002; // Windfury Totem - break; - case 1: - mod->mask[1] = 0x004; // Flametongue Totem - break; - } - - m_spellmod = mod; - } - - ((Player*)m_target)->AddSpellMod(m_spellmod, apply); - return; - } if (!Real) break; // Sentry Totem @@ -2888,7 +2728,7 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount) if (Real) { // pet auras - if(PetAura const* petSpell = spellmgr.GetPetAura(GetId())) + if(PetAura const* petSpell = spellmgr.GetPetAura(GetId(), m_effIndex)) { if(apply) m_target->AddPetAura(petSpell); @@ -3088,6 +2928,8 @@ void AuraEffect::HandleAuraModShapeshift(bool apply, bool Real, bool changeAmoun case FORM_AMBIENT: case FORM_SHADOW: case FORM_STEALTH: + case FORM_UNDEAD: + case FORM_SHADOW_DANCE: break; case FORM_TREE: modelid = 864; @@ -3116,13 +2958,37 @@ void AuraEffect::HandleAuraModShapeshift(bool apply, bool Real, bool changeAmoun case FORM_FLIGHT_EPIC: case FORM_FLIGHT: case FORM_MOONKIN: + { // remove movement affects m_target->RemoveMovementImpairingAuras(); +/* + m_target->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); + Unit::AuraList const& slowingAuras = m_target->GetAurasByType(SPELL_AURA_MOD_DECREASE_SPEED); + for (Unit::AuraList::const_iterator iter = slowingAuras.begin(); iter != slowingAuras.end();) + { + SpellEntry const* aurSpellInfo = (*iter)->GetSpellProto(); + + // If spell that caused this aura has Croud Control or Daze effect + if((GetAllSpellMechanicMask(aurSpellInfo) & MECHANIC_NOT_REMOVED_BY_SHAPESHIFT) || + // some Daze spells have these parameters instead of MECHANIC_DAZE + (aurSpellInfo->SpellIconID == 15 && aurSpellInfo->Dispel == 0)) + { + ++iter; + continue; + } + + // All OK, remove aura now + m_target->RemoveAurasDueToSpellByCancel(aurSpellInfo->Id); + iter = slowingAuras.begin(); + } +*/ // and polymorphic affects if(m_target->IsPolymorphed()) m_target->RemoveAurasDueToSpell(m_target->getTransForm()); + break; + } default: break; } @@ -3176,28 +3042,6 @@ void AuraEffect::HandleAuraModShapeshift(bool apply, bool Real, bool changeAmoun } break; } - case FORM_BATTLESTANCE: - case FORM_DEFENSIVESTANCE: - case FORM_BERSERKERSTANCE: - { - uint32 Rage_val = 0; - // Stance mastery + Tactical mastery (both passive, and last have aura only in defense stance, but need apply at any stance switch) - if(m_target->GetTypeId() == TYPEID_PLAYER) - { - PlayerSpellMap const& sp_list = ((Player *)m_target)->GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) - { - if(itr->second->state == PLAYERSPELL_REMOVED) continue; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); - if (spellInfo && spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && spellInfo->SpellIconID == 139) - Rage_val += m_target->CalculateSpellDamage(spellInfo,0,spellInfo->EffectBasePoints[0],m_target) * 10; - } - } - - if (m_target->GetPower(POWER_RAGE) > Rage_val) - m_target->SetPower(POWER_RAGE,Rage_val); - break; - } default: break; } @@ -3222,14 +3066,41 @@ void AuraEffect::HandleAuraModShapeshift(bool apply, bool Real, bool changeAmoun case FORM_BEAR: case FORM_DIREBEAR: case FORM_CAT: - if(AuraEffect* dummy = m_target->GetDummyAura(37315) ) + if(AuraEffect* dummy = m_target->GetAuraEffect(37315, 0) ) m_target->CastSpell(m_target,37316,true,NULL,dummy); break; // Nordrassil Regalia - bonus case FORM_MOONKIN: - if(AuraEffect* dummy = m_target->GetDummyAura(37324) ) + if(AuraEffect* dummy = m_target->GetAuraEffect(37324, 0) ) m_target->CastSpell(m_target,37325,true,NULL,dummy); break; + case FORM_BATTLESTANCE: + case FORM_DEFENSIVESTANCE: + case FORM_BERSERKERSTANCE: + { + uint32 Rage_val = 0; + // Defensive Tactics + if (form == FORM_DEFENSIVESTANCE) + { + if (AuraEffect const * aurEff = m_target->IsScriptOverriden(m_spellProto,831)) + Rage_val += aurEff->GetAmount() * 10; + } + // Stance mastery + Tactical mastery (both passive, and last have aura only in defense stance, but need apply at any stance switch) + if(m_target->GetTypeId() == TYPEID_PLAYER) + { + PlayerSpellMap const& sp_list = ((Player *)m_target)->GetSpellMap(); + for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) + { + if(itr->second->state == PLAYERSPELL_REMOVED) continue; + SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); + if (spellInfo && spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && spellInfo->SpellIconID == 139) + Rage_val += m_target->CalculateSpellDamage(spellInfo,0,spellInfo->EffectBasePoints[0],m_target) * 10; + } + } + if (m_target->GetPower(POWER_RAGE) > Rage_val) + m_target->SetPower(POWER_RAGE,Rage_val); + break; + } default: break; } @@ -3244,8 +3115,9 @@ void AuraEffect::HandleAuraModShapeshift(bool apply, bool Real, bool changeAmoun if(m_target->getClass() == CLASS_DRUID) { - if(form == FORM_CAT && apply) // add dash if in cat-from + if(form == FORM_CAT && apply) { + // add dash if in cat-from Unit::AuraMap & auras = m_target->GetAuras(); for (Unit::AuraMap::iterator iter = auras.begin(); iter != auras.end();++iter) { @@ -3364,6 +3236,13 @@ void AuraEffect::HandleAuraTransform(bool apply, bool Real, bool /*changeAmount* // Dragonmaw Illusion (set mount model also) if(GetId()==42016 && m_target->GetMountID() && !m_target->GetAurasByType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED).empty()) m_target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,16314); + + // Polymorph (sheep) + if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_MAGE && GetSpellProto()->SpellIconID == 82 && GetSpellProto()->SpellVisual[0] == 12978) + if (Unit * caster = GetCaster()) + // Glyph of the Penguin + if (caster->HasAura(52648)) + m_target->SetDisplayId(26452); } } @@ -4055,12 +3934,12 @@ void AuraEffect::HandleModThreat(bool apply, bool Real, bool changeAmount) if(!Real && !changeAmount) return; - if(!m_target->isAlive()) + if (!m_target->isAlive()) return; Unit* caster = GetCaster(); - if(!caster || !caster->isAlive()) + if (!caster || !caster->isAlive()) return; int level_diff = 0; @@ -4078,17 +3957,14 @@ void AuraEffect::HandleModThreat(bool apply, bool Real, bool changeAmount) multiplier = 1; break; } + if (level_diff > 0) m_amount += multiplier * level_diff; - for(int8 x=0;x < MAX_SPELL_SCHOOL;x++) - { - if(GetMiscValue() & int32(1<<x)) - { - if(m_target->GetTypeId() == TYPEID_PLAYER) + if (m_target->GetTypeId() == TYPEID_PLAYER) + for(int8 x=0;x < MAX_SPELL_SCHOOL;x++) + if (GetMiscValue() & int32(1<<x)) ApplyPercentModFloatVar(m_target->m_threatModifier[x], m_amount, apply); - } - } } void AuraEffect::HandleAuraModTotalThreat(bool apply, bool Real, bool changeAmount) @@ -4097,19 +3973,15 @@ void AuraEffect::HandleAuraModTotalThreat(bool apply, bool Real, bool changeAmou if(!Real && !changeAmount) return; - if(!m_target->isAlive() || m_target->GetTypeId()!= TYPEID_PLAYER) + if (!m_target->isAlive() || m_target->GetTypeId() != TYPEID_PLAYER) return; Unit* caster = GetCaster(); - if(!caster || !caster->isAlive()) + if (!caster || !caster->isAlive()) return; - float threatMod = 0.0f; - if(apply) - threatMod = float(m_amount); - else - threatMod = float(-m_amount); + float threatMod = apply ? float(m_amount) : float(-m_amount); m_target->getHostilRefManager().threatAssist(caster, threatMod); } @@ -4117,18 +3989,18 @@ void AuraEffect::HandleAuraModTotalThreat(bool apply, bool Real, bool changeAmou void AuraEffect::HandleModTaunt(bool apply, bool Real, bool /*changeAmount*/) { // only at real add/remove aura - if(!Real) + if (!Real) return; - if(!m_target->isAlive() || !m_target->CanHaveThreatList()) + if (!m_target->isAlive() || !m_target->CanHaveThreatList()) return; Unit* caster = GetCaster(); - if(!caster || !caster->isAlive() || caster->GetTypeId() != TYPEID_PLAYER) + if (!caster || !caster->isAlive()) return; - if(apply) + if (apply) m_target->TauntApply(caster); else { @@ -4204,15 +4076,28 @@ void AuraEffect::HandleAuraModIncreaseSwimSpeed(bool /*apply*/, bool Real, bool m_target->UpdateSpeed(MOVE_SWIM, true); } -void AuraEffect::HandleAuraModDecreaseSpeed(bool /*apply*/, bool Real, bool changeAmount) +void AuraEffect::HandleAuraModDecreaseSpeed(bool apply, bool Real, bool changeAmount) { // all applied/removed only at real aura add/remove if(!Real && !changeAmount) return; + if (apply) + { + // Gronn Lord's Grasp, becomes stoned + if (GetId() == 33572) + { + if (GetParentAura()->GetStackAmount() >= 5 && !m_target->HasAura(33652)) + m_target->CastSpell(m_target, 33652, true); + } + } + m_target->UpdateSpeed(MOVE_RUN, true); m_target->UpdateSpeed(MOVE_SWIM, true); m_target->UpdateSpeed(MOVE_FLIGHT, true); + m_target->UpdateSpeed(MOVE_RUN_BACK, true); + m_target->UpdateSpeed(MOVE_SWIM_BACK, true); + m_target->UpdateSpeed(MOVE_FLIGHT_BACK, true); } void AuraEffect::HandleAuraModUseNormalSpeed(bool /*apply*/, bool Real, bool changeAmount) @@ -4240,7 +4125,7 @@ void AuraEffect::HandleModStateImmunityMask(bool apply, bool Real, bool /*change if (GetMiscValue() & (1<<7)) immunity_list.push_back(SPELL_AURA_MOD_DISARM); if (GetMiscValue() & (1<<1)) - immunity_list.push_back(SPELL_AURA_MOD_TAUNT); + immunity_list.push_back(SPELL_AURA_TRANSFORM); // These flag can be recognized wrong: if (GetMiscValue() & (1<<6)) @@ -4283,6 +4168,11 @@ void AuraEffect::HandleModMechanicImmunity(bool apply, bool Real, bool /*changeA //immune movement impairment and loss of control if(GetId()==42292 || GetId()==59752) mechanic=IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; + // Forbearance + // in DBC wrong mechanic immune since 3.0.x + else if (GetId() == 25771) + mechanic = 1 << MECHANIC_IMMUNE_SHIELD; + if (!mechanic) return; @@ -4310,23 +4200,18 @@ void AuraEffect::HandleModMechanicImmunity(bool apply, bool Real, bool /*changeA m_target->ApplySpellImmune(GetId(),IMMUNITY_MECHANIC,GetMiscValue(),apply); // Bestial Wrath - if ( GetSpellProto()->SpellFamilyName == SPELLFAMILY_HUNTER && GetSpellProto()->Id == 19574) + if (GetSpellProto()->Id == 19574) { // The Beast Within cast on owner if talent present if ( Unit* owner = m_target->GetOwner() ) { // Search talent - Unit::AuraEffectList const& m_dummyAuras = owner->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraEffectList::const_iterator i = m_dummyAuras.begin(); i != m_dummyAuras.end(); ++i) + if (owner->GetAuraEffect(34692, 0)) { - if ( (*i)->GetSpellProto()->SpellIconID == 2229 ) - { - if (apply) - owner->CastSpell(owner, 34471, true, 0, this); - else - owner->RemoveAurasDueToSpell(34471); - break; - } + if (apply) + owner->CastSpell(owner, 34471, true, 0, this); + else + owner->RemoveAurasDueToSpell(34471); } } } @@ -4365,7 +4250,7 @@ void AuraEffect::HandleModMechanicImmunity(bool apply, bool Real, bool /*changeA m_target->ApplySpellImmune(GetId(),IMMUNITY_STATE,SPELL_AURA_MOD_DECREASE_SPEED,apply); } // Demonic Circle - else if (m_spellProto->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellProto->SpellIconID == 3221) + else if (GetId() == 48020) { if (m_target->GetTypeId() != TYPEID_PLAYER) return; @@ -4378,7 +4263,6 @@ void AuraEffect::HandleModMechanicImmunity(bool apply, bool Real, bool /*changeA } } -//this method is called whenever we add / remove aura which gives m_target some imunity to some spell effect void AuraEffect::HandleAuraModEffectImmunity(bool apply, bool Real, bool /*changeAmount*/) { // when removing flag aura, handle flag drop @@ -4518,15 +4402,20 @@ void AuraEffect::HandlePeriodicTriggerSpellWithValue(bool apply, bool Real, bool void AuraEffect::HandlePeriodicEnergize(bool apply, bool Real, bool changeAmount) { - if(!Real && !changeAmount) + if(!Real) return; m_isPeriodic = apply; - // Replenishment (0.25% from max) - // Infinite Replenishment - if (m_spellProto->SpellIconID == 3184 && m_spellProto->SpellVisual[0] == 12495) - m_amount = m_target->GetMaxPower(POWER_MANA) * 25 / 10000; + if (apply) + { + // Replenishment (0.25% from max) + // Infinite Replenishment + if (m_spellProto->SpellIconID == 3184 && m_spellProto->SpellVisual[0] == 12495) + m_amount = m_target->GetMaxPower(POWER_MANA) * 25 / 10000; + else if (m_spellProto->Id == 29166) // Innervate + m_amount = m_target->GetCreatePowers(POWER_MANA) * m_amount / (GetTotalTicks() * 100.0f); + } } void AuraEffect::HandleAuraPowerBurn(bool apply, bool Real, bool /*changeAmount*/) @@ -4572,10 +4461,19 @@ void AuraEffect::HandleAuraPeriodicDummy(bool apply, bool Real, bool changeAmoun case SPELLFAMILY_HUNTER: { // Explosive Shot - if (apply && !loading && caster) + if (apply && !loading && caster && spell->SpellFamilyFlags[1] & 0x80000000) m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 14 / 100); break; } + case SPELLFAMILY_DEATHKNIGHT: + { + // Reaping + // Blood of the North + // Death Rune Mastery + if (spell->SpellIconID == 3041 || spell->SpellIconID == 22 || spell->SpellIconID == 2622) + m_amount = 0; + break; + } } m_isPeriodic = apply; @@ -4599,7 +4497,7 @@ void AuraEffect::HandlePeriodicDamage(bool apply, bool Real, bool changeAmount) // Curse of Doom // This is a hack - this aura should be handled by passive aura and proc doomguard spawn on kill, however there is no such aura in dbcs - if(m_spellProto->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellProto->SpellFamilyFlags.IsEqual(0,0x02,0)) + if(m_spellProto->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellProto->SpellFamilyFlags[1] & 0x02) { if (Real && !apply && GetParentAura()->GetRemoveMode()==AURA_REMOVE_BY_DEATH) { @@ -4621,17 +4519,6 @@ void AuraEffect::HandlePeriodicDamage(bool apply, bool Real, bool changeAmount) switch (m_spellProto->SpellFamilyName) { - case SPELLFAMILY_GENERIC: - { - // Pounce Bleed - if ( m_spellProto->SpellIconID == 147 && m_spellProto->SpellVisual[0] == 0 ) - { - // $AP*0.18/6 bonus per tick - m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 3 / 100); - return; - } - break; - } case SPELLFAMILY_WARRIOR: { // Rend @@ -4643,28 +4530,32 @@ void AuraEffect::HandlePeriodicDamage(bool apply, bool Real, bool changeAmount) float mwb_min = caster->GetWeaponDamageRange(BASE_ATTACK,MINDAMAGE); float mwb_max = caster->GetWeaponDamageRange(BASE_ATTACK,MAXDAMAGE); m_amount+=int32(((mwb_min+mwb_max)/2+ap*mws/14000)*0.2f); + // "If used while your target is above 75% health, Rend does 35% more damage." + // as for 3.1.3 only ranks above 9 (wrong tooltip?) + if (spellmgr.GetSpellRank(m_spellProto->Id) >= 9) + { + if (m_target->HasAuraState(AURA_STATE_HEALTH_ABOVE_75_PERCENT, m_spellProto, caster)) + m_amount += int32(m_amount*0.35); + } return; } break; } - case SPELLFAMILY_DRUID: + // Drain Soul - If the target is at or below 25% health, Drain Soul causes four times the normal damage + case SPELLFAMILY_WARLOCK: { - // Rake - if (m_spellProto->SpellFamilyFlags[0] & 0x1000) - { - // $AP*0.06 bonus per tick - m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 6 / 100); - return; - } - // Lacerate - if (m_spellProto->SpellFamilyFlags[1] & 0x0000000100) + if (m_spellProto->SpellFamilyFlags[0] & 0x00004000) { - // $AP*0.05/5 bonus per tick - m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100); - return; + // if victim is below 25% of hp + if (m_target->GetMaxHealth() / 4 > m_target->GetHealth()) + m_amount *= 4; } + break; + } + case SPELLFAMILY_DRUID: + { // Rip - if (m_spellProto->SpellVisual[0] == 3941) + if (m_spellProto->SpellFamilyFlags[0] & 0x00800000) { // 0.01*$AP*cp if (caster->GetTypeId() != TYPEID_PLAYER) @@ -4673,25 +4564,12 @@ void AuraEffect::HandlePeriodicDamage(bool apply, bool Real, bool changeAmount) uint8 cp = ((Player*)caster)->GetComboPoints(); // Idol of Feral Shadows. Cant be handled as SpellMod in SpellAura:Dummy due its dependency from CPs - Unit::AuraEffectList const& dummyAuras = caster->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraEffectList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr) - { - if((*itr)->GetId()==34241) - { - m_amount += cp * (*itr)->GetAmount(); - break; - } - } + if (AuraEffect const * aurEff = caster->GetAuraEffect(34241,0)) + m_amount += cp * aurEff->GetAmount(); + m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * cp / 100); return; } - // Lock Jaw - if (m_spellProto->SpellFamilyFlags[1] & 0x10000000) - { - // 0.15*$AP - m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 15 / 100); - return; - } break; } case SPELLFAMILY_ROGUE: @@ -4712,38 +4590,6 @@ void AuraEffect::HandlePeriodicDamage(bool apply, bool Real, bool changeAmount) m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * AP_per_combo[cp]); return; } - // Garrote - if (m_spellProto->SpellFamilyFlags[0] & 0x100) - { - // $AP*0.07 bonus per tick - m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 7 / 100); - return; - } - // Deadly Poison - if (m_spellProto->SpellFamilyFlags[0] & 0x10000) - { - // 0.08*$AP / 4 * amount of stack - m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 2 * GetParentAura()->GetStackAmount() / 100); - return; - } - break; - } - case SPELLFAMILY_HUNTER: - { - // Serpent Sting - if (m_spellProto->SpellFamilyFlags[0] & 0x4000) - { - // $RAP*0.1/5 bonus per tick - m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 10 / 500); - return; - } - // Immolation Trap - if (m_spellProto->SpellFamilyFlags[0] & 0x4 && m_spellProto->SpellIconID == 678) - { - // $RAP*0.1/5 bonus per tick - m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 10 / 500); - return; - } break; } default: @@ -4913,7 +4759,7 @@ void AuraEffect::HandleModSpellDamagePercentFromStat(bool /*apply*/, bool Real, ((Player*)m_target)->UpdateSpellDamageAndHealingBonus(); } -void AuraEffect::HandleModSpellHealingPercentFromStat(bool /*apply*/, bool Real, bool /*changeAmount*/) +void AuraEffect::HandleModSpellHealingPercentFromStat(bool apply, bool Real, bool /*changeAmount*/) { if(m_target->GetTypeId() != TYPEID_PLAYER) return; @@ -5663,6 +5509,7 @@ void AuraEffect::HandleAuraModPacify(bool apply, bool Real, bool /*changeAmount* void AuraEffect::HandleAuraModPacifyAndSilence(bool apply, bool Real, bool changeAmount) { + // Vengeance of the Blue Flight if(m_spellProto->Id == 45839) { if(apply) @@ -5855,7 +5702,7 @@ void AuraEffect::HandleSchoolAbsorb(bool apply, bool Real, bool changeAmount) { case SPELLFAMILY_PRIEST: // Power Word: Shield - if(m_spellProto->SpellFamilyFlags.IsEqual(0x1, 0, 0x400)) + if(m_spellProto->SpellFamilyFlags[0] & 0x1 && m_spellProto->SpellFamilyFlags[2] & 0x400) { // +80.68% from sp bonus DoneActualBenefit = caster->SpellBaseHealingBonus(GetSpellSchoolMask(m_spellProto)) * 0.8068f; @@ -5863,7 +5710,7 @@ void AuraEffect::HandleSchoolAbsorb(bool apply, bool Real, bool changeAmount) break; case SPELLFAMILY_MAGE: // ice barrier - if(m_spellProto->SpellFamilyFlags.IsEqual(0, 0x1, 0x8)) + if(m_spellProto->SpellFamilyFlags[1] & 0x1 && m_spellProto->SpellFamilyFlags[2] & 0x8) { // +80.67% from sp bonus DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.8067f; @@ -5880,7 +5727,7 @@ void AuraEffect::HandleSchoolAbsorb(bool apply, bool Real, bool changeAmount) break; case SPELLFAMILY_WARLOCK: // shadow ward - if(m_spellProto->SpellFamilyFlags.IsEqual(0, 0, 0x40)) + if(m_spellProto->SpellFamilyFlags[2]& 0x40) { // +30% from sp bonus DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.3f; @@ -5891,7 +5738,7 @@ void AuraEffect::HandleSchoolAbsorb(bool apply, bool Real, bool changeAmount) if (m_spellProto->SpellFamilyFlags[1] & 0x80000) { // 0.75 from sp bonus - DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.75f; + DoneActualBenefit = caster->SpellBaseHealingBonus(GetSpellSchoolMask(m_spellProto)) * 0.75f; } break; default: @@ -5903,6 +5750,38 @@ void AuraEffect::HandleSchoolAbsorb(bool apply, bool Real, bool changeAmount) m_amount += (int32)DoneActualBenefit; } } + + // Guardian Spirit + if(m_spellProto->Id == 47788 && Real && !apply) + { + Unit *caster = GetCaster(); + if(!caster) + return; + + if(caster->GetTypeId() != TYPEID_PLAYER) + return; + + Player *player = ((Player*)caster); + // Glyph of Guardian Spirit + if(AuraEffect * aurEff = player->GetAuraEffect(63231, 0)) + { + if (GetParentAura()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE) + { + if (!player->HasSpellCooldown(47788)) + return; + + player->RemoveSpellCooldown(m_spellProto->Id, true); + player->AddSpellCooldown(m_spellProto->Id, 0, uint32(time(NULL) + aurEff->GetAmount())); + + WorldPacket data(SMSG_SPELL_COOLDOWN, 8+1+4+4); + data << uint64(player->GetGUID()); + data << uint8(0x0); // flags (0x1, 0x2) + data << uint32(m_spellProto->Id); + data << uint32(aurEff->GetAmount()*IN_MILISECONDS); + player->SendDirectMessage(&data); + } + } + } } void AuraEffect::PeriodicTick() @@ -5933,15 +5812,15 @@ void AuraEffect::PeriodicTick() { switch(GetId()) { - case 43093: case 31956: case 38801: - case 35321: case 38363: case 39215: + case 43093: case 31956: case 38801: // Grievous Wound + case 35321: case 38363: case 39215: // Gushing Wound if(m_target->GetHealth() == m_target->GetMaxHealth() ) { m_target->RemoveAurasDueToSpell(GetId()); return; } break; - case 38772: + case 38772: // Grievous Wound { uint32 percent = GetEffIndex() < 2 && GetSpellProto()->Effect[GetEffIndex()]==SPELL_EFFECT_DUMMY ? @@ -5954,18 +5833,13 @@ void AuraEffect::PeriodicTick() } break; } - case 41337:// aura of anger + case 41337:// Aura of Anger { - Unit::AuraEffectList const& mMod = m_target->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); - for(Unit::AuraEffectList::const_iterator i = mMod.begin(); i != mMod.end(); ++i) + if (AuraEffect * aurEff = GetParentAura()->GetPartAura(1)) { - if ((*i)->GetId() == 41337) - { - (*i)->ApplyModifier(false); - (*i)->SetAmount((*i)->GetAmount()+5); - (*i)->ApplyModifier(true); - break; - } + aurEff->ApplyModifier(false, false, true); + aurEff->SetAmount(aurEff->GetAmount()+5); + aurEff->ApplyModifier(true, false, true); } m_amount = 100 * m_tickNumber; break; @@ -5977,7 +5851,7 @@ void AuraEffect::PeriodicTick() uint32 absorb=0; uint32 resist=0; - CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL ); + CleanDamage cleanDamage = CleanDamage(0, 0, BASE_ATTACK, MELEE_HIT_NORMAL ); // ignore non positive values (can be result apply spellmods to aura damage //uint32 amount = GetModifierValuePerStack() > 0 ? GetModifierValuePerStack() : 0; @@ -5993,7 +5867,7 @@ void AuraEffect::PeriodicTick() GetEffectMechanic(GetSpellProto(), m_effIndex) != MECHANIC_BLEED) { uint32 pdamageReductedArmor = pCaster->CalcArmorReducedDamage(m_target, pdamage, GetSpellProto()); - cleanDamage.damage += pdamage - pdamageReductedArmor; + cleanDamage.mitigated_damage += pdamage - pdamageReductedArmor; pdamage = pdamageReductedArmor; } @@ -6013,6 +5887,21 @@ void AuraEffect::PeriodicTick() else pdamage = uint32(m_target->GetMaxHealth()*pdamage/100); + bool crit = false; + Unit::AuraEffectList const& mPeriodicCritAuras= pCaster->GetAurasByType(SPELL_AURA_ABILITY_PERIODIC_CRIT); + for(Unit::AuraEffectList::const_iterator itr = mPeriodicCritAuras.begin(); itr != mPeriodicCritAuras.end(); ++itr) + { + if (!(*itr)->isAffectedOnSpell(m_spellProto)) + continue; + + if (pCaster->isSpellCrit(m_target, m_spellProto, GetSpellSchoolMask(m_spellProto))) + { + crit = true; + pdamage = pCaster->SpellCriticalDamageBonus(m_spellProto, pdamage, m_target); + } + break; + } + //As of 2.2 resilience reduces damage from DoT ticks as much as the chance to not be critically hit // Reduce dot damage from resilience for players if (m_target->GetTypeId()==TYPEID_PLAYER) @@ -6025,7 +5914,7 @@ void AuraEffect::PeriodicTick() pCaster->DealDamageMods(m_target,pdamage,&absorb); - SpellPeriodicAuraLogInfo pInfo(this, pdamage, 0, absorb, resist, 0.0f); + SpellPeriodicAuraLogInfo pInfo(this, pdamage, 0, absorb, resist, 0.0f, crit); m_target->SendPeriodicAuraLog(&pInfo); Unit* target = m_target; // aura can be deleted in DealDamage @@ -6062,7 +5951,7 @@ void AuraEffect::PeriodicTick() uint32 absorb=0; uint32 resist=0; - CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL ); + CleanDamage cleanDamage = CleanDamage(0, 0, BASE_ATTACK, MELEE_HIT_NORMAL ); //uint32 pdamage = GetModifierValuePerStack() > 0 ? GetModifierValuePerStack() : 0; uint32 pdamage = GetAmount() > 0 ? GetAmount() : 0; @@ -6072,7 +5961,7 @@ void AuraEffect::PeriodicTick() if (GetSpellSchoolMask(GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL) { uint32 pdamageReductedArmor = pCaster->CalcArmorReducedDamage(m_target, pdamage, GetSpellProto()); - cleanDamage.damage += pdamage - pdamageReductedArmor; + cleanDamage.mitigated_damage += pdamage - pdamageReductedArmor; pdamage = pdamageReductedArmor; } @@ -6156,7 +6045,7 @@ void AuraEffect::PeriodicTick() return; // heal for caster damage (must be alive) - if(m_target != pCaster && GetSpellProto()->SpellVisual[0]==163 && !pCaster->isAlive()) + if(m_target != pCaster && GetSpellProto()->AttributesEx2 & SPELL_ATTR_EX2_HEALTH_FUNNEL && !pCaster->isAlive()) return; if(GetParentAura()->GetAuraDuration() ==-1 && m_target->GetHealth()==m_target->GetMaxHealth()) @@ -6169,14 +6058,39 @@ void AuraEffect::PeriodicTick() if(m_auraName==SPELL_AURA_OBS_MOD_HEALTH) pdamage = uint32(m_target->GetMaxHealth() * pdamage * GetParentAura()->GetStackAmount() / 100); else + { + // Wild Growth (1/7 - 6 + 2*ramainTicks) % + if (m_spellProto->SpellFamilyName == SPELLFAMILY_DRUID && m_spellProto->SpellIconID == 2864) + { + int32 ticks = GetParentAura()->GetAuraMaxDuration()/m_amplitude; + int32 remainingTicks = int32(float(GetParentAura()->GetAuraDuration()) / m_amplitude + 0.5); + pdamage = int32(pdamage) + int32(pdamage)*ticks*(-6+2*remainingTicks)/100; + } + pdamage = pCaster->SpellHealingBonus(m_target, GetSpellProto(), pdamage, DOT, GetParentAura()->GetStackAmount()); + } + + bool crit = false; + Unit::AuraEffectList const& mPeriodicCritAuras= pCaster->GetAurasByType(SPELL_AURA_ABILITY_PERIODIC_CRIT); + for(Unit::AuraEffectList::const_iterator itr = mPeriodicCritAuras.begin(); itr != mPeriodicCritAuras.end(); ++itr) + { + if (!(*itr)->isAffectedOnSpell(m_spellProto)) + continue; + + if (pCaster->isSpellCrit(m_target, m_spellProto, GetSpellSchoolMask(m_spellProto))) + { + crit = true; + pdamage = pCaster->SpellCriticalHealingBonus(m_spellProto, pdamage, m_target); + } + break; + } sLog.outDetail("PeriodicTick: %u (TypeId: %u) heal of %u (TypeId: %u) for %u health inflicted by %u", GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId()); int32 gain = m_target->ModifyHealth(pdamage); - SpellPeriodicAuraLogInfo pInfo(this, pdamage, pdamage - gain, 0, 0, 0.0f); + SpellPeriodicAuraLogInfo pInfo(this, pdamage, pdamage - gain, 0, 0, 0.0f, crit); m_target->SendPeriodicAuraLog(&pInfo); // add HoTs to amount healed in bgs @@ -6192,7 +6106,7 @@ void AuraEffect::PeriodicTick() // Health Funnel // heal for caster damage - if(m_target!=pCaster && spellProto->SpellVisual[0]==163) + if(m_target!=pCaster && GetSpellProto()->AttributesEx2 & SPELL_ATTR_EX2_HEALTH_FUNNEL) { uint32 dmg = spellProto->manaPerSecond; if(pCaster->GetHealth() <= dmg && pCaster->GetTypeId()==TYPEID_PLAYER) @@ -6217,7 +6131,7 @@ void AuraEffect::PeriodicTick() pCaster->DealDamageMods(pCaster,damage,&absorb); pCaster->SendSpellNonMeleeDamageLog(pCaster, GetId(), damage, GetSpellSchoolMask(GetSpellProto()), absorb, 0, false, 0, false); - CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL ); + CleanDamage cleanDamage = CleanDamage(0, 0, BASE_ATTACK, MELEE_HIT_NORMAL ); pCaster->DealDamage(pCaster, damage, &cleanDamage, NODAMAGE, GetSpellSchoolMask(GetSpellProto()), GetSpellProto(), true); } } @@ -6291,7 +6205,7 @@ void AuraEffect::PeriodicTick() modOwner->ApplySpellMod(GetId(), SPELLMOD_MULTIPLE_VALUE, gain_multiplier); } - SpellPeriodicAuraLogInfo pInfo(this, drain_amount, 0, 0, 0, gain_multiplier); + SpellPeriodicAuraLogInfo pInfo(this, drain_amount, 0, 0, 0, gain_multiplier, false); m_target->SendPeriodicAuraLog(&pInfo); int32 gain_amount = int32(drain_amount*gain_multiplier); @@ -6323,6 +6237,20 @@ void AuraEffect::PeriodicTick() GetParentAura()->SetAuraDuration(0); } } + // Mana Feed - Drain Mana + if (m_spellProto->SpellFamilyName == SPELLFAMILY_WARLOCK + && m_spellProto->SpellFamilyFlags[0] & 0x00000010) + { + int32 manaFeedVal = 0; + if (AuraEffect const * aurEff = GetParentAura()->GetPartAura(1)) + manaFeedVal = aurEff->GetAmount(); + + if(manaFeedVal > 0) + { + manaFeedVal = manaFeedVal * gain_amount / 100; + pCaster->CastCustomSpell(pCaster, 32554, &manaFeedVal, NULL, NULL, true, NULL, this); + } + } break; } case SPELL_AURA_OBS_MOD_ENERGY: @@ -6346,7 +6274,7 @@ void AuraEffect::PeriodicTick() sLog.outDetail("PeriodicTick: %u (TypeId: %u) energize %u (TypeId: %u) for %u dmg inflicted by %u", GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), amount, GetId()); - SpellPeriodicAuraLogInfo pInfo(this, amount, 0, 0, 0, 0.0f); + SpellPeriodicAuraLogInfo pInfo(this, amount, 0, 0, 0, 0.0f, false); m_target->SendPeriodicAuraLog(&pInfo); int32 gain = m_target->ModifyPower(power,amount); @@ -6371,7 +6299,7 @@ void AuraEffect::PeriodicTick() uint32 amount = m_amount; - SpellPeriodicAuraLogInfo pInfo(this, amount, 0, 0, 0, 0.0f); + SpellPeriodicAuraLogInfo pInfo(this, amount, 0, 0, 0, 0.0f, false); m_target->SendPeriodicAuraLog(&pInfo); sLog.outDetail("PeriodicTick: %u (TypeId: %u) energize %u (TypeId: %u) for %u dmg inflicted by %u", @@ -6523,6 +6451,7 @@ void AuraEffect::PeriodicDummyTick() case 34291: case 43182: case 43183: + case 43706: case 46755: case 49472: // Drink Coffee case 57073: @@ -6602,165 +6531,10 @@ void AuraEffect::PeriodicDummyTick() // 7053 Forsaken Skill: Shadow return; } -// // Panda -// case 19230: break; -// // Gossip NPC Periodic - Talk -// case 33208: break; -// // Gossip NPC Periodic - Despawn -// case 33209: break; -// // Steal Weapon -// case 36207: break; -// // Simon Game START timer, (DND) -// case 39993: break; -// // Knockdown Fel Cannon: break; The Aggro Burst -// case 40119: break; -// // Old Mount Spell -// case 40154: break; -// // Magnetic Pull -// case 40581: break; -// // Ethereal Ring: break; The Bolt Burst -// case 40801: break; -// // Crystal Prison -// case 40846: break; -// // Copy Weapon -// case 41054: break; -// // Dementia -// case 41404: break; -// // Ethereal Ring Visual, Lightning Aura -// case 41477: break; -// // Ethereal Ring Visual, Lightning Aura (Fork) -// case 41525: break; -// // Ethereal Ring Visual, Lightning Jumper Aura -// case 41567: break; -// // No Man's Land -// case 41955: break; -// // Headless Horseman - Fire -// case 42074: break; -// // Headless Horseman - Visual - Large Fire -// case 42075: break; -// // Headless Horseman - Start Fire, Periodic Aura -// case 42140: break; -// // Ram Speed Boost -// case 42152: break; -// // Headless Horseman - Fires Out Victory Aura -// case 42235: break; -// // Pumpkin Life Cycle -// case 42280: break; -// // Brewfest Request Chick Chuck Mug Aura -// case 42537: break; -// // Squashling -// case 42596: break; -// // Headless Horseman Climax, Head: Periodic -// case 42603: break; -// // Fire Bomb -// case 42621: break; -// // Headless Horseman - Conflagrate, Periodic Aura -// case 42637: break; -// // Headless Horseman - Create Pumpkin Treats Aura -// case 42774: break; -// // Headless Horseman Climax - Summoning Rhyme Aura -// case 42879: break; -// // Tricky Treat -// case 42919: break; -// // Giddyup! -// case 42924: break; -// // Ram - Trot -// case 42992: break; -// // Ram - Canter -// case 42993: break; -// // Ram - Gallop -// case 42994: break; -// // Ram Level - Neutral -// case 43310: break; -// // Headless Horseman - Maniacal Laugh, Maniacal, Delayed 17 -// case 43884: break; -// // Wretched! -// case 43963: break; -// // Headless Horseman - Maniacal Laugh, Maniacal, other, Delayed 17 -// case 44000: break; -// // Energy Feedback -// case 44328: break; -// // Romantic Picnic -// case 45102: break; -// // Romantic Picnic -// case 45123: break; -// // Looking for Love -// case 45124: break; -// // Kite - Lightning Strike Kite Aura -// case 45197: break; -// // Rocket Chicken -// case 45202: break; -// // Copy Offhand Weapon -// case 45205: break; -// // Upper Deck - Kite - Lightning Periodic Aura -// case 45207: break; -// // Kite -Sky Lightning Strike Kite Aura -// case 45251: break; -// // Ribbon Pole Dancer Check Aura -// case 45390: break; -// // Holiday - Midsummer, Ribbon Pole Periodic Visual -// case 45406: break; -// // Parachute -// case 45472: break; -// // Alliance Flag, Extra Damage Debuff -// case 45898: break; -// // Horde Flag, Extra Damage Debuff -// case 45899: break; -// // Ahune - Summoning Rhyme Aura -// case 45926: break; -// // Ahune - Slippery Floor -// case 45945: break; -// // Ahune's Shield -// case 45954: break; -// // Nether Vapor Lightning -// case 45960: break; -// // Darkness -// case 45996: break; -// // Summon Blood Elves Periodic -// case 46041: break; -// // Transform Visual Missile Periodic -// case 46205: break; -// // Find Opening Beam End -// case 46333: break; -// // Ice Spear Control Aura -// case 46371: break; -// // Hailstone Chill -// case 46458: break; -// // Hailstone Chill, Internal -// case 46465: break; -// // Chill, Internal Shifter -// case 46549: break; -// // Summon Ice Spear Knockback Delayer -// case 46878: break; -// // Burninate Effect -// case 47214: break; -// // Fizzcrank Practice Parachute -// case 47228: break; -// // Send Mug Control Aura -// case 47369: break; -// // Direbrew's Disarm (precast) -// case 47407: break; -// // Mole Machine Port Schedule -// case 47489: break; -// case 47941: break; // Crystal Spike -// case 48200: break; // Healer Aura -// case 48630: break; // Summon Gauntlet Mobs Periodic -// case 49313: break; // Proximity Mine Area Aura -// // Mole Machine Portal Schedule -// case 49466: break; -// case 49555: break; // Corpse Explode -// case 49592: break; // Temporal Rift -// case 49957: break; // Cutting Laser -// case 50085: break; // Slow Fall -// // Listening to Music -// case 50493: break; -// // Love Rocket Barrage -// case 50530: break; case 58549: // Tenacity case 59911: // Tenacity (vehicle) GetParentAura()->RefreshAura(); break; -// Exist more after, need add later default: break; } @@ -6768,8 +6542,12 @@ void AuraEffect::PeriodicDummyTick() case SPELLFAMILY_MAGE: { // Mirror Image -// if (spell->Id == 55342) -// return; + if (spell->Id == 55342) + { + // Set name of summons to name of caster + m_target->CastSpell((Unit *)NULL, m_spellProto->EffectTriggerSpell[m_effIndex], true); + m_isPeriodic = false; + } break; } case SPELLFAMILY_WARLOCK: @@ -6881,9 +6659,6 @@ void AuraEffect::PeriodicDummyTick() } switch (spell->Id) { - // Harpooner's Mark - // case 40084: - // return; // Feeding Frenzy Rank 1 case 53511: if ( m_target->GetHealth() * 100 < m_target->GetMaxHealth() * 35 ) @@ -6935,26 +6710,46 @@ void AuraEffect::PeriodicDummyTick() return; } // Summon Gargoyle -// if (spell->SpellFamilyFlags & 0x0000008000000000LL) -// return; + // Being pursuaded by Gargoyle - AI related? + // if (spell->SpellFamilyFlags[1] & 0x00000080) + // break; + // Blood of the North + // Reaping // Death Rune Mastery -// if (spell->SpellFamilyFlags & 0x0000000000004000LL) -// return; - // Bladed Armor - if (spell->SpellIconID == 2653) + if (spell->SpellIconID == 3041 || spell->SpellIconID == 22 || spell->SpellIconID == 2622) { - // Increases your attack power by $s1 for every $s2 armor value you have. - // Calculate AP bonus (from 1 efect of this spell) - int32 apBonus = m_amount * m_target->GetArmor() / m_target->CalculateSpellDamage(spell, 1, spell->EffectBasePoints[1], m_target); - m_target->CastCustomSpell(m_target, 61217, &apBonus, &apBonus, 0, true, 0, this); + if (m_target->GetTypeId() != TYPEID_PLAYER) + return; + // Aura not used - prevent removing death runes from other effects + if (!GetAmount()) + return; + if(((Player*)m_target)->getClass() != CLASS_DEATH_KNIGHT) + return; + + // Remove death rune added on proc + for (uint8 i=0;i<MAX_RUNES && m_amount;++i) + { + if (m_spellProto->SpellIconID == 2622) + { + if (((Player*)m_target)->GetCurrentRune(i) != RUNE_DEATH || + ((Player*)m_target)->GetBaseRune(i) == RUNE_BLOOD ) + continue; + } + else + { + if (((Player*)m_target)->GetCurrentRune(i) != RUNE_DEATH || + ((Player*)m_target)->GetBaseRune(i) != RUNE_BLOOD ) + continue; + } + + if (!(m_amount & (1<<i))) + continue; + + ((Player*)m_target)->ConvertRune(i,((Player*)m_target)->GetBaseRune(i)); + } + m_amount = 0; return; } - // Reaping -// if (spell->SpellIconID == 22) -// return; - // Blood of the North -// if (spell->SpellIconID == 30412) -// return; break; } default: @@ -6992,7 +6787,7 @@ void AuraEffect::HandleManaShield(bool apply, bool Real, bool changeAmount) switch(m_spellProto->SpellFamilyName) { case SPELLFAMILY_MAGE: - if(m_spellProto->SpellFamilyFlags[0] & 0x8000) + if(m_spellProto->SpellFamilyFlags[0] & 0x8000 && m_spellProto->SpellFamilyFlags[2] & 0x8) { // Mana Shield // +50% from +spd bonus @@ -7075,26 +6870,32 @@ void AuraEffect::HandleAuraConvertRune(bool apply, bool Real, bool /*changeAmoun if(plr->getClass() != CLASS_DEATH_KNIGHT) return; - // how to determine what rune need to be converted? - for(uint32 i = 0; i < MAX_RUNES; ++i) + uint32 runes = 0; + // convert number of runes specified in aura amount of rune type in miscvalue to runetype in miscvalueb + for(uint32 i = 0; i < MAX_RUNES && m_amount; ++i) { if(apply) { + if (GetMiscValue() != plr->GetCurrentRune(i)) + continue; if(!plr->GetRuneCooldown(i)) { plr->ConvertRune(i, GetSpellProto()->EffectMiscValueB[m_effIndex]); - break; + runes |= 1<<i; + --m_amount; } } else { if(plr->GetCurrentRune(i) == GetSpellProto()->EffectMiscValueB[m_effIndex]) { - plr->ConvertRune(i, plr->GetBaseRune(i)); - break; + if (m_amount & (1<<i)) + plr->ConvertRune(i, plr->GetBaseRune(i)); } } } + if (apply) + m_amount = runes; } // Control Auras @@ -7130,12 +6931,7 @@ void AuraEffect::HandleModPossess(bool apply, bool Real, bool /*changeAmount*/) } if(apply) - { - if(m_target->getLevel() > m_amount) - return; - m_target->SetCharmedBy(caster, CHARM_TYPE_POSSESS); - } else m_target->RemoveCharmedBy(caster); } @@ -7153,7 +6949,6 @@ void AuraEffect::HandleModPossessPet(bool apply, bool Real, bool /*changeAmount* { if(caster->GetGuardianPet() != m_target) return; - m_target->SetCharmedBy(caster, CHARM_TYPE_POSSESS); } else @@ -7164,7 +6959,7 @@ void AuraEffect::HandleModPossessPet(bool apply, bool Real, bool /*changeAmount* ((Player*)caster)->PetSpellInitialize(); if(!m_target->getVictim()) { - m_target->GetMotionMaster()->MoveFollow(caster, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + m_target->GetMotionMaster()->MoveFollow(caster, PET_FOLLOW_DIST, m_target->GetFollowAngle()); m_target->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW); } } @@ -7178,12 +6973,7 @@ void AuraEffect::HandleModCharm(bool apply, bool Real, bool /*changeAmount*/) Unit* caster = GetCaster(); if(apply) - { - if(m_amount && int32(m_target->getLevel()) > m_amount) - return; - m_target->SetCharmedBy(caster, CHARM_TYPE_CHARM); - } else m_target->RemoveCharmedBy(caster); } @@ -7196,12 +6986,7 @@ void AuraEffect::HandleCharmConvert(bool apply, bool Real, bool /*changeAmount*/ Unit* caster = GetCaster(); if(apply) - { - if(m_amount && int32(m_target->getLevel()) > m_amount) - return; - m_target->SetCharmedBy(caster, CHARM_TYPE_CONVERT); - } else m_target->RemoveCharmedBy(caster); } @@ -7268,7 +7053,7 @@ void AuraEffect::HandleAuraSafeFall( bool Apply, bool Real , bool /*changeAmount { // implemented in WorldSession::HandleMovementOpcodes - // only special case + // Buffeting Winds of Susurrus - only special case if(Apply && Real && GetId()==32474 && m_target->GetTypeId()==TYPEID_PLAYER) ((Player*)m_target)->ActivateTaxiPathTo(506,GetId()); } @@ -7286,12 +7071,13 @@ void AuraEffect::HandleReflectSpells( bool Apply, bool Real , bool /*changeAmoun // implemented in Unit::SpellHitResult // only special case - if(!Apply && Real && m_spellProto->SpellFamilyName == SPELLFAMILY_WARRIOR && m_spellProto->SpellFamilyFlags[1] & 0x2) + if(!Apply && Real && GetParentAura()->GetRemoveMode() != AURA_REMOVE_BY_DEFAULT + && m_spellProto->SpellFamilyName == SPELLFAMILY_WARRIOR && m_spellProto->SpellFamilyFlags[1] & 0x2) { if (Unit * caster = GetCaster()) { // Improved Spell Reflection - if (caster->GetDummyAura(SPELLFAMILY_WARRIOR,1935)) + if (caster->GetDummyAura(SPELLFAMILY_WARRIOR,1935, 1)) { // aura remove - remove auras from all party members std::list<Unit*> PartyMembers; @@ -7305,4 +7091,98 @@ void AuraEffect::HandleReflectSpells( bool Apply, bool Real , bool /*changeAmoun } } } +void AuraEffect::HandleAuraInitializeImages( bool Apply, bool Real , bool /*changeAmount*/) +{ + if (!Real) + return; + if (Apply) + { + Unit * caster = GetCaster(); + if (!caster) + return; + // Set item visual + if (caster->GetTypeId()== TYPEID_PLAYER) + { + if (Item const * item = ((Player *)caster)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND)) + m_target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, item->GetProto()->ItemId); + if (Item const * item = ((Player *)caster)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) + m_target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, item->GetProto()->ItemId); + } + else // TYPEID_UNIT + { + m_target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID)); + m_target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1)); + m_target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2)); + } + } + else + { + // Remove equipment visual + if (m_target->GetTypeId() == TYPEID_PLAYER) + { + for (uint8 i = 0; i < 3; ++i) + m_target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i, 0); + } + else // TYPEID_UNIT + { + ((Creature*)m_target)->LoadEquipment(((Creature*)m_target)->GetEquipmentId()); + } + } +} + +void AuraEffect::HandleAuraCloneCaster( bool Apply, bool Real , bool /*changeAmount*/) +{ + if (!Real) + return; + + if (Apply) + { + Unit * caster = GetCaster(); + if (!caster) + return; + // Set display id (probably for portrait?) + m_target->SetDisplayId(caster->GetDisplayId()); + m_target->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); + } + else + { + m_target->SetDisplayId(m_target->GetNativeDisplayId()); + m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); + } +} + +int32 AuraEffect::CalculateCrowdControlAuraAmount(Unit * caster) +{ + // Damage cap for CC effects + if (!m_spellProto->procFlags) + return 0; + + if (m_auraName !=SPELL_AURA_MOD_CONFUSE && + m_auraName !=SPELL_AURA_MOD_FEAR && + m_auraName !=SPELL_AURA_MOD_STUN && + m_auraName !=SPELL_AURA_MOD_ROOT && + m_auraName !=SPELL_AURA_TRANSFORM) + return 0; + + int32 damageCap = (int32)(m_target->GetMaxHealth()*0.10f); + + if (!caster) + return damageCap; + + // Glyphs increasing damage cap + Unit::AuraEffectList const& overrideClassScripts = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for(Unit::AuraEffectList::const_iterator itr = overrideClassScripts.begin();itr != overrideClassScripts.end(); ++itr) + { + if((*itr)->isAffectedOnSpell(m_spellProto)) + { + // Glyph of Fear, Glyph of Frost nova and similar auras + if ((*itr)->GetMiscValue() == 7801) + { + damageCap += (int32)(damageCap*(*itr)->GetAmount()/100.0f); + break; + } + } + } + return damageCap; +} diff --git a/src/game/SpellAuras.h b/src/game/SpellAuras.h index 987c8b0db25..6a1c11e49c9 100644 --- a/src/game/SpellAuras.h +++ b/src/game/SpellAuras.h @@ -84,8 +84,6 @@ class TRINITY_DLL_SPEC Aura int8 GetStackAmount() const {return m_stackAmount;} void SetStackAmount(uint8 num, bool applied = true); bool modStackAmount(int32 num); // return true if last charge dropped - uint32 GetAuraStateMask(){return m_auraStateMask;} - void SetAuraState(uint8 num){m_auraStateMask |= 1<<(num-1);} //modifies auras' aura state (not unit!) void SetRemoveMode(AuraRemoveMode mode) { m_removeMode = mode; } uint8 GetRemoveMode() const {return m_removeMode;} @@ -119,6 +117,7 @@ class TRINITY_DLL_SPEC Aura m_target->HandleAuraEffect(m_partAuras[i], apply); } void ApplyAllModifiers(bool apply, bool Real=false); + void HandleAuraSpecificMods(bool apply); void Update(uint32 diff); @@ -153,7 +152,6 @@ class TRINITY_DLL_SPEC Aura uint8 m_auraLevel; // Aura level (store caster level for correct show level dep amount) uint8 m_procCharges; // Aura charges (0 for infinite) uint8 m_stackAmount; // Aura stack amount - uint32 m_auraStateMask; AuraEffect * m_partAuras[3]; uint32 m_procDamage; // used in aura proc code @@ -334,6 +332,10 @@ class TRINITY_DLL_SPEC AuraEffect void HandleCharmConvert(bool apply, bool Real, bool changeAmount); void HandleReflectSpells( bool Apply, bool Real , bool changeAmount); void HandleModArmorPenetrationPct(bool Apply, bool Real, bool changeAmount); + void HandleAuraInitializeImages(bool Apply, bool Real, bool changeAmount); + void HandleAuraCloneCaster(bool Apply, bool Real, bool changeAmount); + + int32 CalculateCrowdControlAuraAmount(Unit * caster); // add/remove SPELL_AURA_MOD_SHAPESHIFT (36) linked auras void HandleShapeshiftBoosts(bool apply); @@ -348,9 +350,12 @@ class TRINITY_DLL_SPEC AuraEffect uint32 GetEffIndex() const { return m_effIndex; } int32 GetBasePoints() const { return m_currentBasePoints; } int32 GetAuraAmplitude(){return m_amplitude;} + void ResetPeriodicTimer(){m_periodicTimer = m_amplitude;} + virtual void Update(uint32 diff); uint32 GetTickNumber() const { return m_tickNumber; } + int32 GetTotalTicks () const { return m_amplitude ? (GetParentAura()->GetAuraMaxDuration() / m_amplitude) : 1;} bool IsAreaAura() const { return m_isAreaAura; } bool IsPeriodic() const { return m_isPeriodic; } bool IsPersistent() const { return m_isPersistent; } diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index 6d1c986d7b2..f68488e538b 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -123,7 +123,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectSummonPet, // 56 SPELL_EFFECT_SUMMON_PET &Spell::EffectLearnPetSpell, // 57 SPELL_EFFECT_LEARN_PET_SPELL &Spell::EffectWeaponDmg, // 58 SPELL_EFFECT_WEAPON_DAMAGE - &Spell::EffectOpenSecretSafe, // 59 SPELL_EFFECT_OPEN_LOCK_ITEM + &Spell::EffectCreateRandomItem, // 59 SPELL_EFFECT_CREATE_RANDOM_ITEM create item base at spell specific loot &Spell::EffectProficiency, // 60 SPELL_EFFECT_PROFICIENCY &Spell::EffectSendEvent, // 61 SPELL_EFFECT_SEND_EVENT &Spell::EffectPowerBurn, // 62 SPELL_EFFECT_POWER_BURN @@ -196,7 +196,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectApplyAreaAura, //129 SPELL_EFFECT_APPLY_AREA_AURA_ENEMY &Spell::EffectRedirectThreat, //130 SPELL_EFFECT_REDIRECT_THREAT &Spell::EffectUnused, //131 SPELL_EFFECT_131 used in some test spells - &Spell::EffectNULL, //132 SPELL_EFFECT_PLAY_MUSIC sound id in misc value (SoundEntries.dbc) + &Spell::EffectPlayMusic, //132 SPELL_EFFECT_PLAY_MUSIC sound id in misc value (SoundEntries.dbc) &Spell::EffectUnlearnSpecialization, //133 SPELL_EFFECT_UNLEARN_SPECIALIZATION unlearn profession specialization &Spell::EffectKillCredit, //134 SPELL_EFFECT_KILL_CREDIT misc value is creature entry &Spell::EffectNULL, //135 SPELL_EFFECT_CALL_PET @@ -213,7 +213,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectActivateRune, //146 SPELL_EFFECT_ACTIVATE_RUNE &Spell::EffectQuestFail, //147 SPELL_EFFECT_QUEST_FAIL quest fail &Spell::EffectUnused, //148 SPELL_EFFECT_148 unused - &Spell::EffectNULL, //149 SPELL_EFFECT_149 swoop + &Spell::EffectCharge2, //149 SPELL_EFFECT_CHARGE2 swoop &Spell::EffectUnused, //150 SPELL_EFFECT_150 unused &Spell::EffectTriggerRitualOfSummoning, //151 SPELL_EFFECT_TRIGGER_SPELL_2 &Spell::EffectNULL, //152 SPELL_EFFECT_152 summon Refer-a-Friend @@ -221,7 +221,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectNULL, //154 unused &Spell::EffectTitanGrip, //155 SPELL_EFFECT_TITAN_GRIP Allows you to equip two-handed axes, maces and swords in one hand, but you attack $49152s1% slower than normal. &Spell::EffectEnchantItemPrismatic, //156 SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC - &Spell::EffectCreateItem2, //157 SPELL_EFFECT_CREATE_ITEM_2 create/learn item/spell for profession + &Spell::EffectCreateItem2, //157 SPELL_EFFECT_CREATE_ITEM_2 create item or create item template and replace by some randon spell loot item &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 @@ -315,18 +315,14 @@ void Spell::EffectSchoolDMG(uint32 effect_idx) void Spell::SpellDamageSchoolDmg(uint32 effect_idx) { + bool apply_direct_bonus=true; + if( unitTarget && unitTarget->isAlive()) { switch(m_spellInfo->SpellFamilyName) { case SPELLFAMILY_GENERIC: { - //Gore - if(m_spellInfo->SpellIconID == 2269 ) - { - damage += (rand()%2 ? damage : 0); - } - // Meteor like spells (divided damage to targets) if(m_customAttr & SPELL_ATTR_CU_SHARE_DAMAGE) { @@ -390,13 +386,6 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) damage = 200; break; } - // Intercept (warrior spell trigger) - case 20253: - case 61491: - { - damage+= uint32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.12f); - break; - } // arcane charge. must only affect demons (also undead?) case 45072: { @@ -430,9 +419,7 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) { // Bloodthirst if(m_spellInfo->SpellFamilyFlags[1] & 0x400) - { damage = uint32(damage * (m_caster->GetTotalAttackPowerValue(BASE_ATTACK)) / 100); - } // Shield Slam else if(m_spellInfo->SpellFamilyFlags[1] & 0x200 && m_spellInfo->Category==1209) damage += int32(m_caster->GetShieldBlockValue()); @@ -442,12 +429,6 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) damage = uint32(damage * m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100); m_caster->ModifyAuraState(AURA_STATE_WARRIOR_VICTORY_RUSH, false); } - // Revenge ${$m1+$AP*0.207} to ${$M1+$AP*0.207} - else if(m_spellInfo->SpellFamilyFlags[0] & 0x400) - damage+= uint32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.207f); - // Heroic Throw ${$m1+$AP*.50} - else if(m_spellInfo->SpellFamilyFlags[1] & 0x00000001) - damage+= uint32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.5f); // Shockwave ${$m3/100*$AP} else if(m_spellInfo->SpellFamilyFlags[1] & 0x00008000) { @@ -456,12 +437,6 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) damage+= int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * pct / 100); break; } - // Thunder Clap - else if(m_spellInfo->SpellFamilyFlags[0] & 0x80) - { - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 12 / 100); - break; - } break; } case SPELLFAMILY_WARLOCK: @@ -469,26 +444,51 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) // Incinerate Rank 1 & 2 if((m_spellInfo->SpellFamilyFlags[1] & 0x000040) && m_spellInfo->SpellIconID==2128) { - // Incinerate does more dmg (dmg*0.25) if the target is Immolated. - if(unitTarget->HasAuraState(AURA_STATE_IMMOLATE, m_spellInfo, m_caster)) - damage += int32(damage*0.25f); + // Incinerate does more dmg (dmg*0.25) if the target have Immolate debuff. + // Check aura state for speed but aura state set not only for Immolate spell + if(unitTarget->HasAuraState(AURA_STATE_CONFLAGRATE)) + { + if (unitTarget->GetAura(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_WARLOCK, 0x4)) + damage += damage/4; + } } - // Conflagrate - consumes immolate - else if (m_spellInfo->TargetAuraState == AURA_STATE_IMMOLATE) + // Conflagrate - consumes Immolate or Shadowflame + else if (m_spellInfo->TargetAuraState == AURA_STATE_CONFLAGRATE) { - // Glyph of Conflagrate - if (m_caster->HasAura(56235)) - break; - // for caster applied auras only + AuraEffect const* aura = NULL; // found req. aura for damage calculation + Unit::AuraEffectList const &mPeriodic = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); for(Unit::AuraEffectList::const_iterator i = mPeriodic.begin(); i != mPeriodic.end(); ++i) { - if( (*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && ((*i)->GetSpellProto()->SpellFamilyFlags[0] & 4 || (*i)->GetSpellProto()->SpellFamilyFlags[2] & 2) && - (*i)->GetCasterGUID()==m_caster->GetGUID() ) + // for caster applied auras only + if ((*i)->GetSpellProto()->SpellFamilyName != SPELLFAMILY_WARLOCK || + (*i)->GetCasterGUID()!=m_caster->GetGUID()) + continue; + + // Immolate + if ((*i)->GetSpellProto()->SpellFamilyFlags[0] & 0x4) { - unitTarget->RemoveAurasDueToSpell((*i)->GetId(), m_caster->GetGUID()); + aura = *i; // it selected always if exist break; } + + // Shadowflame + if ((*i)->GetSpellProto()->SpellFamilyFlags[2] & 0x00000002) + aura = *i; // remember but wait possible Immolate as primary priority + } + + // found Immolate or Shadowflame + if (aura) + { + uint32 pdamage = aura->GetAmount() > 0 ? aura->GetAmount() : 0; + pdamage = m_caster->SpellDamageBonus(unitTarget, aura->GetSpellProto(), pdamage, DOT, aura->GetParentAura()->GetStackAmount()); + damage += pdamage * 4; // 4 ticks of 3 seconds = 12 secs + apply_direct_bonus = false; + // Glyph of Conflagrate + if (!m_caster->HasAura(56235)) + unitTarget->RemoveAurasDueToSpell(aura->GetId(), m_caster->GetGUID()); + + break; } } // Shadow Bite @@ -517,31 +517,9 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) // converts each extra point of energy into ($f1+$AP/410) additional damage float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); float multiple = ap / 410 + m_spellInfo->DmgMultiplier[effect_idx]; - damage += int32(m_caster->GetPower(POWER_ENERGY) * multiple); + int32 energy = -(m_caster->ModifyPower(POWER_ENERGY, -30)); + damage += int32(energy * multiple); damage += int32(((Player*)m_caster)->GetComboPoints() * ap * 7 / 100); - m_caster->SetPower(POWER_ENERGY,0); - } - // Rake - else if(m_spellInfo->SpellFamilyFlags[0] & 0x1000) - { - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100); - } - // Swipe - else if(m_spellInfo->SpellFamilyFlags[1] & 0x00100000) - { - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.08f); - } - //Mangle Bonus for the initial damage of Lacerate and Rake - if((m_spellInfo->SpellFamilyFlags.IsEqual(0x1000,0,0) && m_spellInfo->SpellIconID==494) || - (m_spellInfo->SpellFamilyFlags.IsEqual(0,0x100,0) && m_spellInfo->SpellIconID==2246)) - { - Unit::AuraEffectList const& mDummyAuras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraEffectList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i) - if((*i)->GetSpellProto()->SpellFamilyFlags[1] & 0x00000440 && (*i)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_DRUID) - { - damage = int32(damage*(100.0f+(*i)->GetAmount())/100.0f); - break; - } } break; } @@ -553,22 +531,13 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) // consume from stack dozes not more that have combo-points if(uint32 combo = ((Player*)m_caster)->GetComboPoints()) { - Aura *poison = 0; // Lookup for Deadly poison (only attacker applied) - Unit::AuraEffectList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); - for(Unit::AuraEffectList::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr) - if( (*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_ROGUE && - (*itr)->GetSpellProto()->SpellFamilyFlags[0] & 0x10000 && - (*itr)->GetCasterGUID()==m_caster->GetGUID() ) - { - poison = (*itr)->GetParentAura(); - break; - } - // count consumed deadly poison doses at target - if (poison) + if (AuraEffect const * aurEff = unitTarget->GetAura(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_ROGUE, 0x10000, 0, 0, m_caster->GetGUID())) { - uint32 spellId = poison->GetId(); - uint32 doses = poison->GetStackAmount(); + // count consumed deadly poison doses at target + Aura *poison = 0; + uint32 spellId = aurEff->GetId(); + uint32 doses = aurEff->GetParentAura()->GetStackAmount(); if (doses > combo) doses = combo; for (int i=0; i< doses; i++) @@ -577,7 +546,7 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) damage += int32(((Player*)m_caster)->GetTotalAttackPowerValue(BASE_ATTACK) * 0.03f * doses); } // Eviscerate and Envenom Bonus Damage (item set effect) - if(m_caster->GetDummyAura(37169)) + if(m_caster->HasAura(37169)) damage += ((Player*)m_caster)->GetComboPoints()*40; } } @@ -590,52 +559,24 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) damage += irand(int32(ap * combo * 0.03f), int32(ap * combo * 0.07f)); // Eviscerate and Envenom Bonus Damage (item set effect) - if(m_caster->GetDummyAura(37169)) + if(m_caster->HasAura(37169)) damage += combo*40; } } - // Gouge - else if(m_spellInfo->SpellFamilyFlags[0] & 0x8) - { - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.21f); - } - // Instant Poison - else if(m_spellInfo->SpellFamilyFlags[0] & 0x2000) - { - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.10f); - } - // Wound Poison - else if(m_spellInfo->SpellFamilyFlags[0] & 0x10000000) - { - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.04f); - } break; } case SPELLFAMILY_HUNTER: { - // Mongoose Bite - if((m_spellInfo->SpellFamilyFlags[0] & 0x2) && m_spellInfo->SpellVisual[0]==342) - { - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2f); - } - // Counterattack - else if(m_spellInfo->SpellFamilyFlags[1] & 0x00080000) - { - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2f); - } - // Arcane Shot - else if((m_spellInfo->SpellFamilyFlags[0] & 0x00000800) && m_spellInfo->maxLevel > 0) + //Gore + if (m_spellInfo->SpellIconID == 1578) { - damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.15f); + if (m_caster->HasAura(57627)) // Charge 6 sec post-affect + damage *= 2; } // Steady Shot else if(m_spellInfo->SpellFamilyFlags[1] & 0x1) { - int32 base = irand((int32)m_caster->GetWeaponDamageRange(RANGED_ATTACK, MINDAMAGE),(int32)m_caster->GetWeaponDamageRange(RANGED_ATTACK, MAXDAMAGE)); - damage += int32(float(base)/m_caster->GetAttackTime(RANGED_ATTACK)*2800 + m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.1f); - bool found = false; - // check dazed affect Unit::AuraEffectList const& decSpeedList = unitTarget->GetAurasByType(SPELL_AURA_MOD_DECREASE_SPEED); for(Unit::AuraEffectList::const_iterator iter = decSpeedList.begin(); iter != decSpeedList.end(); ++iter) @@ -651,29 +592,12 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) if(found) damage += m_spellInfo->EffectBasePoints[1]; } - // Explosive Trap Effect - else if(m_spellInfo->SpellFamilyFlags[0] & 0x00000004) - { - damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.1f); - } break; } case SPELLFAMILY_PALADIN: { - // Avenger's Shield ($m1+0.07*$SPH+0.07*$AP) - if(m_spellInfo->SpellFamilyFlags[0] & 0x4000) - { - float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); - damage += int32(ap * 0.07f); - } - // Hammer of Wrath ($m1+0.15*$SPH+0.15*$AP) - else if(m_spellInfo->SpellFamilyFlags[1] & 0x00000080) - { - float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); - damage += int32(ap * 0.15f); - } // Hammer of the Righteous - else if(m_spellInfo->SpellFamilyFlags[1]&0x00040000) + if(m_spellInfo->SpellFamilyFlags[1]&0x00040000) { // Add main hand dps * effect[2] amount float average = (m_caster->GetFloatValue(UNIT_FIELD_MINDAMAGE) + m_caster->GetFloatValue(UNIT_FIELD_MAXDAMAGE)) / 2; @@ -683,13 +607,23 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) // Shield of Righteousness else if(m_spellInfo->SpellFamilyFlags[1]&0x00100000) { - damage+=int32(m_caster->GetShieldBlockValue()); + damage += int32(m_caster->GetShieldBlockValue() * 1.3f); + } + break; + } + case SPELLFAMILY_DEATHKNIGHT: + { + // Blood Boil - bonus for diseased targets + if (m_spellInfo->SpellFamilyFlags[0] & 0x00040000 && unitTarget->GetAura(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DEATHKNIGHT, 0, 0, 0x00000002, m_caster->GetGUID())) + { + damage += m_damage / 2; + damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)* 0.035f); } break; } } - if(m_originalCaster && damage > 0) + if(m_originalCaster && damage > 0 && apply_direct_bonus) damage = m_originalCaster->SpellDamageBonus(unitTarget, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE); m_damage += damage; @@ -740,14 +674,11 @@ void Spell::EffectDummy(uint32 i) { if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || ((Creature*)unitTarget)->isPet()) return; - Creature* creatureTarget = (Creature*)unitTarget; GameObject* pGameObj = new GameObject; - if (!creatureTarget || !pGameObj) return; - - if (!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), 181574, creatureTarget->GetMap(), creatureTarget->GetPhaseMask(), - creatureTarget->GetPositionX(), creatureTarget->GetPositionY(), creatureTarget->GetPositionZ(), - creatureTarget->GetOrientation(), 0, 0, 0, 0, 100, GO_STATE_READY)) + if (!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), 181574, unitTarget->GetMap(), unitTarget->GetPhaseMask(), + unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), + unitTarget->GetOrientation(), 0, 0, 0, 0, 100, GO_STATE_READY)) { delete pGameObj; return; @@ -758,7 +689,7 @@ void Spell::EffectDummy(uint32 i) //pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()); pGameObj->SetSpellId(m_spellInfo->Id); - MapManager::Instance().GetMap(creatureTarget->GetMapId(), pGameObj)->Add(pGameObj); + unitTarget->GetMap()->Add(pGameObj); WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8); data << uint64(pGameObj->GetGUID()); @@ -841,15 +772,24 @@ void Spell::EffectDummy(uint32 i) switch (m_spellInfo->Id) { - case 12850: damage *= 0.2f; break; - case 12162: damage *= 0.4f; break; - case 12868: damage *= 0.6f; break; + case 12162: damage *= 0.16f; break; // Rank 1 + case 12850: damage *= 0.32f; break; // Rank 2 + case 12868: damage *= 0.48f; break; // Rank 3 default: sLog.outError("Spell::EffectDummy: Spell %u not handled in DW",m_spellInfo->Id); return; }; - int32 deepWoundsDotBasePoints0 = int32(damage / 4); + // get remaining damage of old Deep Wound aura + AuraEffect* deepWound = unitTarget->GetAuraEffect(12721, 0); + if(deepWound) + { + int32 remainingTicks = deepWound->GetParentAura()->GetAuraDuration() / deepWound->GetAuraAmplitude(); + damage += remainingTicks * deepWound->GetAmount(); + } + + // 1 tick/sec * 6 sec = 6 ticks + int32 deepWoundsDotBasePoints0 = int32(damage / 6); m_caster->CastCustomSpell(unitTarget, 12721, &deepWoundsDotBasePoints0, NULL, NULL, true, NULL); return; } @@ -895,9 +835,7 @@ void Spell::EffectDummy(uint32 i) if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) return; - Creature* creatureTarget = (Creature*)unitTarget; - - creatureTarget->ForcedDespawn(); + ((Creature*)unitTarget)->ForcedDespawn(); return; } case 16589: // Noggenfogger Elixir @@ -934,7 +872,9 @@ void Spell::EffectDummy(uint32 i) if(!itemTarget && m_caster->GetTypeId()!=TYPEID_PLAYER) return; - uint32 spell_id = roll_chance_i(50) ? 17269 : 17270; + uint32 spell_id = roll_chance_i(50) + ? 17269 // Create Resonating Skull + : 17270; // Create Bone Dust m_caster->CastSpell(m_caster, spell_id, true, NULL); return; @@ -949,8 +889,6 @@ void Spell::EffectDummy(uint32 i) return; Creature* creatureTarget = (Creature*)unitTarget; - if(creatureTarget->isPet()) - return; GameObject* Crystal_Prison = m_caster->SummonGameObject(179644, creatureTarget->GetPositionX(), creatureTarget->GetPositionY(), creatureTarget->GetPositionZ(), creatureTarget->GetOrientation(), 0, 0, 0, 0, creatureTarget->GetRespawnTime()-time(NULL)); sLog.outDebug("SummonGameObject at SpellEfects.cpp EffectDummy for Spell 23019"); @@ -979,25 +917,25 @@ void Spell::EffectDummy(uint32 i) if (!m_CastItem) return; m_caster->CastSpell(m_caster, 13166, true, m_CastItem); return; - case 23448: // Ultrasafe Transporter: Gadgetzan - backfires + case 23448: // Transporter Arrival - Ultrasafe Transporter: Gadgetzan - backfires { int32 r = irand(0, 119); - if ( r < 20 ) // 1/6 polymorph + if ( r < 20 ) // Transporter Malfunction - 1/6 polymorph m_caster->CastSpell(m_caster, 23444, true); - else if ( r < 100 ) // 4/6 evil twin + else if ( r < 100 ) // Evil Twin - 4/6 evil twin m_caster->CastSpell(m_caster, 23445, true); - else // 1/6 miss the target + else // Transporter Malfunction - 1/6 miss the target m_caster->CastSpell(m_caster, 36902, true); return; } - case 23453: // Ultrasafe Transporter: Gadgetzan - if ( roll_chance_i(50) ) // success + case 23453: // Gnomish Transporter - Ultrasafe Transporter: Gadgetzan + if ( roll_chance_i(50) ) // Gadgetzan Transporter - success m_caster->CastSpell(m_caster, 23441, true); - else // failure + else // Gadgetzan Transporter Failure - failure m_caster->CastSpell(m_caster, 23446, true); return; case 23645: // Hourglass Sand - m_caster->RemoveAurasDueToSpell(23170); + m_caster->RemoveAurasDueToSpell(23170); // Brood Affliction: Bronze return; case 23725: // Gift of Life (warrior bwl trinket) m_caster->CastSpell(m_caster, 23782, true); @@ -1015,23 +953,30 @@ void Spell::EffectDummy(uint32 i) //5 different spells used depending on mounted speed and if mount can fly or not if (flyspeed >= 4.1f) + // Flying Reindeer m_caster->CastSpell(m_caster, 44827, true); //310% flying Reindeer else if (flyspeed >= 3.8f) + // Flying Reindeer m_caster->CastSpell(m_caster, 44825, true); //280% flying Reindeer else if (flyspeed >= 1.6f) + // Flying Reindeer m_caster->CastSpell(m_caster, 44824, true); //60% flying Reindeer else if (speed >= 2.0f) + // Reindeer m_caster->CastSpell(m_caster, 25859, true); //100% ground Reindeer else + // Reindeer m_caster->CastSpell(m_caster, 25858, true); //60% ground Reindeer return; } - //case 26074: // Holiday Cheer - // return; -- implemented at client side + case 26074: // Holiday Cheer + // implemented at client side + return; case 28006: // Arcane Cloaking { if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER ) + // Naxxramas Entry Flag Effect DND m_caster->CastSpell(unitTarget, 29294, true); return; } @@ -1050,7 +995,9 @@ void Spell::EffectDummy(uint32 i) if( m_caster->GetTypeId() != TYPEID_PLAYER ) return; - uint32 spell_id = roll_chance_i(50) ? 29277 : 29278; + uint32 spell_id = roll_chance_i(50) + ? 29277 // Summon Purified Helboar Meat + : 29278; // Summon Toxic Helboar Meat m_caster->CastSpell(m_caster,spell_id,true,NULL); return; @@ -1062,26 +1009,19 @@ void Spell::EffectDummy(uint32 i) return; case 30458: // Nigh Invulnerability if (!m_CastItem) return; - if(roll_chance_i(86)) // success + if(roll_chance_i(86)) // Nigh-Invulnerability - success m_caster->CastSpell(m_caster, 30456, true, m_CastItem); - else // backfire in 14% casts + else // Complete Vulnerability - backfire in 14% casts m_caster->CastSpell(m_caster, 30457, true, m_CastItem); return; case 30507: // Poultryizer if (!m_CastItem) return; - if(roll_chance_i(80)) // success + if(roll_chance_i(80)) // Poultryized! - success m_caster->CastSpell(unitTarget, 30501, true, m_CastItem); - else // backfire 20% + else // Poultryized! - backfire 20% m_caster->CastSpell(unitTarget, 30504, true, m_CastItem); return; - case 55004: //Nitro Boosts - if(!m_CastItem) return; - if(roll_chance_i(95)) //success - m_caster->CastSpell(m_caster, 54861, true, m_CastItem); - else //backfire 5% - m_caster->CastSpell(m_caster, 46014, true, m_CastItem); - return; - case 33060: // Make a Wish + case 33060: // Make a Wish { if(m_caster->GetTypeId()!=TYPEID_PLAYER) return; @@ -1090,23 +1030,23 @@ void Spell::EffectDummy(uint32 i) switch(urand(1,5)) { - case 1: spell_id = 33053; break; - case 2: spell_id = 33057; break; - case 3: spell_id = 33059; break; - case 4: spell_id = 33062; break; - case 5: spell_id = 33064; break; + case 1: spell_id = 33053; break; // Mr Pinchy's Blessing + case 2: spell_id = 33057; break; // Summon Mighty Mr. Pinchy + case 3: spell_id = 33059; break; // Summon Furious Mr. Pinchy + case 4: spell_id = 33062; break; // Tiny Magical Crawdad + case 5: spell_id = 33064; break; // Mr. Pinchy's Gift } m_caster->CastSpell(m_caster, spell_id, true, NULL); return; } - case 35745: + case 35745: // Socrethar's Stone { uint32 spell_id; switch(m_caster->GetAreaId()) { - case 3900: spell_id = 35743; break; - case 3742: spell_id = 35744; break; + case 3900: spell_id = 35743; break; // Socrethar Portal + case 3742: spell_id = 35744; break; // Socrethar Portal default: return; } @@ -1171,9 +1111,7 @@ void Spell::EffectDummy(uint32 i) if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) return; - Creature* creatureTarget = (Creature*)unitTarget; - - creatureTarget->ForcedDespawn(); + ((Creature*)unitTarget)->ForcedDespawn(); //cast spell Raptor Capture Credit m_caster->CastSpell(m_caster, 42337, true, NULL); @@ -1181,33 +1119,15 @@ void Spell::EffectDummy(uint32 i) } case 34665: //Administer Antidote { - if(!unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER ) + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT + || unitTarget->GetEntry() != 16880 || ((Creature*)unitTarget)->isPet()) return; - if(!unitTarget) - return; - - TempSummon* tempSummon = dynamic_cast<TempSummon*>(unitTarget); - if(!tempSummon) - return; - - uint32 health = tempSummon->GetHealth(); + ((Creature*)unitTarget)->UpdateEntry(16992); + ((Player*)m_caster)->RewardPlayerAndGroupAtEvent(16992, unitTarget); - float x = tempSummon->GetPositionX(); - float y = tempSummon->GetPositionY(); - float z = tempSummon->GetPositionZ(); - float o = tempSummon->GetOrientation(); - tempSummon->UnSummon(); - - Creature* pCreature = m_caster->SummonCreature(16992, x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,180000); - if (!pCreature) - return; - - pCreature->SetHealth(health); - ((Player*)m_caster)->RewardPlayerAndGroupAtEvent(16992, pCreature); - - if (pCreature->IsAIEnabled) - pCreature->AI()->AttackStart(m_caster); + if (unitTarget->IsAIEnabled) + ((Creature*)unitTarget)->AI()->AttackStart(m_caster); return; } @@ -1223,6 +1143,13 @@ void Spell::EffectDummy(uint32 i) m_caster->CastSpell(m_caster, 45088, true); return; } + case 55004: // Nitro Boosts + if(!m_CastItem) return; + if(roll_chance_i(95)) // Nitro Boosts - success + m_caster->CastSpell(m_caster, 54861, true, m_CastItem); + else // Knocked Up - backfire 5% + m_caster->CastSpell(m_caster, 46014, true, m_CastItem); + return; case 50243: // Teach Language { if(m_caster->GetTypeId() != TYPEID_PLAYER) @@ -1260,13 +1187,11 @@ void Spell::EffectDummy(uint32 i) if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) return; - Creature* creatureTarget = (Creature*)unitTarget; - - creatureTarget->ForcedDespawn(); + ((Creature*)unitTarget)->ForcedDespawn(); return; } - case 52308: + case 52308: // Take Sputum Sample { switch(i) { @@ -1289,15 +1214,31 @@ void Spell::EffectDummy(uint32 i) return; m_caster->CastCustomSpell(unitTarget, 52752, &damage, NULL, NULL, true); return; - case 53341: - case 53343: + case 53341: // Rune of Cinderglacier + case 53343: // Rune of Razorice { + // Runeforging Credit m_caster->CastSpell(m_caster, 54586, true); return; } case 58418: // Portal to Orgrimmar case 58420: // Portal to Stormwind return; // implemented in EffectScript[0] + case 59640: // Underbelly Elixir + { + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + uint32 spell_id = 0; + switch(urand(1,3)) + { + case 1: spell_id = 59645; break; + case 2: spell_id = 59831; break; + case 3: spell_id = 59843; break; + } + m_caster->CastSpell(m_caster,spell_id,true,NULL); + return; + } } //All IconID Check in there @@ -1334,30 +1275,35 @@ void Spell::EffectDummy(uint32 i) return; // immediately finishes the cooldown on Frost spells - const PlayerSpellMap& sp_list = ((Player *)m_caster)->GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) + const SpellCooldowns& cm = ((Player *)m_caster)->GetSpellCooldownMap(); + for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();) { - if (itr->second->state == PLAYERSPELL_REMOVED) - continue; - - uint32 classspell = itr->first; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell); + SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); if( spellInfo->SpellFamilyName == SPELLFAMILY_MAGE && (GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_FROST) && spellInfo->Id != 11958 && GetSpellRecoveryTime(spellInfo) > 0 ) { - ((Player*)m_caster)->RemoveSpellCooldown(classspell, true); + ((Player*)m_caster)->RemoveSpellCooldown((itr++)->first, true); } + else + ++itr; } return; } - case 32826: + case 32826: // Polymorph Cast Visual { if ( unitTarget && unitTarget->GetTypeId() == TYPEID_UNIT ) { //Polymorph Cast Visual Rank 1 - const uint32 spell_list[6] = {32813, 32816, 32817, 32818, 32819, 32820}; + const uint32 spell_list[6] = { + 32813, // Squirrel Form + 32816, // Giraffe Form + 32817, // Serpent Form + 32818, // Dragonhawk Form + 32819, // Worgen Form + 32820 // Sheep Form + }; unitTarget->CastSpell( unitTarget, spell_list[urand(0, 5)], true); } return; @@ -1387,7 +1333,7 @@ void Spell::EffectDummy(uint32 i) uint32 rage=0; // Glyph of Execution bonus - if (AuraEffect *aura = m_caster->GetDummyAura(58367)) + if (AuraEffect *aura = m_caster->GetAuraEffect(58367, 0)) rage+=aura->GetAmount(); spell_id = 20647; @@ -1407,7 +1353,7 @@ void Spell::EffectDummy(uint32 i) } bp = damage+int32(rage * m_spellInfo->DmgMultiplier[i] + - m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2f); + m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2f); break; } // Concussion Blow @@ -1484,7 +1430,10 @@ void Spell::EffectDummy(uint32 i) for(Unit::AuraEffectList::const_iterator itr = mod.begin(); itr != mod.end(); ++itr) { if((*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (*itr)->GetSpellProto()->SpellIconID == 1982) - manaFeedVal+= (*itr)->GetAmount(); + { + manaFeedVal = (*itr)->GetAmount(); + break; + } } if(manaFeedVal > 0) { @@ -1517,9 +1466,9 @@ void Spell::EffectDummy(uint32 i) return; } if (m_caster->IsFriendlyTo(unitTarget)) - m_caster->CastSpell(unitTarget, heal, true, 0); + m_caster->CastSpell(unitTarget, heal, false, 0); else - m_caster->CastSpell(unitTarget, hurt, true, 0); + m_caster->CastSpell(unitTarget, hurt, false, 0); return; } break; @@ -1587,20 +1536,21 @@ void Spell::EffectDummy(uint32 i) m_caster->CastSpell(unitTarget, 5940, true); return; } - case 14185: // Preparation Rogue + case 14185: // Preparation { if(m_caster->GetTypeId()!=TYPEID_PLAYER) return; //immediately finishes the cooldown on certain Rogue abilities - const PlayerSpellMap& sp_list = ((Player *)m_caster)->GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) + const SpellCooldowns& cm = ((Player *)m_caster)->GetSpellCooldownMap(); + for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();) { - uint32 classspell = itr->first; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell); + SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags[1] & 0x00000240 || spellInfo->SpellFamilyFlags[0] & 0x00000860)) - ((Player*)m_caster)->RemoveSpellCooldown(classspell,true); + ((Player*)m_caster)->RemoveSpellCooldown((itr++)->first,true); + else + ++itr; } return; } @@ -1614,20 +1564,21 @@ void Spell::EffectDummy(uint32 i) case SPELLFAMILY_HUNTER: switch(m_spellInfo->Id) { - case 23989: //Readiness talent + case 23989: // Readiness talent { if(m_caster->GetTypeId()!=TYPEID_PLAYER) return; - //immediately finishes the cooldown for hunter abilities - const PlayerSpellMap& sp_list = ((Player *)m_caster)->GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) + // immediately finishes the cooldown on your other Hunter abilities except Bestial Wrath + const SpellCooldowns& cm = ((Player*)m_caster)->GetSpellCooldownMap(); + for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();) { - uint32 classspell = itr->first; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell); + SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && spellInfo->Id != 23989 && GetSpellRecoveryTime(spellInfo) > 0 ) - ((Player*)m_caster)->RemoveSpellCooldown(classspell,true); + ((Player*)m_caster)->RemoveSpellCooldown((itr++)->first,true); + else + ++itr; } return; } @@ -1645,15 +1596,15 @@ void Spell::EffectDummy(uint32 i) } break; case SPELLFAMILY_PALADIN: + // Divine Storm + if (m_spellInfo->SpellFamilyFlags[1] & 0x20000 && i == 1) + { + int32 dmg= m_damage * damage /100; + m_caster->CastCustomSpell(unitTarget, 54172, &dmg , 0, 0, true); + return; + } switch(m_spellInfo->SpellIconID) { - // Divine Storm - if (m_spellInfo->SpellFamilyFlags[1] & 0x20000) - { - int32 damage=m_currentBasePoints[0] * damage /100; - m_caster->CastCustomSpell(unitTarget, 54172, &damage , 0, 0, true); - return; - } case 156: // Holy Shock { if(!unitTarget) @@ -1771,8 +1722,8 @@ void Spell::EffectDummy(uint32 i) } break; case SPELLFAMILY_SHAMAN: - //Shaman Rockbiter Weapon - if (m_spellInfo->SpellFamilyFlags.IsEqual(0x400000)) + // Rockbiter Weapon + if (m_spellInfo->SpellFamilyFlags[0] & 0x400000) { // TODO: use expect spell for enchant (if exist talent) // In 3.0.3 no mods present for rockbiter @@ -1820,10 +1771,12 @@ void Spell::EffectDummy(uint32 i) } return; } - // Cleansing Totem + // Cleansing Totem Pulse if(m_spellInfo->SpellFamilyFlags[0] & 0x04000000 && m_spellInfo->SpellIconID==1673) { - m_caster->CastSpell(unitTarget, 52025, true, 0, 0, m_originalCasterGUID); + int32 bp1 = 1; + // Cleansing Totem Effect + m_caster->CastCustomSpell(unitTarget, 52025, NULL, &bp1, NULL, true, NULL, NULL, m_originalCasterGUID); return; } // Healing Stream Totem @@ -1845,9 +1798,8 @@ void Spell::EffectDummy(uint32 i) if(!unitTarget || unitTarget->getPowerType() != POWER_MANA) return; // Glyph of Mana Tide - Unit *owner = m_caster->GetOwner(); - if (owner) - if (AuraEffect *dummy = owner->GetDummyAura(55441)) + if(Unit *owner = m_caster->GetOwner()) + if (AuraEffect *dummy = owner->GetAuraEffect(55441, 0)) damage+=dummy->GetAmount(); // Regenerate 6% of Total Mana Every 3 secs int32 EffectBasePoints0 = unitTarget->GetMaxPower(POWER_MANA) * damage / 100; @@ -1862,6 +1814,7 @@ void Spell::EffectDummy(uint32 i) Item *item = ((Player*)m_caster)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); if (item) { + // Damage is increased by 25% if your off-hand weapon is enchanted with Flametongue. if (m_caster->GetAura(SPELL_AURA_DUMMY, SPELLFAMILY_SHAMAN, 0x200000)) { m_damage += m_damage * damage / 100; @@ -1871,12 +1824,27 @@ void Spell::EffectDummy(uint32 i) } break; case SPELLFAMILY_DEATHKNIGHT: - // Death strike dummy aura apply - // Used to proc healing later + // Death strike if (m_spellInfo->SpellFamilyFlags[0] & 0x00000010) { - spell_id=45469; - m_caster->CastSpell(m_caster,spell_id,true); + uint32 count = 0; + Unit::AuraMap const& auras = unitTarget->GetAuras(); + for(Unit::AuraMap::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr) + { + if(itr->second->GetSpellProto()->Dispel == DISPEL_DISEASE && + itr->second->GetCasterGUID() == m_caster->GetGUID()) + { + ++count; + // max. 15% + if(count == 3) + break; + } + } + int32 bp = count * m_caster->GetMaxHealth() * m_spellInfo->DmgMultiplier[0] / 100; + // Improved Death Strike + if (AuraEffect const * aurEff = m_caster->GetAuraEffect(SPELL_AURA_ADD_PCT_MODIFIER, SPELLFAMILY_DEATHKNIGHT, 2751, 0)) + bp = bp * (m_caster->CalculateSpellDamage(aurEff->GetSpellProto(), 2, aurEff->GetSpellProto()->EffectBasePoints[2], m_caster) + 100.0f) / 100.0f; + m_caster->CastCustomSpell(m_caster, 45470, &bp, NULL, NULL, true); return; } // Scourge Strike @@ -1890,9 +1858,6 @@ void Spell::EffectDummy(uint32 i) { if(m_caster->IsFriendlyTo(unitTarget)) { - if(unitTarget->GetCreatureType() != CREATURE_TYPE_UNDEAD) - return; - int32 bp = damage * 1.5f; m_caster->CastCustomSpell(unitTarget, 47633, &bp, NULL, NULL, true); } @@ -1912,7 +1877,10 @@ void Spell::EffectDummy(uint32 i) // Death Grip if(m_spellInfo->Id == 49560) { - unitTarget->CastSpell(m_caster, damage, true); + if (unitTarget->m_Vehicle) + unitTarget->m_Vehicle->CastSpell(m_caster, damage, true); + else + unitTarget->CastSpell(m_caster, damage, true); return; } else if(m_spellInfo->Id == 46584) // Raise dead @@ -1950,6 +1918,48 @@ void Spell::EffectDummy(uint32 i) spell_id = m_currentBasePoints[0]; } + // Corpse Explosion + else if(m_spellInfo->SpellIconID == 1737) + { + // Dummy effect 1 is used only for targeting and damage amount + if (i!=0) + return; + int32 bp = 0; + // Living ghoul as a target + if (unitTarget->isAlive()) + { + bp = unitTarget->GetMaxHealth()*0.25f; + } + // Some corpse + else + { + bp = damage; + } + m_caster->CastCustomSpell(unitTarget,m_spellInfo->CalculateSimpleValue(1),&bp,NULL,NULL,true); + // Corpse Explosion (Suicide) + unitTarget->CastCustomSpell(unitTarget,43999,&bp,NULL,NULL,true); + // Set corpse look + unitTarget->SetDisplayId(25537+urand(0,3)); + } + // Runic Power Feed ( keeping Gargoyle alive ) + else if (m_spellInfo->Id == 50524) + { + // No power, dismiss Gargoyle + if (m_caster->GetPower(POWER_RUNIC_POWER)<30) + m_caster->CastSpell((Unit*)NULL,50515,true); + else + m_caster->ModifyPower(POWER_RUNIC_POWER,-30); + + return; + } + // Dismiss Gargoyle + else if (m_spellInfo->Id == 50515) + { + // FIXME: gargoyle should fly away + unitTarget->setDeathState(JUST_DIED); + m_caster->RemoveAurasDueToSpell(50514); + return; + } break; } @@ -1971,7 +1981,7 @@ void Spell::EffectDummy(uint32 i) } // pet auras - if(PetAura const* petSpell = spellmgr.GetPetAura(m_spellInfo->Id)) + if(PetAura const* petSpell = spellmgr.GetPetAura(m_spellInfo->Id,i)) { m_caster->AddPetAura(petSpell); return; @@ -2043,7 +2053,10 @@ void Spell::EffectForceCast(uint32 i) return; } - unitTarget->CastSpell(unitTarget, spellInfo, true, NULL, NULL, m_originalCasterGUID); + if (damage) + unitTarget->CastCustomSpell(unitTarget, spellInfo->Id, &damage, NULL, NULL, true, NULL, NULL, m_originalCasterGUID); + else + unitTarget->CastSpell(unitTarget, spellInfo, true, NULL, NULL, m_originalCasterGUID); } void Spell::EffectTriggerSpell(uint32 i) @@ -2053,7 +2066,16 @@ void Spell::EffectTriggerSpell(uint32 i) // special cases switch(triggered_spell_id) { - // Vanish + // Mirror Image + case 58832: + { + // Glyph of Mirror Image + if (m_caster->HasAura(63093)) + m_caster->CastSpell(m_caster, 65047, true); // Mirror Image + + break; + } + // Vanish (not exist) case 18461: { m_caster->RemoveMovementImpairingAuras(); @@ -2114,7 +2136,8 @@ void Spell::EffectTriggerSpell(uint32 i) // Brittle Armor - (need add max stack of 24575 Brittle Armor) case 29284: { - const SpellEntry *spell = sSpellStore.LookupEntry(24575); + // Brittle Armor + SpellEntry const* spell = sSpellStore.LookupEntry(24575); if (!spell) return; @@ -2125,7 +2148,8 @@ void Spell::EffectTriggerSpell(uint32 i) // Mercurial Shield - (need add max stack of 26464 Mercurial Shield) case 29286: { - const SpellEntry *spell = sSpellStore.LookupEntry(26464); + // Mercurial Shield + SpellEntry const* spell = sSpellStore.LookupEntry(26464); if (!spell) return; @@ -2140,7 +2164,7 @@ void Spell::EffectTriggerSpell(uint32 i) return; } // Cloak of Shadows - case 35729 : + case 35729: { uint32 dispelMask = GetDispellMask(DISPEL_ALL); Unit::AuraMap& Auras = m_caster->GetAuras(); @@ -2436,7 +2460,7 @@ void Spell::EffectUnlearnSpecialization( uint32 i ) _player->removeSpell(spellToUnlearn); - sLog.outDebug( "Spell: Player %u have unlearned spell %u from NpcGUID: %u", _player->GetGUIDLow(), spellToUnlearn, m_caster->GetGUIDLow() ); + sLog.outDebug( "Spell: Player %u has unlearned spell %u from NpcGUID: %u", _player->GetGUIDLow(), spellToUnlearn, m_caster->GetGUIDLow() ); } void Spell::EffectPowerDrain(uint32 i) @@ -2495,7 +2519,7 @@ void Spell::EffectSendEvent(uint32 EffectIndex) we do not handle a flag dropping or clicking on flag in battleground by sendevent system */ sLog.outDebug("Spell ScriptStart %u for spellid %u in EffectSendEvent ", m_spellInfo->EffectMiscValue[EffectIndex], m_spellInfo->Id); - sWorld.ScriptsStart(sEventScripts, m_spellInfo->EffectMiscValue[EffectIndex], m_caster, focusObject); + m_caster->GetMap()->ScriptsStart(sEventScripts, m_spellInfo->EffectMiscValue[EffectIndex], m_caster, focusObject); } void Spell::EffectPowerBurn(uint32 i) @@ -2569,12 +2593,11 @@ void Spell::SpellDamageHeal(uint32 /*i*/) { // Amount of heal - depends from stacked Holy Energy int damageAmount = 0; - Unit::AuraEffectList const& mDummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraEffectList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i) - if((*i)->GetId() == 45062) - damageAmount+=(*i)->GetAmount(); - if (damageAmount) + if (AuraEffect const * aurEff = m_caster->GetAuraEffect(45062, 0)) + { + damageAmount+= aurEff->GetAmount(); m_caster->RemoveAurasDueToSpell(45062); + } addhealth += damageAmount; } @@ -2587,7 +2610,7 @@ void Spell::SpellDamageHeal(uint32 /*i*/) for(Unit::AuraEffectList::const_iterator i = RejorRegr.begin(); i != RejorRegr.end(); ++i) { if((*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID - && ((*i)->GetSpellProto()->SpellFamilyFlags.IsEqual(0x40) || (*i)->GetSpellProto()->SpellFamilyFlags.IsEqual(0x10)) ) + && (*i)->GetSpellProto()->SpellFamilyFlags[0] & 0x50) { if(!targetAura || (*i)->GetParentAura()->GetAuraDuration() < targetAura->GetParentAura()->GetAuraDuration()) targetAura = *i; @@ -2607,18 +2630,17 @@ void Spell::SpellDamageHeal(uint32 /*i*/) //It is said that talent bonus should not be included int32 tickcount = 0; - if(targetAura->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID) - { - switch(targetAura->GetSpellProto()->SpellFamilyFlags[0]) - { - case 0x10: tickcount = 4; break; // Rejuvenation - case 0x40: tickcount = 6; break; // Regrowth - } - } + // Rejuvenation + if (targetAura->GetSpellProto()->SpellFamilyFlags[0] & 0x10) + tickcount = 4; + // Regrowth + else // if (targetAura->GetSpellProto()->SpellFamilyFlags[0] & 0x40) + tickcount = 6; + addhealth += tickheal * tickcount; // Glyph of Swiftmend - if(!caster->GetDummyAura(54824)) + if(!caster->HasAura(54824)) unitTarget->RemoveAura(targetAura->GetId(), targetAura->GetCasterGUID()); //addhealth += tickheal * tickcount; @@ -2635,6 +2657,11 @@ void Spell::SpellDamageHeal(uint32 /*i*/) unitTarget->RemoveAura(aurEff->GetParentAura()); } } + // Death Pact - return pct of max health to caster + else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && m_spellInfo->SpellFamilyFlags[0] & 0x00080000) + { + addhealth = int32(caster->GetMaxHealth()*damage/100.0f); + } else addhealth = caster->SpellHealingBonus(unitTarget, m_spellInfo, addhealth, HEAL); @@ -2827,27 +2854,34 @@ void Spell::EffectCreateItem2(uint32 i) Player* player = (Player*)m_caster; uint32 item_id = m_spellInfo->EffectItemType[i]; - if(item_id) - DoCreateItem(i, item_id); + + DoCreateItem(i, item_id); // special case: fake item replaced by generate using spell_loot_template if(IsLootCraftingSpell(m_spellInfo)) { - if(item_id) - { - if(!player->HasItemCount(item_id, 1)) - return; + if(!player->HasItemCount(item_id, 1)) + return; - // remove reagent - uint32 count = 1; - player->DestroyItemCount(item_id, count, true); - } + // remove reagent + uint32 count = 1; + player->DestroyItemCount(item_id, count, true); // create some random items player->AutoStoreLoot(m_spellInfo->Id, LootTemplates_Spell); } } +void Spell::EffectCreateRandomItem(uint32 i) +{ + if(m_caster->GetTypeId()!=TYPEID_PLAYER) + return; + Player* player = (Player*)m_caster; + + // create some random items + player->AutoStoreLoot(m_spellInfo->Id, LootTemplates_Spell); +} + void Spell::EffectPersistentAA(uint32 i) { float radius = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); @@ -2880,31 +2914,31 @@ void Spell::EffectEnergize(uint32 i) Powers power = Powers(m_spellInfo->EffectMiscValue[i]); // Some level depends spells - int multiplier = 0; + int level_multiplier = 0; int level_diff = 0; switch (m_spellInfo->Id) { - // Restore Energy - case 9512: + case 9512: // Restore Energy level_diff = m_caster->getLevel() - 40; - multiplier = 2; + level_multiplier = 2; break; - // Blood Fury - case 24571: + case 24571: // Blood Fury level_diff = m_caster->getLevel() - 60; - multiplier = 10; + level_multiplier = 10; break; - // Burst of Energy - case 24532: + case 24532: // Burst of Energy level_diff = m_caster->getLevel() - 60; - multiplier = 4; + level_multiplier = 4; break; + case 31930: // Judgements of the Wise + case 63375: // Improved Stormstrike + damage = damage * unitTarget->GetCreateMana() / 100; default: break; } if (level_diff > 0) - damage -= multiplier * level_diff; + damage -= level_multiplier * level_diff; if(damage < 0) return; @@ -2912,6 +2946,12 @@ void Spell::EffectEnergize(uint32 i) if(unitTarget->GetMaxPower(power) == 0) return; + // Spells which use pct of max mana, but have wrong effect + if (m_spellInfo->Id == 48542) + { + damage = damage * unitTarget->GetMaxPower(power) / 100; + } + m_caster->EnergizeBySpell(unitTarget, m_spellInfo->Id, damage, power); // Mad Alchemist's Potion @@ -2993,7 +3033,7 @@ void Spell::SendLoot(uint64 guid, LootType loottype) case GAMEOBJECT_TYPE_DOOR: case GAMEOBJECT_TYPE_BUTTON: gameObjTarget->UseDoorOrButton(); - sWorld.ScriptsStart(sGameObjectScripts, gameObjTarget->GetDBTableGUIDLow(), player, gameObjTarget); + player->GetMap()->ScriptsStart(sGameObjectScripts, gameObjTarget->GetDBTableGUIDLow(), player, gameObjTarget); return; case GAMEOBJECT_TYPE_QUESTGIVER: @@ -3013,7 +3053,7 @@ void Spell::SendLoot(uint64 guid, LootType loottype) if (gameObjTarget->GetGOInfo()->goober.eventId) { sLog.outDebug("Goober ScriptStart id %u for GO %u", gameObjTarget->GetGOInfo()->goober.eventId,gameObjTarget->GetDBTableGUIDLow()); - sWorld.ScriptsStart(sEventScripts, gameObjTarget->GetGOInfo()->goober.eventId, player, gameObjTarget); + player->GetMap()->ScriptsStart(sEventScripts, gameObjTarget->GetGOInfo()->goober.eventId, player, gameObjTarget); gameObjTarget->EventInform(gameObjTarget->GetGOInfo()->goober.eventId); } @@ -3024,7 +3064,7 @@ void Spell::SendLoot(uint64 guid, LootType loottype) return; Script->GOHello(player, gameObjTarget); - sWorld.ScriptsStart(sGameObjectScripts, gameObjTarget->GetDBTableGUIDLow(), player, gameObjTarget); + gameObjTarget->GetMap()->ScriptsStart(sGameObjectScripts, gameObjTarget->GetDBTableGUIDLow(), player, gameObjTarget); gameObjTarget->AddUniqueUse(player); gameObjTarget->SetLootState(GO_JUST_DEACTIVATED); @@ -3046,7 +3086,7 @@ void Spell::SendLoot(uint64 guid, LootType loottype) if (gameObjTarget->GetGOInfo()->chest.eventId) { sLog.outDebug("Chest ScriptStart id %u for GO %u", gameObjTarget->GetGOInfo()->chest.eventId,gameObjTarget->GetDBTableGUIDLow()); - sWorld.ScriptsStart(sEventScripts, gameObjTarget->GetGOInfo()->chest.eventId, player, gameObjTarget); + player->GetMap()->ScriptsStart(sEventScripts, gameObjTarget->GetGOInfo()->chest.eventId, player, gameObjTarget); } // triggering linked GO @@ -3109,7 +3149,7 @@ void Spell::EffectOpenLock(uint32 effIndex) // these objects must have been spawned by outdoorpvp! else if(gameObjTarget->GetGOInfo()->type == GAMEOBJECT_TYPE_GOOBER && sOutdoorPvPMgr.HandleOpenGo(player, gameObjTarget->GetGUID())) return; - lockId = gameObjTarget->GetLockId(); + lockId = goInfo->GetLockId(); guid = gameObjTarget->GetGUID(); } else if(itemTarget) @@ -3255,11 +3295,6 @@ void Spell::EffectSummonChangeItem(uint32 i) delete pNewItem; } -void Spell::EffectOpenSecretSafe(uint32 i) -{ - EffectOpenLock(i); //no difference for now -} - void Spell::EffectProficiency(uint32 /*i*/) { if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) @@ -3327,6 +3362,11 @@ void Spell::EffectSummonType(uint32 i) switch(properties->Category) { default: + if (properties->Flags & 512) + { + SummonGuardian(entry, properties); + break; + } switch(properties->Type) { case SUMMON_TYPE_PET: @@ -3409,7 +3449,12 @@ void Spell::EffectSummonType(uint32 i) TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN; - m_originalCaster->SummonCreature(entry,px,py,pz,m_caster->GetOrientation(),summonType,duration); + TempSummon * summon = m_originalCaster->SummonCreature(entry,px,py,pz,m_caster->GetOrientation(),summonType,duration); + if (!summon) + continue; + summon->SetUInt64Value(UNIT_FIELD_SUMMONEDBY, m_originalCaster->GetGUID()); + if (properties->Category == SUMMON_CATEGORY_ALLY) + summon->setFaction(m_originalCaster->getFaction()); } break; } @@ -3464,7 +3509,7 @@ void Spell::EffectLearnSpell(uint32 i) uint32 spellToLearn = ((m_spellInfo->Id==SPELL_ID_GENERIC_LEARN) || (m_spellInfo->Id==SPELL_ID_GENERIC_LEARN_PET)) ? damage : m_spellInfo->EffectTriggerSpell[i]; player->learnSpell(spellToLearn,false); - sLog.outDebug( "Spell: Player %u have learned spell %u from NpcGUID=%u", player->GetGUIDLow(), spellToLearn, m_caster->GetGUIDLow() ); + sLog.outDebug( "Spell: Player %u has learned spell %u from NpcGUID=%u", player->GetGUIDLow(), spellToLearn, m_caster->GetGUIDLow() ); } void Spell::EffectDispel(uint32 i) @@ -3554,26 +3599,12 @@ void Spell::EffectDispel(uint32 i) } m_caster->SendMessageToSet(&dataSuccess, true); - // On succes dispel + // On success dispel // Devour Magic if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->Category == SPELLCATEGORY_DEVOUR_MAGIC) { - uint32 heal_spell = 0; - switch (m_spellInfo->Id) - { - case 19505: heal_spell = 19658; break; - case 19731: heal_spell = 19732; break; - case 19734: heal_spell = 19733; break; - case 19736: heal_spell = 19735; break; - case 27276: heal_spell = 27278; break; - case 27277: heal_spell = 27279; break; - case 48011: heal_spell = 48010; break; - default: - sLog.outDebug("Spell for Devour Magic %d not handled in Spell::EffectDispel", m_spellInfo->Id); - break; - } - if (heal_spell) - m_caster->CastSpell(m_caster, heal_spell, true); + int32 heal_amount = m_spellInfo->CalculateSimpleValue(1); + m_caster->CastCustomSpell(m_caster, 19658, &heal_amount, NULL, NULL, true); } } } @@ -3992,7 +4023,7 @@ void Spell::EffectTameCreature(uint32 /*i*/) if(!unitTarget) return; - if(unitTarget->GetTypeId() == TYPEID_PLAYER) + if(unitTarget->GetTypeId() != TYPEID_UNIT) return; Creature* creatureTarget = (Creature*)unitTarget; @@ -4068,7 +4099,6 @@ void Spell::EffectSummonPet(uint32 i) return; OldSummon->GetMap()->Remove((Creature*)OldSummon,false); - OldSummon->SetMapId(owner->GetMapId()); float px, py, pz; owner->GetClosePoint(px, py, pz, OldSummon->GetObjectSize()); @@ -4211,11 +4241,11 @@ void Spell::SpellDamageWeaponDmg(uint32 i) SpellEntry const *spellInfo = NULL; uint32 stack = 0; - if (AuraEffect * aur = unitTarget->GetAura(SPELL_AURA_MOD_RESISTANCE,SPELLFAMILY_WARRIOR,SPELLFAMILYFLAG_WARRIOR_SUNDERARMOR, 0, 0, m_caster->GetGUID())) + if (Aura * aur = unitTarget->GetAura(58567, m_caster->GetGUID())) { - aur->GetParentAura()->RefreshAura(); + aur->RefreshAura(); spellInfo = aur->GetSpellProto(); - stack = aur->GetParentAura()->GetStackAmount(); + stack = aur->GetStackAmount(); } for(uint8 j = 0; j < 3; j++) @@ -4254,7 +4284,7 @@ void Spell::SpellDamageWeaponDmg(uint32 i) break; int32 count = 1; // Glyph of Devastate - if (AuraEffect * aurEff = m_caster->GetDummyAura(58388)) + if (AuraEffect * aurEff = m_caster->GetAuraEffect(58388, 0)) count += aurEff->GetAmount(); for (;count>0;count--) m_caster->CastSpell(unitTarget, spellInfo, true); @@ -4269,7 +4299,7 @@ void Spell::SpellDamageWeaponDmg(uint32 i) if(m_spellInfo->SpellFamilyFlags[0] & 0x2000000) { if(m_caster->GetTypeId()==TYPEID_PLAYER) - ((Player*)m_caster)->AddComboPoints(unitTarget, 1); + ((Player*)m_caster)->AddComboPoints(unitTarget, 1, this); } // Fan of Knives else if(m_spellInfo->SpellFamilyFlags[1] & 0x40000) @@ -4326,19 +4356,8 @@ void Spell::SpellDamageWeaponDmg(uint32 i) { // Skyshatter Harness item set bonus // Stormstrike - if(m_spellInfo->SpellFamilyFlags[1] & 0x0010) - { - Unit::AuraEffectList const& m_OverrideClassScript = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(Unit::AuraEffectList::const_iterator citr = m_OverrideClassScript.begin(); citr != m_OverrideClassScript.end(); ++citr) - { - // Stormstrike AP Buff - if ( (*citr)->GetMiscValue() == 5634 ) - { - m_caster->CastSpell(m_caster, 38430, true, NULL, *citr); - break; - } - } - } + if (AuraEffect * aurEff = m_caster->IsScriptOverriden(m_spellInfo, 5634)) + m_caster->CastSpell(m_caster, 38430, true, NULL, aurEff); break; } case SPELLFAMILY_DRUID: @@ -4347,18 +4366,60 @@ void Spell::SpellDamageWeaponDmg(uint32 i) if(m_spellInfo->SpellFamilyFlags.IsEqual(0,0x00000400)) { if(m_caster->GetTypeId()==TYPEID_PLAYER) - ((Player*)m_caster)->AddComboPoints(unitTarget,1); + ((Player*)m_caster)->AddComboPoints(unitTarget,1, this); + } + // Shred, Maul - Rend and Tear + else if (m_spellInfo->SpellFamilyFlags[0] & 0x00008800 && unitTarget->HasAuraState(AURA_STATE_BLEEDING, m_spellInfo, m_caster)) + { + if (AuraEffect const* rendAndTear = m_caster->GetDummyAura(SPELLFAMILY_DRUID, 2859, 0)) + { + totalDamagePercentMod *= float((rendAndTear->GetAmount() + 100.0f) / 100.0f); + } } break; } case SPELLFAMILY_DEATHKNIGHT: { + // Plague Strike + if (m_spellInfo->SpellFamilyFlags[0] & 0x00000001) + { + // Glyph of Plague Strike + if (AuraEffect * aurEff = m_caster->GetAuraEffect(58657,0)) + totalDamagePercentMod *= float((aurEff->GetAmount() + 100.0f) / 100.0f); + } + // Blood Strike + else if (m_spellInfo->SpellFamilyFlags[0] & 0x400000) + { + totalDamagePercentMod *= (float(unitTarget->GetDiseasesByCaster(m_caster->GetGUID())) * 12.5f + 100.0f) / 100.0f; + + // Glyph of Blood Strike + if (AuraEffect * aurEff = m_caster->GetAuraEffect(59332,0)) + { + if(unitTarget->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED)) + totalDamagePercentMod *= float((20 + 100.0f) / 100.0f); + } + } + // Death Strike + else if (m_spellInfo->SpellFamilyFlags[0] & 0x00000010) + { + // Glyph of Death Strike + if (AuraEffect * aurEff = m_caster->GetAuraEffect(59336,0)) + { + if(uint32 runic = m_caster->GetPower(POWER_RUNIC_POWER)) + { + if (runic > 25) + runic = 25; + + totalDamagePercentMod *= float((runic + 100.0f) / 100.0f); + } + } + } // Obliterate (12.5% more damage per disease) - if (m_spellInfo->SpellFamilyFlags[1] & 0x20000) + else if (m_spellInfo->SpellFamilyFlags[1] & 0x20000) { bool consumeDiseases = true; // Annihilation - if (AuraEffect * aurEff = m_caster->GetDummyAura(SPELLFAMILY_DEATHKNIGHT, 2710)) + if (AuraEffect * aurEff = m_caster->GetDummyAura(SPELLFAMILY_DEATHKNIGHT, 2710, 0)) { // Do not consume diseases if roll sucesses if (roll_chance_i(aurEff->GetAmount())) @@ -4366,8 +4427,8 @@ void Spell::SpellDamageWeaponDmg(uint32 i) } totalDamagePercentMod *= (float(CalculateDamage(2, unitTarget) * unitTarget->GetDiseasesByCaster(m_caster->GetGUID(), consumeDiseases) / 2) + 100.0f) / 100.0f; } - // Blood-Caked Strike - Blood-Caked Blade // Blood Strike - else if (m_spellInfo->SpellIconID == 1736 || m_spellInfo->SpellFamilyFlags[0] & 0x400000) + // Blood-Caked Strike - Blood-Caked Blade + else if (m_spellInfo->SpellIconID == 1736) totalDamagePercentMod *= (float(unitTarget->GetDiseasesByCaster(m_caster->GetGUID())) * 12.5f + 100.0f) / 100.0f; break; } @@ -4536,44 +4597,45 @@ void Spell::EffectSummonObjectWild(uint32 i) } int32 duration = GetSpellDuration(m_spellInfo); + pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); pGameObj->SetSpellId(m_spellInfo->Id); - if(pGameObj->GetGoType() != GAMEOBJECT_TYPE_FLAGDROP) // make dropped flag clickable for other players (not set owner guid (created by) for this)... - m_caster->AddGameObject(pGameObj); + // Wild object not have owner and check clickable by players map->Add(pGameObj); - if(pGameObj->GetMapId() == 489 && pGameObj->GetGoType() == GAMEOBJECT_TYPE_FLAGDROP) //WS + if(pGameObj->GetGoType() == GAMEOBJECT_TYPE_FLAGDROP && m_caster->GetTypeId() == TYPEID_PLAYER) { - if(m_caster->GetTypeId() == TYPEID_PLAYER) + Player *pl = (Player*)m_caster; + BattleGround* bg = ((Player *)m_caster)->GetBattleGround(); + + switch(pGameObj->GetMapId()) { - Player *pl = (Player*)m_caster; - BattleGround* bg = ((Player *)m_caster)->GetBattleGround(); - if(bg && bg->GetTypeID()==BATTLEGROUND_WS && bg->GetStatus() == STATUS_IN_PROGRESS) + case 489: //WS { - uint32 team = ALLIANCE; + if(bg && bg->GetTypeID()==BATTLEGROUND_WS && bg->GetStatus() == STATUS_IN_PROGRESS) + { + uint32 team = ALLIANCE; - if(pl->GetTeam() == team) - team = HORDE; + if(pl->GetTeam() == team) + team = HORDE; - ((BattleGroundWS*)bg)->SetDroppedFlagGUID(pGameObj->GetGUID(),team); + ((BattleGroundWS*)bg)->SetDroppedFlagGUID(pGameObj->GetGUID(),team); + } + break; } - } - } - - if(pGameObj->GetMapId() == 566 && pGameObj->GetGoType() == GAMEOBJECT_TYPE_FLAGDROP) //EY - { - if(m_caster->GetTypeId() == TYPEID_PLAYER) - { - BattleGround* bg = ((Player *)m_caster)->GetBattleGround(); - if(bg && bg->GetTypeID()==BATTLEGROUND_EY && bg->GetStatus() == STATUS_IN_PROGRESS) + case 566: //EY { - ((BattleGroundEY*)bg)->SetDroppedFlagGUID(pGameObj->GetGUID()); + if(bg && bg->GetTypeID()==BATTLEGROUND_EY && bg->GetStatus() == STATUS_IN_PROGRESS) + { + ((BattleGroundEY*)bg)->SetDroppedFlagGUID(pGameObj->GetGUID()); + } + break; } } } - if(uint32 linkedEntry = pGameObj->GetLinkedGameObjectEntry()) + if(uint32 linkedEntry = pGameObj->GetGOInfo()->GetLinkedGameObjectEntry()) { GameObject* linkedGO = new GameObject; if(linkedGO->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), linkedEntry, map, @@ -4582,7 +4644,7 @@ void Spell::EffectSummonObjectWild(uint32 i) linkedGO->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); linkedGO->SetSpellId(m_spellInfo->Id); - m_caster->AddGameObject(linkedGO); + // Wild object not have owner and check clickable by players map->Add(linkedGO); } else @@ -4604,6 +4666,31 @@ void Spell::EffectScriptEffect(uint32 effIndex) { switch(m_spellInfo->Id) { + // Dispelling Analysis + case 37028: + { + if (unitTarget->HasAura(36904)) + unitTarget->RemoveAurasDueToSpell(36904); + + return; + } + case 45204: // Clone Me! + case 41055: // Copy Weapon + case 45206: // Copy Off-hand Weapon + unitTarget->CastSpell(m_caster, damage, false); + break; + case 45205: // Copy Offhand Weapon + case 41054: // Copy Weapon + m_caster->CastSpell(unitTarget, damage, false); + break; + // Despawn Horse + case 52267: + { + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) + return; + ((Creature*)unitTarget)->ForcedDespawn(); + return; + } // PX-238 Winter Wondervolt TRAP case 26275: { @@ -4695,7 +4782,7 @@ void Spell::EffectScriptEffect(uint32 effIndex) return; // Onyxia Scale Cloak - if(unitTarget->GetDummyAura(22683)) + if(unitTarget->HasAura(22683)) return; // Shadow Flame @@ -4964,6 +5051,11 @@ void Spell::EffectScriptEffect(uint32 effIndex) } break; } + case 52173: // Coyote Spirit Despawn + case 60243: // Blood Parrot Despawn + if (unitTarget->GetTypeId() == TYPEID_UNIT && ((Creature*)unitTarget)->isSummon()) + ((TempSummon*)unitTarget)->UnSummon(); + return; // Sky Darkener Assault case 52124: if(unitTarget) @@ -5039,6 +5131,24 @@ void Spell::EffectScriptEffect(uint32 effIndex) } } return; + case 58983: // Big Blizzard Bear + { + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + // Prevent stacking of mounts + unitTarget->RemoveAurasByType(SPELL_AURA_MOUNTED); + + // Triggered spell id dependent of riding skill + if(uint16 skillval = ((Player*)unitTarget)->GetSkillValue(SKILL_RIDING)) + { + if (skillval >= 150) + unitTarget->CastSpell(unitTarget, 58999, true); + else + unitTarget->CastSpell(unitTarget, 58997, true); + } + return; + } case 59317: // Teleporting if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) return; @@ -5056,6 +5166,7 @@ void Spell::EffectScriptEffect(uint32 effIndex) case 61177: // Northrend Inscription Research case 61288: // Minor Inscription Research case 61756: // Northrend Inscription Research (FAST QA VERSION) + case 64323: // Book of Glyph Mastery { if(m_caster->GetTypeId() != TYPEID_PLAYER) return; @@ -5176,6 +5287,14 @@ void Spell::EffectScriptEffect(uint32 effIndex) } return; } + // Guarded by The Light + case 63521: + { + // Divine Plea + if(Aura *AuraDivinePlea = m_caster->GetAura(54428)) + AuraDivinePlea->RefreshAura(); + return; + } } break; } @@ -5193,16 +5312,6 @@ void Spell::EffectScriptEffect(uint32 effIndex) aur->GetParentAura()->RefreshAura(); return; } - // Divine Hymn - case 47951: - { - if (!unitTarget) - return; - Unit * target=NULL; - unitTarget->CastSpell(target, 59600, false); - unitTarget->CastSpell(target, 47953, false); - return; - } default: break; } @@ -5215,7 +5324,7 @@ void Spell::EffectScriptEffect(uint32 effIndex) // Invigoration case 53412: { - if (AuraEffect * aurEff = unitTarget->GetDummyAura(SPELLFAMILY_HUNTER, 3487)) + if (AuraEffect * aurEff = unitTarget->GetDummyAura(SPELLFAMILY_HUNTER, 3487, 0)) { if (roll_chance_i(aurEff->GetAmount())) unitTarget->CastSpell(unitTarget, 53398, true); @@ -5246,6 +5355,8 @@ void Spell::EffectScriptEffect(uint32 effIndex) flag96 familyFlag = aura->GetSpellProto()->SpellFamilyFlags; if (!(familyFlag[1] & 0x00000080 || familyFlag[0] & 0x0000C000)) continue; + if (!aura->GetPartAura(0)) + continue; // Serpent Sting - Instantly deals 40% of the damage done by your Serpent Sting. if (familyFlag[0] & 0x4000) @@ -5290,8 +5401,8 @@ void Spell::EffectScriptEffect(uint32 effIndex) } case SPELLFAMILY_PALADIN: { - // Judgement - if (m_spellInfo->SpellFamilyFlags[0] & 0x800000 || m_spellInfo->SpellFamilyFlags[2] & 0x8) + // Judgement (seal trigger) + if (m_spellInfo->Category == SPELLCATEGORY_JUDGEMENT) { if(!unitTarget || !unitTarget->isAlive()) return; @@ -5301,12 +5412,12 @@ void Spell::EffectScriptEffect(uint32 effIndex) // Judgement self add switch switch (m_spellInfo->Id) { - case 41467: break; // Judgement case 53407: spellId1 = 20184; break; // Judgement of Justice case 20271: // Judgement of Light case 57774: spellId1 = 20185; break; // Judgement of Light case 53408: spellId1 = 20186; break; // Judgement of Wisdom default: + sLog.outError("Unsupported Judgement (seal trigger) spell (Id: %u) in Spell::EffectScriptEffect",m_spellInfo->Id); return; } // all seals have aura dummy in 2 effect @@ -5349,6 +5460,43 @@ void Spell::EffectScriptEffect(uint32 effIndex) { switch(m_spellInfo->Id) { + // Death Knight Initiate Visual + case 51519: + { + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) + return; + + uint32 iTmpSpellId = 0; + switch (unitTarget->GetDisplayId()) + { + case 25369: iTmpSpellId = 51552; break; // bloodelf female + case 25373: iTmpSpellId = 51551; break; // bloodelf male + case 25363: iTmpSpellId = 51542; break; // draenei female + case 25357: iTmpSpellId = 51541; break; // draenei male + case 25361: iTmpSpellId = 51537; break; // dwarf female + case 25356: iTmpSpellId = 51538; break; // dwarf male + case 25372: iTmpSpellId = 51550; break; // forsaken female + case 25367: iTmpSpellId = 51549; break; // forsaken male + case 25362: iTmpSpellId = 51540; break; // gnome female + case 25359: iTmpSpellId = 51539; break; // gnome male + case 25355: iTmpSpellId = 51534; break; // human female + case 25354: iTmpSpellId = 51520; break; // human male + case 25360: iTmpSpellId = 51536; break; // nightelf female + case 25358: iTmpSpellId = 51535; break; // nightelf male + case 25368: iTmpSpellId = 51544; break; // orc female + case 25364: iTmpSpellId = 51543; break; // orc male + case 25371: iTmpSpellId = 51548; break; // tauren female + case 25366: iTmpSpellId = 51547; break; // tauren male + case 25370: iTmpSpellId = 51545; break; // troll female + case 25365: iTmpSpellId = 51546; break; // troll male + default: return; + } + + unitTarget->CastSpell(unitTarget, iTmpSpellId, true); + Creature* npc = (Creature*)unitTarget; + npc->LoadEquipment(npc->GetEquipmentId()); + return; + } // Dreaming Glory case 28698: { @@ -5413,11 +5561,24 @@ void Spell::EffectScriptEffect(uint32 effIndex) } break; } + case SPELLFAMILY_WARRIOR: + { + // Shattering Throw + if ( m_spellInfo->SpellFamilyFlags[1] & 0x1 ) + { + if(!unitTarget) + return; + // remove shields, will still display immune to damage part + unitTarget->RemoveAurasWithMechanic(1<<MECHANIC_IMMUNE_SHIELD); + return; + } + break; + } } // normal DB scripted effect sLog.outDebug("Spell ScriptStart spellid %u in EffectScriptEffect ", m_spellInfo->Id); - sWorld.ScriptsStart(sSpellScripts, m_spellInfo->Id, m_caster, unitTarget); + m_caster->GetMap()->ScriptsStart(sSpellScripts, m_spellInfo->Id, m_caster, unitTarget); } void Spell::EffectSanctuary(uint32 /*i*/) @@ -5464,7 +5625,7 @@ void Spell::EffectAddComboPoints(uint32 /*i*/) if(damage <= 0) return; - ((Player*)m_caster)->AddComboPoints(unitTarget, damage); + ((Player*)m_caster)->AddComboPoints(unitTarget, damage, this); } void Spell::EffectDuel(uint32 i) @@ -5571,8 +5732,10 @@ void Spell::EffectStuck(uint32 /*i*/) if(pTarget->isInFlight()) return; + PlayerInfo const *info = objmgr.GetPlayerInfo(pTarget->getRace(), pTarget->getClass()); + pTarget->TeleportTo(info->mapId, info->positionX, info->positionY, info->positionZ, pTarget->GetOrientation(), (unitTarget==m_caster ? TELE_TO_SPELL : 0)); // homebind location is loaded always - pTarget->TeleportTo(pTarget->m_homebindMapId,pTarget->m_homebindX,pTarget->m_homebindY,pTarget->m_homebindZ,pTarget->GetOrientation(), (unitTarget==m_caster ? TELE_TO_SPELL : 0)); + // pTarget->TeleportTo(pTarget->m_homebindMapId,pTarget->m_homebindX,pTarget->m_homebindY,pTarget->m_homebindZ,pTarget->GetOrientation(), (unitTarget==m_caster ? TELE_TO_SPELL : 0)); // Stuck spell trigger Hearthstone cooldown SpellEntry const *spellInfo = sSpellStore.LookupEntry(8690); @@ -5588,7 +5751,7 @@ void Spell::EffectSummonPlayer(uint32 /*i*/) return; // Evil Twin (ignore player summon, but hide this for summoner) - if(unitTarget->GetDummyAura(23445)) + if(unitTarget->HasAura(23445)) return; float x, y, z; @@ -5619,7 +5782,7 @@ void Spell::EffectActivateObject(uint32 effect_idx) int32 delay_secs = m_spellInfo->EffectMiscValue[effect_idx]; - sWorld.ScriptCommandStart(activateCommand, delay_secs, m_caster, gameObjTarget); + gameObjTarget->GetMap()->ScriptCommandStart(activateCommand, delay_secs, m_caster, gameObjTarget); } void Spell::EffectApplyGlyph(uint32 i) @@ -5802,7 +5965,7 @@ void Spell::EffectSummonObject(uint32 i) // Recast case - null spell id to make auras not be removed on object remove from world if (m_spellInfo->Id == obj->GetSpellId()) obj->SetSpellId(0); - obj->Delete(); + m_caster->RemoveGameObject(obj, true); } m_caster->m_ObjectSlot[slot] = 0; } @@ -5955,23 +6118,23 @@ void Spell::EffectMomentMove(uint32 i) dist = sqrt((x-destx)*(x-destx) + (y-desty)*(y-desty)); step = dist/10.0f; } - + int j = 0; for(j; j<10 ;j++) { - if(fabs(z - destz) > 6) - { - destx -= step * cos(orientation); - desty -= step * sin(orientation); - ground = unitTarget->GetMap()->GetHeight(destx,desty,MAX_HEIGHT,true); - floor = unitTarget->GetMap()->GetHeight(destx,desty,z, true); - destz = fabs(ground - z) <= fabs(floor - z) ? ground:floor; - }else - break; + if(fabs(z - destz) > 6) + { + destx -= step * cos(orientation); + desty -= step * sin(orientation); + ground = unitTarget->GetMap()->GetHeight(destx,desty,MAX_HEIGHT,true); + floor = unitTarget->GetMap()->GetHeight(destx,desty,z, true); + destz = fabs(ground - z) <= fabs(floor - z) ? ground:floor; + }else + break; } if(j == 9) { - return; + return; } unitTarget->NearTeleportTo(destx, desty, destz + 0.07531, orientation, unitTarget==m_caster); @@ -6087,6 +6250,28 @@ void Spell::EffectCharge(uint32 /*i*/) m_caster->Attack(target, true); } +void Spell::EffectCharge2(uint32 /*i*/) +{ + float x, y, z; + if(m_targets.HasDst()) + { + x = m_targets.m_destX; + y = m_targets.m_destY; + z = m_targets.m_destZ; + } + else if(Unit *target = m_targets.getUnitTarget()) + { + target->GetContactPoint(m_caster, x, y, z); + // not all charge effects used in negative spells + if(!IsPositiveSpell(m_spellInfo->Id) && m_caster->GetTypeId() == TYPEID_PLAYER) + m_caster->Attack(target, true); + } + else + return; + + m_caster->GetMotionMaster()->MoveCharge(x, y, z); +} + void Spell::EffectKnockBack(uint32 i) { if(!unitTarget) @@ -6330,7 +6515,7 @@ void Spell::EffectTransmitted(uint32 effIndex) if(goinfo->type==GAMEOBJECT_TYPE_FISHINGNODE) { - if ( !cMap->IsInWater(fx, fy, fz-0.5f)) // Hack to prevent fishing bobber from failing to land on fishing hole + if ( !cMap->IsInWater(fx, fy, fz-0.5f, 0.5f)) // Hack to prevent fishing bobber from failing to land on fishing hole { // but this is not proper, we really need to ignore not materialized objects SendCastResult(SPELL_FAILED_NOT_HERE); SendChannelUpdate(0); @@ -6390,16 +6575,14 @@ void Spell::EffectTransmitted(uint32 effIndex) case GAMEOBJECT_TYPE_FISHINGHOLE: case GAMEOBJECT_TYPE_CHEST: default: - { break; - } } pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); - pGameObj->SetOwnerGUID(m_caster->GetGUID() ); + pGameObj->SetOwnerGUID(m_caster->GetGUID()); - //pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() ); + //pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()); pGameObj->SetSpellId(m_spellInfo->Id); DEBUG_LOG("AddObject at SpellEfects.cpp EffectTransmitted"); @@ -6412,16 +6595,16 @@ void Spell::EffectTransmitted(uint32 effIndex) data << uint64(pGameObj->GetGUID()); m_caster->SendMessageToSet(&data,true); - if(uint32 linkedEntry = pGameObj->GetLinkedGameObjectEntry()) + if(uint32 linkedEntry = pGameObj->GetGOInfo()->GetLinkedGameObjectEntry()) { GameObject* linkedGO = new GameObject; if(linkedGO->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), linkedEntry, cMap, m_caster->GetPhaseMask(), fx, fy, fz, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY)) { linkedGO->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); - //linkedGO->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() ); + //linkedGO->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()); linkedGO->SetSpellId(m_spellInfo->Id); - linkedGO->SetOwnerGUID(m_caster->GetGUID() ); + linkedGO->SetOwnerGUID(m_caster->GetGUID()); linkedGO->GetMap()->Add(linkedGO); } @@ -6700,6 +6883,7 @@ void Spell::SummonGuardian(uint32 entry, SummonPropertiesEntry const *properties switch(m_spellInfo->Id) { case 1122: // Inferno + amount = 1; break; } int32 duration = GetSpellDuration(m_spellInfo); @@ -6714,11 +6898,12 @@ void Spell::SummonGuardian(uint32 entry, SummonPropertiesEntry const *properties TempSummon *summon = map->SummonCreature(entry, px, py, pz, m_caster->GetOrientation(), properties, duration, caster); if(!summon) return; - if(summon->HasSummonMask(SUMMON_MASK_GUARDIAN)) ((Guardian*)summon)->InitStatsForLevel(level); summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + if(summon->HasSummonMask(SUMMON_MASK_MINION) && m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + ((Minion*)summon)->SetFollowAngle(m_caster->GetAngle(summon)); summon->AI()->EnterEvadeMode(); } @@ -6769,4 +6954,23 @@ void Spell::EffectRenamePet(uint32 /*eff_idx*/) return; unitTarget->SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED); -}
\ No newline at end of file +} + +void Spell::EffectPlayMusic(uint32 i) +{ + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + uint32 soundid = m_spellInfo->EffectMiscValue[i]; + + if (!sSoundEntriesStore.LookupEntry(soundid)) + { + sLog.outError("EffectPlayMusic: Sound (Id: %u) not exist in spell %u.",soundid,m_spellInfo->Id); + return; + } + + WorldPacket data(SMSG_PLAY_MUSIC, 4); + data << uint32(soundid); + ((Player*)unitTarget)->GetSession()->SendPacket(&data); +} + diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index 74313eaa38b..dd88ef8e50a 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -537,3 +537,79 @@ void WorldSession::HandleSpellClick( WorldPacket & recv_data ) if(unit->isVehicle()) _player->EnterVehicle((Vehicle*)unit); } + +void WorldSession::HandleMirrrorImageDataRequest( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: CMSG_GET_MIRRORIMAGE_DATA"); + CHECK_PACKET_SIZE(recv_data, 8); + uint64 guid; + recv_data >> guid; + + // Get unit for which data is needed by client + Unit *unit = ObjectAccessor::GetObjectInWorld(guid, (Unit*)NULL); + if(!unit) + return; + // Get creator of the unit + Unit *creator = ObjectAccessor::GetObjectInWorld(unit->GetCreatorGUID(),(Unit*)NULL); + if (!creator) + return; + WorldPacket data(SMSG_MIRRORIMAGE_DATA, 68); + data << (uint64)guid; + data << (uint32)creator->GetDisplayId(); + if (creator->GetTypeId()==TYPEID_PLAYER) + { + Player * pCreator = (Player *)creator; + data << (uint8)pCreator->getRace(); + data << (uint8)pCreator->getGender(); + data << (uint8)pCreator->getClass(); + data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 0); // skin + + data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 1); // face + data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 2); // hair + data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 3); // haircolor + data << (uint8)pCreator->GetByteValue(PLAYER_BYTES_2, 0); // facialhair + + data << (uint32)0; // unk + static const EquipmentSlots ItemSlots[] = + { + EQUIPMENT_SLOT_HEAD, + EQUIPMENT_SLOT_SHOULDERS, + EQUIPMENT_SLOT_BODY, + EQUIPMENT_SLOT_CHEST, + EQUIPMENT_SLOT_WAIST, + EQUIPMENT_SLOT_LEGS, + EQUIPMENT_SLOT_FEET, + EQUIPMENT_SLOT_WRISTS, + EQUIPMENT_SLOT_HANDS, + EQUIPMENT_SLOT_BACK, + EQUIPMENT_SLOT_TABARD, + EQUIPMENT_SLOT_END + }; + // Display items in visible slots + for (EquipmentSlots const* itr = &ItemSlots[0];*itr!=EQUIPMENT_SLOT_END;++itr) + if (Item const *item = pCreator->GetItemByPos(INVENTORY_SLOT_BAG_0, *itr)) + data << (uint32)item->GetProto()->DisplayInfoID; + else + data << (uint32)0; + } + else + { + // Skip player data for creatures + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + } + SendPacket( &data ); +} + diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index 513075ea2b6..ca01f4422b7 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -147,6 +147,7 @@ SpellMgr::SpellMgr() case TARGET_UNIT_CONE_ALLY: case TARGET_UNIT_CONE_ENTRY: case TARGET_UNIT_CONE_ENEMY_UNKNOWN: + case TARGET_UNIT_AREA_PATH: SpellTargetType[i] = TARGET_TYPE_AREA_CONE; break; case TARGET_DST_CASTER: @@ -226,6 +227,7 @@ SpellMgr::SpellMgr() case TARGET_UNIT_CONE_ENEMY: case TARGET_UNIT_CONE_ALLY: case TARGET_UNIT_CONE_ENEMY_UNKNOWN: + case TARGET_UNIT_AREA_PATH: case TARGET_UNIT_RAID_CASTER: IsAreaEffectTarget[i] = true; break; @@ -236,6 +238,7 @@ SpellMgr::SpellMgr() } } + SpellMgr::~SpellMgr() { } @@ -246,6 +249,16 @@ SpellMgr& SpellMgr::Instance() return spellMgr; } +bool SpellMgr::IsSrcTargetSpell(SpellEntry const *spellInfo) const +{ + for (uint8 i = 0; i< MAX_SPELL_EFFECTS; ++i) + { + if(SpellTargetType[spellInfo->EffectImplicitTargetA[i]] == TARGET_TYPE_AREA_SRC || SpellTargetType[spellInfo->EffectImplicitTargetB[i]] == TARGET_TYPE_AREA_SRC) + return true; + } + return false; +} + int32 GetSpellDuration(SpellEntry const *spellInfo) { if(!spellInfo) @@ -307,8 +320,6 @@ bool IsPassiveSpell(uint32 spellId) return false; if(spellInfo->Attributes & SPELL_ATTR_PASSIVE) return true; - if(spellInfo->activeIconID == 2158) //flight - return true; return false; } @@ -319,8 +330,6 @@ bool IsAutocastableSpell(uint32 spellId) return false; if(spellInfo->Attributes & SPELL_ATTR_PASSIVE) return false; - if(spellInfo->activeIconID == 2158) - return false; if(spellInfo->AttributesEx & SPELL_ATTR_EX_UNAUTOCASTABLE_BY_PET) return false; return true; @@ -331,6 +340,127 @@ bool IsHigherHankOfSpell(uint32 spellId_1, uint32 spellId_2) return spellmgr.GetSpellRank(spellId_1)<spellmgr.GetSpellRank(spellId_2); } +uint32 CalculatePowerCost(SpellEntry const * spellInfo, Unit const * caster, SpellSchoolMask schoolMask) +{ + // Spell drain all exist power on cast (Only paladin lay of Hands) + if (spellInfo->AttributesEx & SPELL_ATTR_EX_DRAIN_ALL_POWER) + { + // If power type - health drain all + if (spellInfo->powerType == POWER_HEALTH) + return caster->GetHealth(); + // Else drain all power + if (spellInfo->powerType < MAX_POWERS) + return caster->GetPower(Powers(spellInfo->powerType)); + sLog.outError("CalculateManaCost: Unknown power type '%d' in spell %d", spellInfo->powerType, spellInfo->Id); + return 0; + } + + // Base powerCost + int32 powerCost = spellInfo->manaCost; + // PCT cost from total amount + if (spellInfo->ManaCostPercentage) + { + switch (spellInfo->powerType) + { + // health as power used + case POWER_HEALTH: + powerCost += spellInfo->ManaCostPercentage * caster->GetCreateHealth() / 100; + break; + case POWER_MANA: + powerCost += spellInfo->ManaCostPercentage * caster->GetCreateMana() / 100; + break; + case POWER_RAGE: + case POWER_FOCUS: + case POWER_ENERGY: + case POWER_HAPPINESS: + powerCost += spellInfo->ManaCostPercentage * caster->GetMaxPower(Powers(spellInfo->powerType)) / 100; + break; + case POWER_RUNE: + case POWER_RUNIC_POWER: + sLog.outDebug("CalculateManaCost: Not implemented yet!"); + break; + default: + sLog.outError("CalculateManaCost: Unknown power type '%d' in spell %d", spellInfo->powerType, spellInfo->Id); + return 0; + } + } + SpellSchools school = GetFirstSchoolInMask(schoolMask); + // Flat mod from caster auras by spell school + powerCost += caster->GetInt32Value(UNIT_FIELD_POWER_COST_MODIFIER + school); + // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost) + if ( spellInfo->AttributesEx4 & SPELL_ATTR_EX4_SPELL_VS_EXTEND_COST ) + powerCost += caster->GetAttackTime(OFF_ATTACK)/100; + // Apply cost mod by spell + if(Player* modOwner = caster->GetSpellModOwner()) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COST, powerCost); + + if(spellInfo->Attributes & SPELL_ATTR_LEVEL_DAMAGE_CALCULATION) + powerCost = int32(powerCost/ (1.117f* spellInfo->spellLevel / caster->getLevel() -0.1327f)); + + // PCT mod from user auras by school + powerCost = int32(powerCost * (1.0f+caster->GetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+school))); + if (powerCost < 0) + powerCost = 0; + return powerCost; +} + +AuraState GetSpellAuraState(SpellEntry const * spellInfo) +{ + // Seals + if (IsSealSpell(spellInfo)) + return (AURA_STATE_JUDGEMENT); + + // Conflagrate aura state on Immolate and Shadowflame + if (spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && + // Immolate + ((spellInfo->SpellFamilyFlags[0] & 4) || + // Shadowflame + (spellInfo->SpellFamilyFlags[2] & 2))) + return (AURA_STATE_CONFLAGRATE); + + // Faerie Fire (druid versions) + if (spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && spellInfo->SpellFamilyFlags[0] & 0x400) + return (AURA_STATE_FAERIE_FIRE); + + // Sting (hunter's pet ability) + if (spellInfo->Category == 1133) + return (AURA_STATE_FAERIE_FIRE); + + // Victorious + if (spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && spellInfo->SpellFamilyFlags[1] & 0x00040000) + return (AURA_STATE_WARRIOR_VICTORY_RUSH); + + // Swiftmend state on Regrowth & Rejuvenation + if (spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && spellInfo->SpellFamilyFlags[0] & 0x50 ) + return (AURA_STATE_SWIFTMEND); + + // Deadly poison aura state + if(spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && spellInfo->SpellFamilyFlags[0] & 0x10000) + return (AURA_STATE_DEADLY_POISON); + + // Enrage aura state + if(spellInfo->Dispel == DISPEL_ENRAGE) + return (AURA_STATE_ENRAGE); + + // Bleeding aura state + if (GetAllSpellMechanicMask(spellInfo) & 1<<MECHANIC_BLEED) + return (AURA_STATE_BLEEDING); + + if(GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_FROST) + { + for (uint8 i = 0;i<MAX_SPELL_EFFECTS;++i) + { + if (spellInfo->EffectApplyAuraName[i]==SPELL_AURA_MOD_STUN + || spellInfo->EffectApplyAuraName[i]==SPELL_AURA_MOD_ROOT) + { + return (AURA_STATE_FROZEN); + break; + } + } + } + return AURA_STATE_NONE; +} + SpellSpecific GetSpellSpecific(uint32 spellId) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); @@ -428,8 +558,8 @@ SpellSpecific GetSpellSpecific(uint32 spellId) if (spellInfo->Dispel == DISPEL_POISON) return SPELL_STING; - // only hunter aspects have this - if( spellInfo->SpellFamilyFlags[1] & 0x00440000 || spellInfo->SpellFamilyFlags[0] & 0x00380000 || spellInfo->SpellFamilyFlags[2] & 0x00001010) + // only hunter aspects have this (but not all aspects in hunter family) + if( spellInfo->SpellFamilyFlags.HasFlag(0x00380000, 0x00440000, 0x00001010)) return SPELL_ASPECT; break; @@ -442,15 +572,17 @@ SpellSpecific GetSpellSpecific(uint32 spellId) if (spellInfo->SpellFamilyFlags[0] & 0x11010002) return SPELL_BLESSING; - if ((spellInfo->SpellFamilyFlags[1] & 0x000008 || spellInfo->SpellFamilyFlags[0] & 0x20180400) && (spellInfo->AttributesEx3 & 0x200)) + if (spellInfo->SpellFamilyFlags[0] & 0x00002190) + return SPELL_HAND; + + // Judgement of Wisdom, Judgement of Light, Judgement of Justice + if (spellInfo->Id == 20184 || spellInfo->Id == 20185 || spellInfo->Id == 20186) return SPELL_JUDGEMENT; - for (int i = 0; i < 3; ++i) - { - // only paladin auras have this (for palaldin class family) - if (spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_RAID) - return SPELL_AURA; - } + // only paladin auras have this (for palaldin class family) + if( spellInfo->SpellFamilyFlags[2] & 0x00000020 ) + return SPELL_AURA; + break; } case SPELLFAMILY_SHAMAN: @@ -465,7 +597,8 @@ SpellSpecific GetSpellSpecific(uint32 spellId) return spellmgr.GetSpellElixirSpecific(spellInfo->Id); case SPELLFAMILY_DEATHKNIGHT: - if ((spellInfo->Attributes & 0x10) && (spellInfo->AttributesEx2 & 0x10) && (spellInfo->AttributesEx4 & 0x200000)) + if (spellInfo->Id == SPELL_ID_BLOOD_PRESENCE || spellInfo->Id == SPELL_ID_FROST_PRESENCE || spellInfo->Id == SPELL_ID_UNHOLY_PRESENCE) + //if (spellInfo->Category == 47) return SPELL_PRESENCE; break; } @@ -501,6 +634,7 @@ bool IsSingleFromSpellSpecificPerCaster(uint32 spellSpec1,uint32 spellSpec2) { case SPELL_SEAL: case SPELL_BLESSING: + case SPELL_HAND: case SPELL_AURA: case SPELL_STING: case SPELL_CURSE: @@ -569,13 +703,16 @@ bool IsPositiveTarget(uint32 targetA, uint32 targetB) return true; } -bool IsPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) +bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) const { SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); if (!spellproto) return false; switch(spellId) { + case 1852: // Silenced (GM) + case 46392: // Focused Assault + case 46393: // Brutal Assault case 28441: // not positive dummy spell case 37675: // Chaos Blast case 41519: // Mark of Stormrage @@ -584,6 +721,7 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) case 31719: // Suspension case 61987: // Avenging Wrath Marker case 11196: // Recently Bandadged + case 50524: // Runic Power Feed return false; case 12042: // Arcane Power return true; @@ -631,6 +769,7 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) case 38637: // Nether Exhaustion (red) case 38638: // Nether Exhaustion (green) case 38639: // Nether Exhaustion (blue) + case 11196: // Recently Bandaged return false; // some spells have unclear target modes for selection, so just make effect positive case 27184: @@ -673,7 +812,7 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) continue; // if non-positive trigger cast targeted to positive target this main cast is non-positive // this will place this spell auras as debuffs - if(IsPositiveTarget(spellTriggeredProto->EffectImplicitTargetA[effIndex],spellTriggeredProto->EffectImplicitTargetB[effIndex]) && !IsPositiveEffect(spellTriggeredId,i, true)) + if(IsPositiveTarget(spellTriggeredProto->EffectImplicitTargetA[effIndex],spellTriggeredProto->EffectImplicitTargetB[effIndex]) && !_isPositiveEffect(spellTriggeredId,i, true)) return false; } } @@ -759,7 +898,7 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) for (uint8 i=0;i<MAX_SPELL_EFFECTS;++i) { if (i != effIndex) - if (IsPositiveEffect(spellId, i, true)) + if (_isPositiveEffect(spellId, i, true)) { negative = false; break; @@ -806,14 +945,30 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) if (!deep && spellproto->EffectTriggerSpell[effIndex] && !spellproto->EffectApplyAuraName[effIndex] && IsPositiveTarget(spellproto->EffectImplicitTargetA[effIndex],spellproto->EffectImplicitTargetB[effIndex]) - && !IsPositiveSpell(spellproto->EffectTriggerSpell[effIndex], true)) + && !_isPositiveSpell(spellproto->EffectTriggerSpell[effIndex], true)) return false; // ok, positive return true; } -bool IsPositiveSpell(uint32 spellId, bool deep) +bool IsPositiveSpell(uint32 spellId) +{ + return !(spellmgr.GetSpellCustomAttr(spellId) & SPELL_ATTR_CU_NEGATIVE); +} + +bool IsPositiveEffect(uint32 spellId, uint32 effIndex) +{ + switch(effIndex) + { + default: + case 0: return !(spellmgr.GetSpellCustomAttr(spellId) & SPELL_ATTR_CU_NEGATIVE_EFF0); + case 1: return !(spellmgr.GetSpellCustomAttr(spellId) & SPELL_ATTR_CU_NEGATIVE_EFF1); + case 2: return !(spellmgr.GetSpellCustomAttr(spellId) & SPELL_ATTR_CU_NEGATIVE_EFF2); + } +} + +bool SpellMgr::_isPositiveSpell(uint32 spellId, bool deep) const { SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); if (!spellproto) return false; @@ -821,7 +976,7 @@ bool IsPositiveSpell(uint32 spellId, bool deep) // spells with at least one negative effect are considered negative // some self-applied spells have negative effects but in self casting case negative check ignored. for (int i = 0; i < 3; ++i) - if (!IsPositiveEffect(spellId, i, deep)) + if (!_isPositiveEffect(spellId, i, deep)) return false; return true; } @@ -875,17 +1030,6 @@ bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellI return false; } -bool IsAuraAddedBySpell(uint32 auraType, uint32 spellId) -{ - SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); - if (!spellproto) return false; - - for (int i = 0; i < 3; ++i) - if (spellproto->EffectApplyAuraName[i] == auraType) - return true; - return false; -} - SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form) { // talents that learn spells can have stance requirements that need ignore @@ -1062,77 +1206,6 @@ void SpellMgr::LoadSpellTargetPositions() sLog.outString( ">> Loaded %u spell teleport coordinates", count ); } -void SpellMgr::LoadSpellAffects() -{ - mSpellAffectMap.clear(); // need for reload case - - uint32 count = 0; - - // 0 1 2 3 4 - QueryResult *result = WorldDatabase.Query("SELECT entry, effectId, SpellClassMask0, SpellClassMask1, SpellClassMask2 FROM spell_affect"); - if( !result ) - { - - barGoLink bar( 1 ); - - bar.step(); - - sLog.outString(); - sLog.outString( ">> Loaded %u spell affect definitions", count ); - return; - } - - barGoLink bar( result->GetRowCount() ); - - do - { - Field *fields = result->Fetch(); - - bar.step(); - - uint16 entry = fields[0].GetUInt16(); - uint8 effectId = fields[1].GetUInt8(); - - SpellEntry const* spellInfo = sSpellStore.LookupEntry(entry); - - if (!spellInfo) - { - sLog.outErrorDb("Spell %u listed in `spell_affect` does not exist", entry); - continue; - } - - if (effectId >= 3) - { - sLog.outErrorDb("Spell %u listed in `spell_affect` have invalid effect index (%u)", entry,effectId); - continue; - } - - flag96 affect(fields[2].GetUInt32(), fields[3].GetUInt32(), fields[4].GetUInt32()); - - // Spell.dbc have own data - if (effectId>3) - continue; - - flag96 dbc_affect; - dbc_affect = spellInfo->EffectSpellClassMask[effectId]; - if(dbc_affect[0] == affect[0] && dbc_affect[1] == affect[1] && dbc_affect[2] == affect[2]) - { - char text[]="ABC"; - sLog.outErrorDb("Spell %u listed in `spell_affect` have redundant (same with EffectSpellClassMask%c) data for effect index (%u) and not needed, skipped.", entry, text[effectId], effectId); - continue; - } - - mSpellAffectMap[(entry<<8) + effectId] = affect; - - ++count; - } while( result->NextRow() ); - - delete result; - - sLog.outString(); - sLog.outString( ">> Loaded %u custom spell affect definitions", count ); -} - bool SpellMgr::IsAffectedByMod(SpellEntry const *spellInfo, SpellModifier *mod) const { // false for spellInfo == NULL @@ -1225,8 +1298,8 @@ void SpellMgr::LoadSpellBonusess() { mSpellBonusMap.clear(); // need for reload case uint32 count = 0; - // 0 1 2 3 - QueryResult *result = WorldDatabase.Query("SELECT entry, direct_bonus, dot_bonus, ap_bonus FROM spell_bonus_data"); + // 0 1 2 3 4 + QueryResult *result = WorldDatabase.Query("SELECT entry, direct_bonus, dot_bonus, ap_bonus, ap_dot_bonus FROM spell_bonus_data"); if( !result ) { barGoLink bar( 1 ); @@ -1255,8 +1328,10 @@ void SpellMgr::LoadSpellBonusess() sbe.direct_damage = fields[1].GetFloat(); sbe.dot_damage = fields[2].GetFloat(); sbe.ap_bonus = fields[3].GetFloat(); + sbe.ap_dot_bonus = fields[4].GetFloat(); mSpellBonusMap[entry] = sbe; + ++count; } while( result->NextRow() ); delete result; @@ -1265,7 +1340,7 @@ void SpellMgr::LoadSpellBonusess() sLog.outString( ">> Loaded %u extra spell bonus data", count); } -bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra) +bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra, bool active) { // No extra req need uint32 procEvent_procEx = PROC_EX_NONE; @@ -1278,42 +1353,52 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr /* Check Periodic Auras - * Both hots and dots can trigger if spell has no PROC_FLAG_SUCCESSFUL_DAMAGING_SPELL_HIT - nor PROC_FLAG_SUCCESSFUL_HEALING_SPELL + *Dots can trigger if spell has no PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL + nor PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL + + *Only Hots can trigger if spell has PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL - *Only Hots can trigger if spell has PROC_FLAG_SUCCESSFUL_HEALING_SPELL + *Only dots can trigger if spell has both positivity flags or PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL - *Only dots can trigger if spell has both positivity flags or PROC_FLAG_SUCCESSFUL_DAMAGING_SPELL_HIT + *Aura has to have PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL or spellfamily specified to trigger from Hot */ if (procFlags & PROC_FLAG_ON_DO_PERIODIC) { - if (EventProcFlag & PROC_FLAG_SUCCESSFUL_DAMAGING_SPELL_HIT) + if (EventProcFlag & PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL) { if (!(procExtra & PROC_EX_INTERNAL_DOT)) return false; } - else if (EventProcFlag & PROC_FLAG_SUCCESSFUL_HEALING_SPELL + else if (EventProcFlag & PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL && !(procExtra & PROC_EX_INTERNAL_HOT)) return false; + else if (procExtra & PROC_EX_INTERNAL_HOT) + procExtra |= PROC_EX_INTERNAL_REQ_FAMILY; } if (procFlags & PROC_FLAG_ON_TAKE_PERIODIC) { - if (EventProcFlag & PROC_FLAG_TAKEN_DAMAGING_SPELL_HIT) + if (EventProcFlag & PROC_FLAG_TAKEN_NEGATIVE_MAGIC_SPELL) { if (!(procExtra & PROC_EX_INTERNAL_DOT)) return false; } - else if (EventProcFlag & PROC_FLAG_TAKEN_HEALING_SPELL + else if (EventProcFlag & PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL && !(procExtra & PROC_EX_INTERNAL_HOT)) return false; + else if (procExtra & PROC_EX_INTERNAL_HOT) + procExtra |= PROC_EX_INTERNAL_REQ_FAMILY; } + // Trap casts are active by default + if (procFlags & PROC_FLAG_ON_TRAP_ACTIVATION) + active = true; // Always trigger for this - if (procFlags & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_ON_TRAP_ACTIVATION)) + if (procFlags & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH)) return true; + if (spellProcEvent) // Exist event data { // Store extra req @@ -1342,11 +1427,14 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr if ((spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags ) == 0) return false; hasFamilyMask = true; + // Some spells are not considered as active even with have spellfamilyflags + if (!(procEvent_procEx & PROC_EX_ONLY_ACTIVE_SPELL)) + active = true; } } } - if (procExtra & PROC_EX_INTERNAL_REQ_FAMILY) + if (procExtra & (PROC_EX_INTERNAL_REQ_FAMILY)) { if (!hasFamilyMask) return false; @@ -1355,15 +1443,27 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr // Check for extra req (if none) and hit/crit if (procEvent_procEx == PROC_EX_NONE) { - // No extra req, so can trigger only for hit/crit - if((procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT))) + // No extra req, so can trigger only for hit/crit - spell has to be active + if((procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) && active) return true; } else // Passive spells hits here only if resist/reflect/immune/evade { - // Exist req for PROC_EX_EX_TRIGGER_ALWAYS - if ((procExtra & AURA_SPELL_PROC_EX_MASK) && (procEvent_procEx & PROC_EX_EX_TRIGGER_ALWAYS)) - return true; + if (procExtra & AURA_SPELL_PROC_EX_MASK) + { + // if spell marked as procing only from not active spells + if (active && procEvent_procEx & PROC_EX_NOT_ACTIVE_SPELL) + return false; + // if spell marked as procing only from active spells + if (!active && procEvent_procEx & PROC_EX_ONLY_ACTIVE_SPELL) + return false; + // Exist req for PROC_EX_EX_TRIGGER_ALWAYS + if (procEvent_procEx & PROC_EX_EX_TRIGGER_ALWAYS) + return true; + // PROC_EX_NOT_ACTIVE_SPELL and PROC_EX_ONLY_ACTIVE_SPELL flags handle: if passed checks before + if ((procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) && ((procEvent_procEx & (AURA_SPELL_PROC_EX_MASK | AURA_REMOVE_PROC_EX_MASK)) == 0)) + return true; + } // Check Extra Requirement like (hit/crit/miss/resist/parry/dodge/block/immune/reflect/absorb and other) if (procEvent_procEx & procExtra) return true; @@ -1399,7 +1499,7 @@ void SpellMgr::LoadSpellElixirs() bar.step(); - uint16 entry = fields[0].GetUInt16(); + uint32 entry = fields[0].GetUInt32(); uint8 mask = fields[1].GetUInt8(); SpellEntry const* spellInfo = sSpellStore.LookupEntry(entry); @@ -1423,12 +1523,50 @@ void SpellMgr::LoadSpellElixirs() void SpellMgr::LoadSpellThreats() { - sSpellThreatStore.Free(); // for reload + mSpellThreatMap.clear(); // need for reload case - sSpellThreatStore.Load(); + uint32 count = 0; + + // 0 1 + QueryResult *result = WorldDatabase.Query("SELECT entry, Threat FROM spell_threat"); + if( !result ) + { + + barGoLink bar( 1 ); + + bar.step(); + + sLog.outString(); + sLog.outString( ">> Loaded %u aggro generating spells", count ); + return; + } + + barGoLink bar( result->GetRowCount() ); + + do + { + Field *fields = result->Fetch(); + + bar.step(); + + uint32 entry = fields[0].GetUInt32(); + uint16 Threat = fields[1].GetUInt16(); + + if (!sSpellStore.LookupEntry(entry)) + { + sLog.outErrorDb("Spell %u listed in `spell_threat` does not exist", entry); + continue; + } + + mSpellThreatMap[entry] = Threat; + + ++count; + } while( result->NextRow() ); + + delete result; - sLog.outString( ">> Loaded %u aggro generating spells", sSpellThreatStore.RecordCount ); sLog.outString(); + sLog.outString( ">> Loaded %u aggro generating spells", count ); } bool SpellMgr::IsRankSpellDueToSpell(SpellEntry const *spellInfo_1,uint32 spellId_2) const @@ -1666,7 +1804,13 @@ void SpellMgr::LoadSpellLearnSpells() if(!sSpellStore.LookupEntry(node.spell)) { - sLog.outErrorDb("Spell %u listed in `spell_learn_spell` does not exist",node.spell); + sLog.outErrorDb("Spell %u listed in `spell_learn_spell` learning not existed spell %u",spell_id,node.spell); + continue; + } + + if(GetTalentSpellCost(node.spell)) + { + sLog.outErrorDb("Spell %u listed in `spell_learn_spell` attempt learning talent spell %u, skipped",spell_id,node.spell); continue; } @@ -1807,6 +1951,9 @@ void SpellMgr::LoadSpellScriptTarget() } break; } + case SPELL_TARGET_TYPE_CONTROLLED: + if( targetEntry==0 ) + sLog.outErrorDb("Table `spell_script_target`: creature template entry %u does not exist.",targetEntry); default: { //players @@ -1892,8 +2039,8 @@ void SpellMgr::LoadSpellPetAuras() uint32 count = 0; - // 0 1 2 - QueryResult *result = WorldDatabase.Query("SELECT spell, pet, aura FROM spell_pet_auras"); + // 0 1 2 3 + QueryResult *result = WorldDatabase.Query("SELECT spell, effectId, pet, aura FROM spell_pet_auras"); if( !result ) { @@ -1914,11 +2061,12 @@ void SpellMgr::LoadSpellPetAuras() bar.step(); - uint16 spell = fields[0].GetUInt16(); - uint16 pet = fields[1].GetUInt16(); - uint16 aura = fields[2].GetUInt16(); + uint32 spell = fields[0].GetUInt32(); + uint8 eff = fields[1].GetUInt8(); + uint32 pet = fields[2].GetUInt32(); + uint32 aura = fields[3].GetUInt32(); - SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find(spell); + SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find((spell<<8) + eff); if(itr != mSpellPetAuraMap.end()) { itr->second.AddAura(pet, aura); @@ -1931,14 +2079,9 @@ void SpellMgr::LoadSpellPetAuras() sLog.outErrorDb("Spell %u listed in `spell_pet_auras` does not exist", spell); continue; } - int i = 0; - for(; i < 3; ++i) - if((spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA && - spellInfo->EffectApplyAuraName[i] == SPELL_AURA_DUMMY) || - spellInfo->Effect[i] == SPELL_EFFECT_DUMMY) - break; - - if(i == 3) + if (spellInfo->Effect[eff] != SPELL_EFFECT_DUMMY && + (spellInfo->Effect[eff] != SPELL_EFFECT_APPLY_AURA || + spellInfo->EffectApplyAuraName[eff] != SPELL_AURA_DUMMY)) { sLog.outError("Spell %u listed in `spell_pet_auras` does not have dummy aura or dummy effect", spell); continue; @@ -1951,8 +2094,8 @@ void SpellMgr::LoadSpellPetAuras() continue; } - PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_PET, spellInfo->CalculateSimpleValue(i)); - mSpellPetAuraMap[spell] = pa; + PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[eff] == TARGET_UNIT_PET, spellInfo->CalculateSimpleValue(eff)); + mSpellPetAuraMap[(spell<<8) + eff] = pa; } ++count; @@ -2159,8 +2302,9 @@ bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg) case 0: continue; - // craft spell for crafting non-existed item (break client recipes list show) + // craft spell for crafting non-existed item (break client recipes list show) case SPELL_EFFECT_CREATE_ITEM: + case SPELL_EFFECT_CREATE_ITEM_2: { if(!ObjectMgr::GetItemPrototype( spellInfo->EffectItemType[i] )) { @@ -2339,10 +2483,15 @@ void SpellMgr::LoadSpellAreas() continue; } - if(spellInfo->EffectApplyAuraName[0]!=SPELL_AURA_DUMMY && spellInfo->EffectApplyAuraName[0]!=SPELL_AURA_PHASE) + switch(spellInfo->EffectApplyAuraName[0]) { - sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell requirement (%u) without dummy/phase aura in effect 0", spell,abs(spellArea.auraSpell)); - continue; + case SPELL_AURA_DUMMY: + case SPELL_AURA_PHASE: + case SPELL_AURA_GHOST: + break; + default: + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell requirement (%u) without dummy/phase/ghost aura in effect 0", spell,abs(spellArea.auraSpell)); + continue; } if(abs(spellArea.auraSpell)==spellArea.spellId) @@ -2435,8 +2584,12 @@ void SpellMgr::LoadSpellAreas() SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player) { + // allow in GM-mode + if (player && player->isGameMaster()) + return SPELL_CAST_OK; + // normal case - if( spellInfo->AreaGroupId > 0) + if (spellInfo->AreaGroupId > 0) { bool found = false; AreaGroupEntry const* groupEntry = sAreaGroupStore.LookupEntry(spellInfo->AreaGroupId); @@ -2455,9 +2608,18 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell return SPELL_FAILED_INCORRECT_AREA; } + // continent limitation (virtual continent) + if (spellInfo->AttributesEx4 & SPELL_ATTR_EX4_CAST_ONLY_IN_OUTLAND) + { + uint32 v_map = GetVirtualMapForMapAndZone(map_id, zone_id); + MapEntry const* mapEntry = sMapStore.LookupEntry(v_map); + if(!mapEntry || mapEntry->addon < 1 || !mapEntry->IsContinent()) + return SPELL_FAILED_INCORRECT_AREA; + } + // DB base check (if non empty then must fit at least single for allow) SpellAreaMapBounds saBounds = spellmgr.GetSpellAreaMapBounds(spellInfo->Id); - if(saBounds.first != saBounds.second) + if (saBounds.first != saBounds.second) { for(SpellAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) { @@ -2484,21 +2646,21 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell case 44535: // Spirit Heal (mana) { MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); - if(!mapEntry) + if (!mapEntry) return SPELL_FAILED_INCORRECT_AREA; return mapEntry->IsBattleGround() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } case 44521: // Preparation { - if(!player) + if (!player) return SPELL_FAILED_REQUIRES_AREA; MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); - if(!mapEntry) + if (!mapEntry) return SPELL_FAILED_INCORRECT_AREA; - if(!mapEntry->IsBattleGround()) + if (!mapEntry->IsBattleGround()) return SPELL_FAILED_REQUIRES_AREA; BattleGround* bg = player->GetBattleGround(); @@ -2510,14 +2672,14 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell case 35775: // Green Team (Horde) { MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); - if(!mapEntry) + if (!mapEntry) return SPELL_FAILED_INCORRECT_AREA; return mapEntry->IsBattleArena() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } case 32727: // Arena Preparation { - if(!player) + if (!player) return SPELL_FAILED_REQUIRES_AREA; MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); @@ -2562,13 +2724,25 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto // Explicit Diminishing Groups switch(spellproto->SpellFamilyName) { + case SPELLFAMILY_GENERIC: + // some generic arena related spells have by some strange reason MECHANIC_TURN + if (spellproto->Mechanic == MECHANIC_TURN) + return DIMINISHING_NONE; + break; + case SPELLFAMILY_MAGE: + { + // Frostbite 0x80000000 + if (spellproto->SpellFamilyFlags[1] & 0x80000000) + return DIMINISHING_TRIGGER_ROOT; + // Frost Nova / Freeze (Water Elemental) + else if (spellproto->SpellIconID == 193) + return DIMINISHING_CONTROL_ROOT; + break; + } case SPELLFAMILY_ROGUE: { - // Sap - if (spellproto->SpellFamilyFlags[0] & 0x80) - return DIMINISHING_POLYMORPH; - // Gouge - else if (spellproto->SpellFamilyFlags[0] & 0x8) + // Sap 0x80 Gouge 0x8 + if (spellproto->SpellFamilyFlags[0] & 0x88) return DIMINISHING_POLYMORPH; // Blind else if (spellproto->SpellFamilyFlags[0] & 0x1000000) @@ -2576,6 +2750,9 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto // Cheap Shot else if (spellproto->SpellFamilyFlags[0] & 0x400) return DIMINISHING_CHEAPSHOT_POUNCE; + // Crippling poison - Limit to 10 seconds in PvP (No SpellFamilyFlags) + else if (spellproto->SpellIconID == 163) + return DIMINISHING_LIMITONLY; break; } case SPELLFAMILY_WARLOCK: @@ -2596,17 +2773,15 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto // Pounce if (spellproto->SpellFamilyFlags[0] & 0x20000) return DIMINISHING_CHEAPSHOT_POUNCE; - + // Cyclone + else if (spellproto->SpellFamilyFlags[1] & 0x20) + return DIMINISHING_CYCLONE; //Entangling Roots: to force natures grasp proc to be control root else if (spellproto->SpellFamilyFlags[0] & 0x00000200) return DIMINISHING_CONTROL_ROOT; - break; - } - case SPELLFAMILY_MAGE: - { - // Frostbite - if (spellproto->SpellFamilyFlags[1] & 0x80000000) - return DIMINISHING_TRIGGER_ROOT; + // Faerie Fire + else if (spellproto->SpellFamilyFlags[0] & 0x400) + return DIMINISHING_LIMITONLY; break; } case SPELLFAMILY_WARRIOR: @@ -2617,8 +2792,8 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto // Intimidating Shout else if (spellproto->SpellFamilyFlags[0] & 0x40000) return DIMINISHING_FEAR_BLIND; - // Charge - else if (spellproto->SpellFamilyFlags[0] & 0x1) + // Charge Stun + else if (spellproto->SpellFamilyFlags[0] & 0x01000000) return DIMINISHING_NONE; break; } @@ -2629,9 +2804,16 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto return DIMINISHING_POLYMORPH; break; } + case SPELLFAMILY_PRIEST: + { + // Vampiric Embrace + if ((spellproto->SpellFamilyFlags[0] & 0x4) && spellproto->SpellIconID == 150) + return DIMINISHING_LIMITONLY; + break; + } case SPELLFAMILY_DEATHKNIGHT: { - // Hungering Cold + // Hungering Cold (no flags) if (spellproto->SpellIconID == 2797) return DIMINISHING_POLYMORPH; break; @@ -2643,15 +2825,20 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto // Get by mechanic uint32 mechanic = GetAllSpellMechanicMask(spellproto); if (mechanic == MECHANIC_NONE) return DIMINISHING_NONE; - if (mechanic & (1<<MECHANIC_STUN)) return triggered ? DIMINISHING_TRIGGER_STUN : DIMINISHING_CONTROL_STUN; - if (mechanic & ((1<<MECHANIC_SLEEP) | (1<<MECHANIC_FREEZE))) return DIMINISHING_SLEEP_FREEZE; + if (mechanic & ((1<<MECHANIC_STUN) | + (1<<MECHANIC_SHACKLE))) return triggered ? DIMINISHING_TRIGGER_STUN : DIMINISHING_CONTROL_STUN; + if (mechanic & ((1<<MECHANIC_SLEEP) | + (1<<MECHANIC_FREEZE))) return DIMINISHING_FREEZE_SLEEP; if (mechanic & (1<<MECHANIC_POLYMORPH)) return DIMINISHING_POLYMORPH; if (mechanic & (1<<MECHANIC_ROOT)) return triggered ? DIMINISHING_TRIGGER_ROOT : DIMINISHING_CONTROL_ROOT; - if (mechanic & (1<<MECHANIC_FEAR)) return DIMINISHING_FEAR; + if (mechanic & ((1<<MECHANIC_FEAR) | + (1<<MECHANIC_TURN))) return DIMINISHING_FEAR_BLIND; if (mechanic & (1<<MECHANIC_CHARM)) return DIMINISHING_CHARM; if (mechanic & (1<<MECHANIC_SILENCE)) return DIMINISHING_SILENCE; if (mechanic & (1<<MECHANIC_DISARM)) return DIMINISHING_DISARM; - if (mechanic & ((1<<MECHANIC_KNOCKOUT) | (1<<MECHANIC_SAPPED))) return DIMINISHING_KNOCKOUT; + if (mechanic & (1<<MECHANIC_FREEZE)) return DIMINISHING_FREEZE_SLEEP; + if (mechanic & ((1<<MECHANIC_KNOCKOUT) | + (1<<MECHANIC_SAPPED))) return DIMINISHING_KNOCKOUT; if (mechanic & (1<<MECHANIC_BANISH)) return DIMINISHING_BANISH; if (mechanic & (1<<MECHANIC_HORROR)) return DIMINISHING_DEATHCOIL; @@ -2664,20 +2851,63 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto return DIMINISHING_NONE; } +int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellEntry const* spellproto) +{ + if(!IsDiminishingReturnsGroupDurationLimited(group)) + return 0; + + // Explicit diminishing duration + switch(spellproto->SpellFamilyName) + { + case SPELLFAMILY_HUNTER: + { + // Wyvern Sting + if (spellproto->SpellFamilyFlags[1] & 0x1000) + return 6000; + break; + } + case SPELLFAMILY_PALADIN: + { + // Repentance - limit to 6 seconds in PvP + if (spellproto->SpellFamilyFlags[0] & 0x4) + return 6000; + break; + } + case SPELLFAMILY_DRUID: + { + // Faerie Fire - limit to 40 seconds in PvP (3.1) + if (spellproto->SpellFamilyFlags[0] & 0x400) + return 40000; + break; + } + case SPELLFAMILY_PRIEST: + { + // Vampiric Embrace - limit to 60 seconds in PvP (3.1) + if ((spellproto->SpellFamilyFlags[0] & 0x4) && spellproto->SpellIconID == 150) + return 60000; + break; + } + default: + break; + } + + return 10000; +} + bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group) { switch(group) { case DIMINISHING_CONTROL_STUN: case DIMINISHING_TRIGGER_STUN: - case DIMINISHING_SLEEP_FREEZE: + case DIMINISHING_FREEZE_SLEEP: case DIMINISHING_CONTROL_ROOT: case DIMINISHING_TRIGGER_ROOT: - case DIMINISHING_FEAR: case DIMINISHING_FEAR_BLIND: case DIMINISHING_CHARM: case DIMINISHING_POLYMORPH: case DIMINISHING_KNOCKOUT: + case DIMINISHING_CYCLONE: case DIMINISHING_BANISH: case DIMINISHING_LIMITONLY: case DIMINISHING_CHEAPSHOT_POUNCE: @@ -2696,18 +2926,18 @@ DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group) case DIMINISHING_CONTROL_STUN: case DIMINISHING_TRIGGER_STUN: case DIMINISHING_CHEAPSHOT_POUNCE: - case DIMINISHING_FEAR_BLIND: + case DIMINISHING_CYCLONE: return DRTYPE_ALL; - case DIMINISHING_BANISH: + case DIMINISHING_FEAR_BLIND: case DIMINISHING_CONTROL_ROOT: case DIMINISHING_TRIGGER_ROOT: - case DIMINISHING_FEAR: case DIMINISHING_CHARM: case DIMINISHING_POLYMORPH: case DIMINISHING_SILENCE: case DIMINISHING_DISARM: case DIMINISHING_DEATHCOIL: - case DIMINISHING_SLEEP_FREEZE: + case DIMINISHING_FREEZE_SLEEP: + case DIMINISHING_BANISH: case DIMINISHING_KNOCKOUT: return DRTYPE_PLAYER; default: @@ -2784,7 +3014,7 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2, bool SpellSpecific spellId_spec_2 = GetSpellSpecific(spellId_2); if (spellId_spec_1 && spellId_spec_2) if (IsSingleFromSpellSpecificPerTarget(spellId_spec_1, spellId_spec_2) - ||(IsSingleFromSpellSpecificPerCaster(spellId_spec_1, spellId_spec_2) && sameCaster)) + ||(sameCaster && IsSingleFromSpellSpecificPerCaster(spellId_spec_1, spellId_spec_2))) return true; if(spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName) @@ -2795,6 +3025,11 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2, bool for(uint32 i = 0; i < 3; ++i) if (spellInfo_1->Effect[i] == SPELL_EFFECT_APPLY_AURA || spellInfo_1->Effect[i] == SPELL_EFFECT_PERSISTENT_AREA_AURA) + { + // not channeled AOE effects can stack + if(IsAreaEffectTarget[spellInfo_1->EffectImplicitTargetA[i]] || IsAreaEffectTarget[spellInfo_1->EffectImplicitTargetB[i]] + && !IsChanneledSpell(spellInfo_1)) + continue; // not area auras (shaman totem) switch(spellInfo_1->EffectApplyAuraName[i]) { @@ -2808,17 +3043,19 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2, bool case SPELL_AURA_POWER_BURN_MANA: case SPELL_AURA_OBS_MOD_ENERGY: case SPELL_AURA_OBS_MOD_HEALTH: + case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE: return false; default: break; } + } } spellId_2 = GetLastSpellInChain(spellId_2); spellId_1 = GetLastSpellInChain(spellId_1); // Hack for Incanter's Absorption - if (spellId_1 == spellId_2 && spellId_1 == 44413) + if (spellId_1 == spellId_2 && (spellId_1 == 44413 || (!sameCaster && spellInfo_1->AttributesEx3 & SPELL_ATTR_EX3_STACKS_FOR_DIFFERENT_CASTERS))) return false; if (spellId_1 == spellId_2) @@ -2852,7 +3089,7 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2, bool || spellInfo_1->EffectMiscValue[i] != spellInfo_2->EffectMiscValue[i]) // paladin resist aura return false; // need itemtype check? need an example to add that check - return true; + return (!(!sameCaster && spellInfo_1->AttributesEx3 & SPELL_ATTR_EX3_STACKS_FOR_DIFFERENT_CASTERS)); } bool IsDispelableBySpell(SpellEntry const * dispelSpell, uint32 spellId, bool def) @@ -3167,7 +3404,7 @@ void SpellMgr::LoadSpellChains() for (itr2 = RankErrorMap.lower_bound(err_entry);itr2!=RankErrorMap.upper_bound(err_entry);itr2++) { sLog.outDebug("There is a duplicate rank entry (%s) for spell: %u",itr2->first,itr2->second->second.Id); - if (!(itr2->second->second.Id==52375 || itr2->second->second.Id==45902)) + if (!(itr2->second->second.Id==47541 || itr2->second->second.Id==45902 || itr2->second->second.Id==7620)) { sLog.outDebug("Spell %u removed from chain data.",itr2->second->second.Id); RankMap.erase(itr2->second); @@ -3248,7 +3485,7 @@ void SpellMgr::LoadSpellChains() } } -//uncomment these two lines to print yourself list of spell_chains on startup + //uncomment these two lines to print yourself list of spell_chains on startup //for (UNORDERED_MAP<uint32, SpellChainNode>::iterator itr=mSpellChains.begin();itr!=mSpellChains.end();itr++) //sLog.outString( "Id: %u, Rank: %d , %s, %u, %u, %u, %u",itr->first,itr->second.rank, sSpellStore.LookupEntry(itr->first)->Rank[sWorld.GetDefaultDbcLocale()], itr->second.first, itr->second.last,itr->second.next ,itr->second.prev); @@ -3362,13 +3599,30 @@ void SpellMgr::LoadSpellCustomAttr() mSpellCustomAttr[i] &= ~SPELL_ATTR_CU_MOVEMENT_IMPAIR; break; } - } + } + + if(!_isPositiveEffect(i, 0, false)) + mSpellCustomAttr[i] |= SPELL_ATTR_CU_NEGATIVE_EFF0; + if(!_isPositiveEffect(i, 1, false)) + mSpellCustomAttr[i] |= SPELL_ATTR_CU_NEGATIVE_EFF1; + if(!_isPositiveEffect(i, 2, false)) + mSpellCustomAttr[i] |= SPELL_ATTR_CU_NEGATIVE_EFF2; if(spellInfo->SpellVisual[0] == 3879) mSpellCustomAttr[i] |= SPELL_ATTR_CU_CONE_BACK; + if(spellInfo->activeIconID == 2158) //flight + spellInfo->Attributes |= SPELL_ATTR_PASSIVE; + switch(i) { + // Heart of the Crusader + case 20335: + case 20336: + case 20337: + // Entries were not updated after spell effect change, we have to do that manually:/ + spellInfo->AttributesEx3 |= SPELL_ATTR_EX3_CAN_PROC_TRIGGERED; + break; case 16007: // Draco-Incarcinatrix 900 // was 46, but effect is aura effect spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_NEARBY_ENTRY; @@ -3388,6 +3642,7 @@ void SpellMgr::LoadSpellCustomAttr() case 40810: case 43267: case 43268: // Saber Lash case 42384: // Brutal Swipe case 45150: // Meteor Slash + case 64422: case 64688: // Sonic Screech mSpellCustomAttr[i] |= SPELL_ATTR_CU_SHARE_DAMAGE; break; case 59725: // Improved Spell Reflection - aoe aura @@ -3446,10 +3701,6 @@ void SpellMgr::LoadSpellCustomAttr() case 54098: // Poison Bolt Volly - Faerlina (H) spellInfo->MaxAffectedTargets = 10; break; - case 8122: case 8124: case 10888: case 10890: // Psychic Scream - case 12494: // Frostbite - spellInfo->Attributes |= SPELL_ATTR_BREAKABLE_BY_DAMAGE; - break; case 38794: case 33711: //Murmur's Touch spellInfo->MaxAffectedTargets = 1; spellInfo->EffectTriggerSpell[0] = 33760; @@ -3465,10 +3716,12 @@ void SpellMgr::LoadSpellCustomAttr() case 57761: // Fireball! case 39805: // Lightning Overload case 52437: // Sudden Death + case 64823: // Item - Druid T8 Balance 4P Bonus spellInfo->procCharges=1; break; case 44544: // Fingers of Frost spellInfo->procCharges=2; + spellInfo->EffectSpellClassMask[0] = flag96(685904631,1151048,0); break; case 28200: // Ascendance (Talisman of Ascendance trinket) spellInfo->procCharges=6; @@ -3479,6 +3732,20 @@ void SpellMgr::LoadSpellCustomAttr() case 51904: // Summon Ghouls On Scarlet Crusade (core does not know the triggered spell is summon spell) spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_CASTER; break; + case 29809: // Desecration Arm - 36 instead of 37 - typo? :/ + spellInfo->EffectRadiusIndex[0] = 37; + break; + // Master Shapeshifter: missing stance data for forms other than bear - bear version has correct data + // To prevent aura staying on target after talent unlearned + case 48420: + spellInfo->Stances = 1 << (FORM_CAT - 1); + break; + case 48421: + spellInfo->Stances = 1 << (FORM_MOONKIN - 1); + break; + case 48422: + spellInfo->Stances = 1 << (FORM_TREE - 1); + break; default: break; } diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h index 9871930a8fd..e651283fa48 100644 --- a/src/game/SpellMgr.h +++ b/src/game/SpellMgr.h @@ -25,7 +25,9 @@ // For more high level function for sSpellStore data #include "SharedDefines.h" +#include "SpellAuraDefines.h" #include "DBCStructure.h" +#include "DBCStores.h" #include "Database/SQLStorage.h" #include "Utilities/UnorderedMap.h" @@ -36,14 +38,14 @@ class Player; class Spell; - -extern SQLStorage sSpellThreatStore; +struct SpellModifier; // only used in code enum SpellCategories { SPELLCATEGORY_HEALTH_MANA_POTIONS = 4, - SPELLCATEGORY_DEVOUR_MAGIC = 12 + SPELLCATEGORY_DEVOUR_MAGIC = 12, + SPELLCATEGORY_JUDGEMENT = 1210, // Judgement (seal trigger) }; enum SpellDisableTypes @@ -120,6 +122,7 @@ enum SpellSpecific SPELL_MAGE_ARCANE_BRILLANCE = 24, SPELL_WARRIOR_ENRAGE = 25, SPELL_PRIEST_DIVINE_SPIRIT = 26, + SPELL_HAND = 27, }; #define SPELL_LINKED_MAX_SPELLS 200000 @@ -133,6 +136,7 @@ enum SpellLinkedType }; SpellSpecific GetSpellSpecific(uint32 spellId); +AuraState GetSpellAuraState(SpellEntry const * spellInfo); // Different spell properties inline float GetSpellRadiusForHostile(SpellRadiusEntry const *radius) { return (radius ? radius->radiusHostile : 0); } @@ -193,6 +197,14 @@ inline bool IsSpellHaveEffect(SpellEntry const *spellInfo, SpellEffects effect) return false; } +inline bool IsSpellHaveAura(SpellEntry const *spellInfo, AuraType aura) +{ + for(int i= 0; i < 3; ++i) + if(AuraType(spellInfo->EffectApplyAuraName[i])==aura) + return true; + return false; +} + //bool IsNoStackAuraDueToAura(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2); inline bool IsSealSpell(SpellEntry const *spellInfo) @@ -213,15 +225,16 @@ inline bool IsElementalShield(SpellEntry const *spellInfo) inline bool IsExplicitDiscoverySpell(SpellEntry const *spellInfo) { - return spellInfo->Effect[0]==SPELL_EFFECT_CREATE_ITEM_2 && spellInfo->Effect[1]==SPELL_EFFECT_SCRIPT_EFFECT; + return spellInfo->Effect[0] == SPELL_EFFECT_CREATE_RANDOM_ITEM + && spellInfo->Effect[1] == SPELL_EFFECT_SCRIPT_EFFECT + || spellInfo->Id == 64323; // Book of Glyph Mastery (Effect0==SPELL_EFFECT_SCRIPT_EFFECT without any other data) } inline bool IsLootCraftingSpell(SpellEntry const *spellInfo) { - return spellInfo->Effect[0]==SPELL_EFFECT_CREATE_ITEM_2 && ( - spellInfo->Effect[1]==SPELL_EFFECT_SCRIPT_EFFECT || // see IsExplicitDiscoverySpell - !spellInfo->EffectItemType[0] || // result item not provided - spellInfo->TotemCategory[0] == 121); // different random cards from Inscription (121==Virtuoso Inking Set category) + return spellInfo->Effect[0]==SPELL_EFFECT_CREATE_RANDOM_ITEM || + // different random cards from Inscription (121==Virtuoso Inking Set category) + spellInfo->Effect[0]==SPELL_EFFECT_CREATE_ITEM_2 && spellInfo->TotemCategory[0] == 121; } bool IsHigherHankOfSpell(uint32 spellId_1,uint32 spellId_2); @@ -230,6 +243,8 @@ bool IsSingleFromSpellSpecificPerTarget(uint32 spellSpec1, uint32 spellSpec2); bool IsPassiveSpell(uint32 spellId); bool IsAutocastableSpell(uint32 spellId); +uint32 CalculatePowerCost(SpellEntry const * spellInfo, Unit const * caster, SpellSchoolMask schoolMask); + inline bool IsPassiveSpellStackableWithRanks(SpellEntry const* spellProto) { if(!IsPassiveSpell(spellProto->Id)) @@ -249,15 +264,14 @@ inline bool IsNonCombatSpell(SpellEntry const *spellInfo) return (spellInfo->Attributes & SPELL_ATTR_CANT_USED_IN_COMBAT) != 0; } -bool IsPositiveSpell(uint32 spellId, bool deep = false); -bool IsPositiveEffect(uint32 spellId, uint32 effIndex, bool deep = false); +bool IsPositiveSpell(uint32 spellId); +bool IsPositiveEffect(uint32 spellId, uint32 effIndex); bool IsPositiveTarget(uint32 targetA, uint32 targetB); bool IsDispelableBySpell(SpellEntry const * dispelSpell, uint32 spellId, bool def = false); bool IsSingleTargetSpell(SpellEntry const *spellInfo); bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellInfo2); -bool IsAuraAddedBySpell(uint32 auraType, uint32 spellId); extern bool IsAreaEffectTarget[TOTAL_SPELL_TARGETS]; @@ -317,7 +331,7 @@ inline bool IsAutoRepeatRangedSpell(SpellEntry const* spellInfo) inline bool IsRangedWeaponSpell(SpellEntry const* spellInfo) { //spell->DmgClass == SPELL_DAMAGE_CLASS_RANGED should be checked outside - return (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER) // for 53352, cannot find better way + return (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && !(spellInfo->SpellFamilyFlags[1] & 0x10000000)) // for 53352, cannot find better way || (spellInfo->EquippedItemSubClassMask & ITEM_SUBCLASS_MASK_WEAPON_RANGED); } @@ -381,55 +395,53 @@ inline uint32 GetDispellMask(DispelType dispel) DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered); bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group); DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group); - -// Spell affects related declarations (accessed using SpellMgr functions) -typedef UNORDERED_MAP<uint32, flag96> SpellAffectMap; +int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellEntry const* spellproto); // Spell proc event related declarations (accessed using SpellMgr functions) enum ProcFlags { - PROC_FLAG_NONE = 0x00000000, + PROC_FLAG_NONE = 0x00000000, - PROC_FLAG_KILLED = 0x00000001, // 00 Killed by agressor - PROC_FLAG_KILL = 0x00000002, // 01 Kill target (in most cases need XP/Honor reward) + PROC_FLAG_KILLED = 0x00000001, // 00 Killed by agressor + PROC_FLAG_KILL = 0x00000002, // 01 Kill target (in most cases need XP/Honor reward) - PROC_FLAG_SUCCESSFUL_MILEE_HIT = 0x00000004, // 02 Successful melee auto attack - PROC_FLAG_TAKEN_MELEE_HIT = 0x00000008, // 03 Taken damage from melee auto attack hit + PROC_FLAG_SUCCESSFUL_MELEE_HIT = 0x00000004, // 02 Successful melee auto attack + PROC_FLAG_TAKEN_MELEE_HIT = 0x00000008, // 03 Taken damage from melee auto attack hit - PROC_FLAG_SUCCESSFUL_MELEE_SPELL_HIT = 0x00000010, // 04 Successful attack by Spell that use melee weapon - PROC_FLAG_TAKEN_MELEE_SPELL_HIT = 0x00000020, // 05 Taken damage by Spell that use melee weapon + PROC_FLAG_SUCCESSFUL_MELEE_SPELL_HIT = 0x00000010, // 04 Successful attack by Spell that use melee weapon + PROC_FLAG_TAKEN_MELEE_SPELL_HIT = 0x00000020, // 05 Taken damage by Spell that use melee weapon - PROC_FLAG_SUCCESSFUL_RANGED_HIT = 0x00000040, // 06 Successful Ranged auto attack - PROC_FLAG_TAKEN_RANGED_HIT = 0x00000080, // 07 Taken damage from ranged auto attack + PROC_FLAG_SUCCESSFUL_RANGED_HIT = 0x00000040, // 06 Successful Ranged auto attack + PROC_FLAG_TAKEN_RANGED_HIT = 0x00000080, // 07 Taken damage from ranged auto attack - PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT = 0x00000100, // 08 Successful Ranged attack by Spell that use ranged weapon - PROC_FLAG_TAKEN_RANGED_SPELL_HIT = 0x00000200, // 09 Taken damage by Spell that use ranged weapon + PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT = 0x00000100, // 08 Successful Ranged attack by Spell that use ranged weapon + PROC_FLAG_TAKEN_RANGED_SPELL_HIT = 0x00000200, // 09 Taken damage by Spell that use ranged weapon - PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL = 0x00000400, // 10 Successful Positive spell hit - PROC_FLAG_TAKEN_POSITIVE_SPELL = 0x00000800, // 11 Taken Positive spell hit + PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL_HIT = 0x00000400, // 10 Successful Positive spell hit + PROC_FLAG_TAKEN_POSITIVE_SPELL = 0x00000800, // 11 Taken Positive spell hit - PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT = 0x00001000, // 12 Successful Negative spell hit - PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT = 0x00002000, // 13 Taken Negative spell hit + PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT = 0x00001000, // 12 Successful Negative spell hit + PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT = 0x00002000, // 13 Taken Negative spell hit - PROC_FLAG_SUCCESSFUL_HEALING_SPELL = 0x00004000, // 14 Successful Direct Heal spell hit - PROC_FLAG_TAKEN_HEALING_SPELL = 0x00008000, // 15 Taken Direct Heal spell hit + PROC_FLAG_SUCCESSFUL_POSITIVE_MAGIC_SPELL = 0x00004000, // 14 Successful Positive Magic spell hit + PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL = 0x00008000, // 15 Taken Positive Magic spell hit - PROC_FLAG_SUCCESSFUL_DAMAGING_SPELL_HIT = 0x00010000, // 16 Successful Direct Damage spell hit - PROC_FLAG_TAKEN_DAMAGING_SPELL_HIT = 0x00020000, // 17 Taken Direct Damage spell hit + PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL = 0x00010000, // 16 Successful Negative Magic spell hit + PROC_FLAG_TAKEN_NEGATIVE_MAGIC_SPELL = 0x00020000, // 17 Taken Negative Magic spell hit - PROC_FLAG_ON_DO_PERIODIC = 0x00040000, // 18 Successful do periodic (damage / healing, determined from 14,16 flags) - PROC_FLAG_ON_TAKE_PERIODIC = 0x00080000, // 19 Taken spell periodic (damage / healing, determined from 15,17 flags) + PROC_FLAG_ON_DO_PERIODIC = 0x00040000, // 18 Successful do periodic (damage / healing, determined from 14,16 flags) + PROC_FLAG_ON_TAKE_PERIODIC = 0x00080000, // 19 Taken spell periodic (damage / healing, determined from 15,17 flags) - PROC_FLAG_TAKEN_ANY_DAMAGE = 0x00100000, // 20 Taken any damage - PROC_FLAG_ON_TRAP_ACTIVATION = 0x00200000, // 21 On trap activation + PROC_FLAG_TAKEN_ANY_DAMAGE = 0x00100000, // 20 Taken any damage + PROC_FLAG_ON_TRAP_ACTIVATION = 0x00200000, // 21 On trap activation (possibly needs name change to ON_GAMEOBJECT_CAST or USE) - PROC_FLAG_TAKEN_OFFHAND_HIT = 0x00400000, // 22 Taken off-hand melee attacks(not used) - PROC_FLAG_SUCCESSFUL_OFFHAND_HIT = 0x00800000, // 23 Successful off-hand melee attacks ( this is probably wrong ) + PROC_FLAG_TAKEN_OFFHAND_HIT = 0x00400000, // 22 Taken off-hand melee attacks ( this is probably wrong ) + PROC_FLAG_SUCCESSFUL_OFFHAND_HIT = 0x00800000, // 23 Successful off-hand melee attacks ( this is probably wrong ) - PROC_FLAG_DEATH = 0x01000000 // 24 Died in any way + PROC_FLAG_DEATH = 0x01000000 // 24 Died in any way }; -#define MELEE_BASED_TRIGGER_MASK (PROC_FLAG_SUCCESSFUL_MILEE_HIT | \ +#define MELEE_BASED_TRIGGER_MASK (PROC_FLAG_SUCCESSFUL_MELEE_HIT | \ PROC_FLAG_TAKEN_MELEE_HIT | \ PROC_FLAG_SUCCESSFUL_MELEE_SPELL_HIT | \ PROC_FLAG_TAKEN_MELEE_SPELL_HIT | \ @@ -454,15 +466,19 @@ enum ProcFlagsEx PROC_EX_ABSORB = 0x0000400, PROC_EX_REFLECT = 0x0000800, PROC_EX_INTERRUPT = 0x0001000, // Melee hit result can be Interrupt (not used) - PROC_EX_AURA_REMOVE_DESTROY = 0x0002000, // aura absorb destroy or dispel - PROC_EX_AURA_REMOVE_EXPIRE = 0x0004000, // aura remove by default and by cancel - PROC_EX_EX_TRIGGER_ALWAYS = 0x0010000, // If set trigger always ( no matter another flags) used for drop charges - PROC_EX_EX_ONE_TIME_TRIGGER = 0x0020000, // If set trigger always but only one time (not used) + PROC_EX_AURA_REMOVE_DESTROY = 0x0002000, // Aura absorb destroy or dispel + PROC_EX_AURA_REMOVE_EXPIRE = 0x0004000, // Aura remove by default and by cancel + PROC_EX_NOT_ACTIVE_SPELL = 0x0008000, // Spell mustn't do damage/heal to proc + PROC_EX_EX_TRIGGER_ALWAYS = 0x0010000, // If set trigger always no matter of hit result + PROC_EX_EX_ONE_TIME_TRIGGER = 0x0020000, // If set trigger always but only one time (not implemented yet) + PROC_EX_ONLY_ACTIVE_SPELL = 0x0040000, // Spell has to do damage/heal to proc + + // Flags for internal use - do not use these in db! PROC_EX_INTERNAL_CANT_PROC = 0x0800000, - PROC_EX_INTERNAL_DOT = 0x1000000, // Only for internal use - PROC_EX_INTERNAL_HOT = 0x2000000, // Only for internal use - PROC_EX_INTERNAL_TRIGGERED = 0x4000000, // Only for internal use - PROC_EX_INTERNAL_REQ_FAMILY = 0x8000000 // Only for internal use + PROC_EX_INTERNAL_DOT = 0x1000000, + PROC_EX_INTERNAL_HOT = 0x2000000, + PROC_EX_INTERNAL_TRIGGERED = 0x4000000, + PROC_EX_INTERNAL_REQ_FAMILY = 0x8000000 }; #define AURA_REMOVE_PROC_EX_MASK \ (PROC_EX_AURA_REMOVE_DESTROY | PROC_EX_AURA_REMOVE_EXPIRE) @@ -490,6 +506,7 @@ struct SpellBonusEntry float direct_damage; float dot_damage; float ap_bonus; + float ap_dot_bonus; }; typedef UNORDERED_MAP<uint32, SpellProcEventEntry> SpellProcEventMap; @@ -511,16 +528,18 @@ typedef UNORDERED_MAP<uint32, SpellBonusEntry> SpellBonusMap; #define ELIXIR_SHATTRATH_MASK 0x8 typedef std::map<uint32, uint8> SpellElixirMap; +typedef std::map<uint32, uint16> SpellThreatMap; // Spell script target related declarations (accessed using SpellMgr functions) enum SpellScriptTargetType { SPELL_TARGET_TYPE_GAMEOBJECT = 0, SPELL_TARGET_TYPE_CREATURE = 1, - SPELL_TARGET_TYPE_DEAD = 2 + SPELL_TARGET_TYPE_DEAD = 2, + SPELL_TARGET_TYPE_CONTROLLED = 3, }; -#define MAX_SPELL_TARGET_TYPE 3 +#define MAX_SPELL_TARGET_TYPE 4 struct SpellTargetEntry { @@ -552,28 +571,24 @@ class PetAura auras.clear(); } - PetAura(uint16 petEntry, uint16 aura, bool _removeOnChangePet, int _damage) : + PetAura(uint32 petEntry, uint32 aura, bool _removeOnChangePet, int _damage) : removeOnChangePet(_removeOnChangePet), damage(_damage) { auras[petEntry] = aura; } - uint16 GetAura(uint16 petEntry) const + uint32 GetAura(uint32 petEntry) const { - std::map<uint16, uint16>::const_iterator itr = auras.find(petEntry); + std::map<uint32, uint32>::const_iterator itr = auras.find(petEntry); if(itr != auras.end()) return itr->second; - else - { - std::map<uint16, uint16>::const_iterator itr2 = auras.find(0); - if(itr2 != auras.end()) - return itr2->second; - else - return 0; - } + std::map<uint32, uint32>::const_iterator itr2 = auras.find(0); + if(itr2 != auras.end()) + return itr2->second; + return 0; } - void AddAura(uint16 petEntry, uint16 aura) + void AddAura(uint32 petEntry, uint32 aura) { auras[petEntry] = aura; } @@ -589,11 +604,11 @@ class PetAura } private: - std::map<uint16, uint16> auras; + std::map<uint32, uint32> auras; bool removeOnChangePet; int32 damage; }; -typedef std::map<uint16, PetAura> SpellPetAuraMap; +typedef std::map<uint32, PetAura> SpellPetAuraMap; struct SpellArea { @@ -688,7 +703,6 @@ inline bool IsProfessionSkill(uint32 skill) return IsPrimaryProfessionSkill(skill) || skill == SKILL_FISHING || skill == SKILL_COOKING || skill == SKILL_FIRST_AID; } -//#define SPELL_ATTR_CU_PLAYERS_ONLY 0x00000001 #define SPELL_ATTR_CU_CONE_BACK 0x00000002 #define SPELL_ATTR_CU_CONE_LINE 0x00000004 #define SPELL_ATTR_CU_SHARE_DAMAGE 0x00000008 @@ -704,7 +718,10 @@ inline bool IsProfessionSkill(uint32 skill) #define SPELL_ATTR_CU_LINK_REMOVE 0x00002000 #define SPELL_ATTR_CU_MOVEMENT_IMPAIR 0x00004000 #define SPELL_ATTR_CU_EXCLUDE_SELF 0x00008000 - +#define SPELL_ATTR_CU_NEGATIVE_EFF0 0x00010000 +#define SPELL_ATTR_CU_NEGATIVE_EFF1 0x00020000 +#define SPELL_ATTR_CU_NEGATIVE_EFF2 0x00040000 +#define SPELL_ATTR_CU_NEGATIVE 0x00070000 typedef std::vector<uint32> SpellCustomAttribute; typedef std::vector<bool> EnchantCustomAttribute; @@ -725,14 +742,6 @@ class SpellMgr // Accessors (const or static functions) public: - // Spell affects - flag96 const*GetSpellAffect(uint16 spellId, uint8 effectId) const - { - SpellAffectMap::const_iterator itr = mSpellAffectMap.find((spellId<<8) + effectId); - if( itr != mSpellAffectMap.end( ) ) - return &itr->second; - return 0; - } bool IsAffectedByMod(SpellEntry const *spellInfo, SpellModifier *mod) const; @@ -760,6 +769,15 @@ class SpellMgr return SPELL_NORMAL; } + uint16 GetSpellThreat(uint32 spellid) const + { + SpellThreatMap::const_iterator itr = mSpellThreatMap.find(spellid); + if(itr==mSpellThreatMap.end()) + return 0; + + return itr->second; + } + // Spell proc events SpellProcEventEntry const* GetSpellProcEvent(uint32 spellId) const { @@ -769,7 +787,7 @@ class SpellMgr return NULL; } - bool IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra); + bool IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra, bool active); SpellEnchantProcEntry const* GetSpellEnchantProcEvent(uint32 enchId) const { @@ -850,8 +868,6 @@ class SpellMgr SpellsRequiringSpellMap const& GetSpellsRequiringSpell() const { return mSpellsReqSpell; } - // Note: not use rank for compare to spell ranks: spell chains isn't linear order - // Use IsHighRankOfSpell instead uint8 GetSpellRank(uint32 spell_id) const { if(SpellChainNode const* node = GetSpellChainNode(spell_id)) @@ -965,9 +981,9 @@ class SpellMgr return mSkillLineAbilityMap.upper_bound(spell_id); } - PetAura const* GetPetAura(uint16 spell_id) + PetAura const* GetPetAura(uint32 spell_id, uint8 eff) { - SpellPetAuraMap::const_iterator itr = mSpellPetAuraMap.find(spell_id); + SpellPetAuraMap::const_iterator itr = mSpellPetAuraMap.find((spell_id<<8) + eff); if(itr != mSpellPetAuraMap.end()) return &itr->second; else @@ -1039,6 +1055,30 @@ class SpellMgr return SpellAreaForAreaMapBounds(mSpellAreaForAreaMap.lower_bound(area_id),mSpellAreaForAreaMap.upper_bound(area_id)); } + bool IsSrcTargetSpell(SpellEntry const *spellInfo) const; + + inline bool IsCasterSourceTarget(uint32 target) + { + switch (SpellTargetType[target]) + { + case TARGET_TYPE_UNIT_TARGET: + case TARGET_TYPE_DEST_TARGET: + return false; + default: + break; + } + return true; + } + + inline bool IsSpellWithCasterSourceTargetsOnly(SpellEntry const* spellInfo) + { + for(int i = 0; i < 3; ++i) + if(uint32 target = spellInfo->EffectImplicitTargetA[i]) + if(!IsCasterSourceTarget(target)) + return false; + return true; + } + // Modifiers public: static SpellMgr& Instance(); @@ -1049,7 +1089,6 @@ class SpellMgr void LoadSpellLearnSkills(); void LoadSpellLearnSpells(); void LoadSpellScriptTarget(); - void LoadSpellAffects(); void LoadSpellElixirs(); void LoadSpellProcEvents(); void LoadSpellBonusess(); @@ -1068,6 +1107,9 @@ class SpellMgr bool CheckDB() const; private: + bool _isPositiveSpell(uint32 spellId, bool deep) const; + bool _isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) const; + SpellScriptTarget mSpellScriptTarget; SpellChainMap mSpellChains; SpellsRequiringSpellMap mSpellsReqSpell; @@ -1075,8 +1117,8 @@ class SpellMgr SpellLearnSkillMap mSpellLearnSkills; SpellLearnSpellMap mSpellLearnSpells; SpellTargetPositionMap mSpellTargetPositions; - SpellAffectMap mSpellAffectMap; SpellElixirMap mSpellElixirs; + SpellThreatMap mSpellThreatMap; SpellProcEventMap mSpellProcEventMap; SpellBonusMap mSpellBonusMap; SkillLineAbilityMap mSkillLineAbilityMap; diff --git a/src/game/StatSystem.cpp b/src/game/StatSystem.cpp index bb7b1273ac0..36d88e065a6 100644 --- a/src/game/StatSystem.cpp +++ b/src/game/StatSystem.cpp @@ -881,10 +881,9 @@ void Creature::UpdateDamagePhysical(WeaponAttackType attType) float weapon_mindamage = GetWeaponDamageRange(attType, MINDAMAGE); float weapon_maxdamage = GetWeaponDamageRange(attType, MAXDAMAGE); - //float base_value = GetModifierValue(unitMod, BASE_VALUE) - // + (weapon_mindamage + weapon_maxdamage) - // * GetTotalAttackPowerValue(attType) / (getLevel() * 30); - float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType); + /* difference in AP between current attack power and base value from DB */ + float att_pwr_change = GetTotalAttackPowerValue(attType) - GetCreatureInfo()->attackpower; + float base_value = GetModifierValue(unitMod, BASE_VALUE) + (att_pwr_change * GetAPMultiplier(attType, false) / 14.0f); float base_pct = GetModifierValue(unitMod, BASE_PCT); float total_value = GetModifierValue(unitMod, TOTAL_VALUE); float total_pct = GetModifierValue(unitMod, TOTAL_PCT); diff --git a/src/game/TargetedMovementGenerator.cpp b/src/game/TargetedMovementGenerator.cpp index c40568318d4..c1c601921b3 100644 --- a/src/game/TargetedMovementGenerator.cpp +++ b/src/game/TargetedMovementGenerator.cpp @@ -53,7 +53,7 @@ template<class T> bool TargetedMovementGenerator<T>::_setTargetLocation(T &owner) { - if( !i_target.isValid() || !&owner ) + if (!i_target.isValid() || !i_target->IsInWorld()) return false; if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED) ) @@ -170,7 +170,7 @@ template<class T> bool TargetedMovementGenerator<T>::Update(T &owner, const uint32 & time_diff) { - if(!i_target.isValid()) + if (!i_target.isValid() || !i_target->IsInWorld()) return false; if( !&owner || !owner.isAlive()) diff --git a/src/game/TaxiHandler.cpp b/src/game/TaxiHandler.cpp index 0928b8ba96a..5655bfd57bb 100644 --- a/src/game/TaxiHandler.cpp +++ b/src/game/TaxiHandler.cpp @@ -253,13 +253,11 @@ void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& /*recv_data*/) SendDoFlight( mountDisplayId, path, 1 ); // skip start fly node else GetPlayer()->m_taxi.ClearTaxiDestinations(); // clear problematic path and next + return; } - GetPlayer()->m_taxi.ClearTaxiDestinations(); // not destinations, clear source node - GetPlayer()->Unmount(); - GetPlayer()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); + GetPlayer()->CleanupAfterTaxiFlight(); GetPlayer()->SetFallInformation(0, GetPlayer()->GetPositionZ()); - GetPlayer()->getHostilRefManager().setOnlineOfflineState(true); if(GetPlayer()->pvpInfo.inHostileArea) GetPlayer()->CastSpell(GetPlayer(), 2479, true); } diff --git a/src/game/TemporarySummon.cpp b/src/game/TemporarySummon.cpp index 724d68c568b..434750c8744 100644 --- a/src/game/TemporarySummon.cpp +++ b/src/game/TemporarySummon.cpp @@ -224,13 +224,18 @@ void TempSummon::SetTempSummonType(TempSummonType type) void TempSummon::UnSummon() { - assert(!isPet()); + //assert(!isPet()); + if(isPet()) + { + ((Pet*)this)->Remove(PET_SAVE_NOT_IN_SLOT); + assert(!IsInWorld()); + return; + } Unit* owner = GetSummoner(); if(owner && owner->GetTypeId() == TYPEID_UNIT && ((Creature*)owner)->IsAIEnabled) ((Creature*)owner)->AI()->SummonedCreatureDespawn(this); - CleanupsBeforeDelete(); AddObjectToRemoveList(); } @@ -266,6 +271,7 @@ Minion::Minion(SummonPropertiesEntry const *properties, Unit *owner) : TempSummo { assert(m_owner); m_summonMask |= SUMMON_MASK_MINION; + m_followAngle = PET_FOLLOW_ANGLE; } void Minion::InitStats(uint32 duration) @@ -287,7 +293,7 @@ void Minion::InitSummon() if(m_owner->GetTypeId() == TYPEID_PLAYER && m_owner->GetMinionGUID() == GetGUID() && !m_owner->GetCharmGUID()) - ((Player*)m_owner)->CharmSpellInitialize(); + ((Player*)m_owner)->CharmSpellInitialize(); } void Minion::RemoveFromWorld() @@ -303,7 +309,11 @@ Guardian::Guardian(SummonPropertiesEntry const *properties, Unit *owner) : Minio , m_bonusdamage(0) { m_summonMask |= SUMMON_MASK_GUARDIAN; - InitCharmInfo(); + if (properties && properties->Type == SUMMON_TYPE_PET) + { + m_summonMask |= SUMMON_MASK_CONTROLABLE_GUARDIAN; + InitCharmInfo(); + } } void Guardian::InitStats(uint32 duration) @@ -312,7 +322,7 @@ void Guardian::InitStats(uint32 duration) InitStatsForLevel(m_owner->getLevel()); - if(m_owner->GetTypeId() == TYPEID_PLAYER) + if(m_owner->GetTypeId() == TYPEID_PLAYER && HasSummonMask(SUMMON_MASK_CONTROLABLE_GUARDIAN)) m_charmInfo->InitCharmCreateSpells(); SetReactState(REACT_AGGRESSIVE); @@ -335,7 +345,8 @@ void Puppet::InitStats(uint32 duration) void Puppet::InitSummon() { Minion::InitSummon(); - SetCharmedBy(m_owner, CHARM_TYPE_POSSESS); + if (!SetCharmedBy(m_owner, CHARM_TYPE_POSSESS)) + assert(false); } void Puppet::Update(uint32 time) diff --git a/src/game/TemporarySummon.h b/src/game/TemporarySummon.h index 47961d714ec..aa4650a02c9 100644 --- a/src/game/TemporarySummon.h +++ b/src/game/TemporarySummon.h @@ -54,9 +54,12 @@ class Minion : public TempSummon void InitSummon(); void RemoveFromWorld(); Unit *GetOwner() { return m_owner; } + float GetFollowAngle() const { return m_followAngle; } + void SetFollowAngle(float angle) { m_followAngle = angle; } bool IsPetGhoul() const {return GetEntry() == 26125;} // Ghoul may be guardian or pet protected: Unit * const m_owner; + float m_followAngle; }; class Guardian : public Minion diff --git a/src/game/ThreatManager.cpp b/src/game/ThreatManager.cpp index a0459aa81dc..60c46c2307d 100644 --- a/src/game/ThreatManager.cpp +++ b/src/game/ThreatManager.cpp @@ -34,11 +34,9 @@ // The pHatingUnit is not used yet float ThreatCalcHelper::calcThreat(Unit* pHatedUnit, Unit* pHatingUnit, float pThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell) { - if(pThreatSpell) - { - if( Player* modOwner = pHatingUnit->GetSpellModOwner() ) + if (pThreatSpell) + if (Player* modOwner = pHatedUnit->GetSpellModOwner()) modOwner->ApplySpellMod(pThreatSpell->Id, SPELLMOD_THREAT, pThreat); - } float threat = pHatedUnit->ApplyTotalThreatModifier(pThreat, schoolMask); return threat; @@ -346,7 +344,7 @@ HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilRe //=================== ThreatManager ========================== //============================================================ -ThreatManager::ThreatManager(Unit* owner) : iCurrentVictim(NULL), iOwner(owner) +ThreatManager::ThreatManager(Unit* owner) : iCurrentVictim(NULL), iOwner(owner), iUpdateTimer(THREAT_UPDATE_INTERVAL) { } @@ -357,6 +355,7 @@ void ThreatManager::clearReferences() iThreatContainer.clearReferences(); iThreatOfflineContainer.clearReferences(); iCurrentVictim = NULL; + iUpdateTimer = THREAT_UPDATE_INTERVAL; } //============================================================ @@ -470,6 +469,10 @@ void ThreatManager::tauntFadeOut(Unit *pTaunter) void ThreatManager::setCurrentVictim(HostilReference* pHostilReference) { + if (pHostilReference && pHostilReference != iCurrentVictim) + { + iOwner->SendChangeCurrentVictimOpcode(pHostilReference); + } iCurrentVictim = pHostilReference; } @@ -515,6 +518,7 @@ void ThreatManager::processThreatEvent(ThreatRefStatusChangeEvent* threatRefStat setCurrentVictim(NULL); setDirty(true); } + iOwner->SendRemoveFromThreatListOpcode(hostilReference); if(hostilReference->isOnline()) iThreatContainer.remove(hostilReference); else @@ -523,3 +527,16 @@ void ThreatManager::processThreatEvent(ThreatRefStatusChangeEvent* threatRefStat } } +bool ThreatManager::isNeedUpdateToClient(uint32 time) +{ + if (isThreatListEmpty()) + return false; + if (time >= iUpdateTimer) + { + iUpdateTimer = THREAT_UPDATE_INTERVAL; + return true; + } + iUpdateTimer -= time; + return false; +} + diff --git a/src/game/ThreatManager.h b/src/game/ThreatManager.h index aa84cb7d52c..1dba6277cfc 100644 --- a/src/game/ThreatManager.h +++ b/src/game/ThreatManager.h @@ -35,6 +35,8 @@ class Creature; class ThreatManager; struct SpellEntry; +#define THREAT_UPDATE_INTERVAL 1 * IN_MILISECONDS // Server should send threat update to client periodically each second + //============================================================== // Class to calculate the real threat based @@ -187,6 +189,8 @@ class TRINITY_DLL_SPEC ThreatManager void processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent); + bool isNeedUpdateToClient(uint32 time); + HostilReference* getCurrentVictim() { return iCurrentVictim; } Unit* getOwner() { return iOwner; } @@ -211,6 +215,7 @@ class TRINITY_DLL_SPEC ThreatManager HostilReference* iCurrentVictim; Unit* iOwner; + uint32 iUpdateTimer; ThreatContainer iThreatContainer; ThreatContainer iThreatOfflineContainer; }; diff --git a/src/game/Totem.cpp b/src/game/Totem.cpp index 67fa0281d91..d35ba9e30b9 100644 --- a/src/game/Totem.cpp +++ b/src/game/Totem.cpp @@ -57,9 +57,9 @@ void Totem::InitStats(uint32 duration) Minion::InitStats(duration); CreatureInfo const *cinfo = GetCreatureInfo(); - if (m_owner->GetTypeId()==TYPEID_PLAYER && cinfo) + if(m_owner->GetTypeId() == TYPEID_PLAYER && cinfo) { - uint32 display_id = objmgr.ChooseDisplayId(((Player*)m_owner)->GetTeam(),cinfo); + uint32 display_id = objmgr.ChooseDisplayId(((Player*)m_owner)->GetTeam(), cinfo); CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id); if (minfo) display_id = minfo->modelid; @@ -91,6 +91,9 @@ void Totem::InitSummon() if(m_type == TOTEM_PASSIVE) CastSpell(this, GetSpell(), true); + // Some totems can have both instant effect and passive spell + if (GetSpell(1)) + CastSpell(this, GetSpell(1), true); } void Totem::UnSummon() @@ -129,13 +132,12 @@ void Totem::UnSummon() } } - CleanupsBeforeDelete(); AddObjectToRemoveList(); } bool Totem::IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const { - // TODO: possibly all negative auras immuned? + // TODO: possibly all negative auras immune? switch(spellInfo->EffectApplyAuraName[index]) { case SPELL_AURA_PERIODIC_DAMAGE: @@ -148,4 +150,3 @@ bool Totem::IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) co } return Creature::IsImmunedToSpellEffect(spellInfo, index); } - diff --git a/src/game/Totem.h b/src/game/Totem.h index 134ca6d6d3f..9975cd4deae 100644 --- a/src/game/Totem.h +++ b/src/game/Totem.h @@ -40,7 +40,7 @@ class Totem : public Minion void InitStats(uint32 duration); void InitSummon(); void UnSummon(); - uint32 GetSpell() const { return m_spells[0]; } + uint32 GetSpell(uint8 slot=0) const { return m_spells[slot]; } uint32 GetTotemDuration() const { return m_duration; } TotemType GetTotemType() const { return m_type; } diff --git a/src/game/Transports.cpp b/src/game/Transports.cpp index 11089f053d1..25248b7b9d2 100644 --- a/src/game/Transports.cpp +++ b/src/game/Transports.cpp @@ -106,8 +106,9 @@ void MapManager::LoadTransports() m_TransportsByMap[*i].insert(t); //If we someday decide to use the grid to track transports, here: - //MapManager::Instance().LoadGrid(mapid,x,y,true); - //MapManager::Instance().GetMap(t->GetMapId())->Add<GameObject>((GameObject *)t); + t->SetMap(MapManager::Instance().CreateMap(mapid, t, 0)); + + //t->GetMap()->Add<GameObject>((GameObject *)t); ++count; } while(result->NextRow()); delete result; @@ -142,8 +143,6 @@ Transport::Transport() : GameObject() bool Transport::Create(uint32 guidlow, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags) { Relocate(x,y,z,ang); - - SetMapId(mapid); // instance id and phaseMask isn't set to values different from std. if(!IsPositionValid()) @@ -439,7 +438,6 @@ Transport::WayPointMap::const_iterator Transport::GetNextWayPoint() void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z) { Map const* oldMap = GetMap(); - SetMapId(newMapid); Relocate(x, y, z); for(PlayerSet::const_iterator itr = m_passengers.begin(); itr != m_passengers.end();) @@ -458,7 +456,15 @@ void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z) //plr->GetSession()->SendPacket(&data); } - Map const* newMap = GetMap(); + //we need to create and save new Map object with 'newMapid' because if not done -> lead to invalid Map object reference... + //player far teleport would try to create same instance, but we need it NOW for transport... + //correct me if I'm wrong O.o + //yes, you're right + + ResetMap(); + Map * newMap = MapManager::Instance().CreateMap(newMapid, this, 0); + SetMap(newMap); + assert (GetMap()); if(oldMap != newMap) { @@ -485,7 +491,7 @@ void Transport::CheckForEvent(uint32 entry, uint32 wp_id) { uint32 key = entry*100+wp_id; if(objmgr.TransportEventMap.find(key) != objmgr.TransportEventMap.end()) - sWorld.ScriptsStart(sEventScripts, objmgr.TransportEventMap[key], this, NULL); + GetMap()->ScriptsStart(sEventScripts, objmgr.TransportEventMap[key], this, NULL); } void Transport::Update(uint32 /*p_time*/) diff --git a/src/game/Transports.h b/src/game/Transports.h index d72fa3b2124..e05a6006971 100644 --- a/src/game/Transports.h +++ b/src/game/Transports.h @@ -56,24 +56,11 @@ class TransportPath std::vector<PathNode> i_nodes; }; -class Transport : protected GameObject +class Transport : public GameObject { public: explicit Transport(); - // prevent using Transports as normal GO, but allow call some inherited functions - using GameObject::IsTransport; - using GameObject::GetEntry; - using GameObject::GetGUID; - using GameObject::GetGUIDLow; - using GameObject::GetMapId; - using GameObject::GetPositionX; - using GameObject::GetPositionY; - using GameObject::GetPositionZ; - using GameObject::BuildCreateUpdateBlockForPlayer; - using GameObject::BuildOutOfRangeUpdateBlock; - using GameObject::GetPackGUID; - bool Create(uint32 guidlow, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags); bool GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids); void Update(uint32 p_time); diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index e107eb90f55..74eda6482d0 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -60,7 +60,7 @@ float baseMoveSpeed[MAX_MOVE_TYPE] = { 2.5f, // MOVE_WALK 7.0f, // MOVE_RUN - 1.25f, // MOVE_RUN_BACK + 3.0f, // MOVE_RUN_BACK 4.722222f, // MOVE_SWIM 4.5f, // MOVE_SWIM_BACK 3.141594f, // MOVE_TURN_RATE @@ -211,13 +211,20 @@ void Unit::Update( uint32 p_time ) m_Events.Update( p_time ); _UpdateSpells( p_time ); - // update combat timer only for players and pets - if (isInCombat() && IsControlledByPlayer()) + // If this is set during update SetCantProc(false) call is missing somewhere in the code + // Having this would prevent spells from being proced, so let's crash + assert(!m_procDeep); + + if (CanHaveThreatList() && getThreatManager().isNeedUpdateToClient(p_time)) + SendThreatListUpdate(); + + // update combat timer only for players and pets (only pets with PetAI) + if (isInCombat() && (GetTypeId() == TYPEID_PLAYER || (((Creature *)this)->isPet() && IsControlledByPlayer()))) { // Check UNIT_STAT_MELEE_ATTACKING or UNIT_STAT_CHASE (without UNIT_STAT_FOLLOW in this case) so pets can reach far away // targets without stopping half way there and running off. // These flags are reset after target dies or another command is given. - if( m_HostilRefManager.isEmpty() ) + if( m_HostilRefManager.isEmpty()) { // m_CombatTimer set at aura start and it will be freeze until aura removing if ( m_CombatTimer <= p_time ) @@ -546,6 +553,22 @@ void Unit::RemoveAurasWithFamily(uint32 family, uint32 familyFlag1, uint32 famil } } +void Unit::RemoveAurasWithMechanic(uint32 mechanic_mask, uint32 except) +{ + for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();) + { + if (!except || iter->second->GetId() != except) + { + if(GetAllSpellMechanicMask(iter->second->GetSpellProto()) & mechanic_mask) + { + RemoveAura(iter, AURA_REMOVE_BY_ENEMY_SPELL); + continue; + } + } + ++iter; + } +} + void Unit::UpdateInterruptMask() { m_interruptMask = 0; @@ -570,29 +593,6 @@ bool Unit::HasAuraTypeWithFamilyFlags(AuraType auraType, uint32 familyName, uint return false; } -/* Called by DealDamage for auras that have a chance to be dispelled on damage taken. */ -void Unit::RemoveSpellbyDamageTaken(uint32 damage, uint32 spell) -{ - // The chance to dispel an aura depends on the damage taken with respect to the casters level. - uint32 max_dmg = getLevel() > 8 ? 25 * getLevel() - 150 : 50; - float chance = float(damage) / max_dmg * 100.0f; - - std::queue < std::pair < uint32, uint64 > > remove_list; - - for (AuraList::iterator iter = m_ccAuras.begin(); iter != m_ccAuras.end();++iter) - { - if((!spell || (*iter)->GetId() != spell) && roll_chance_f(chance)) - { - remove_list.push(std::make_pair((*iter)->GetId(), (*iter)->GetCasterGUID() ) ); - } - } - - for(;remove_list.size();remove_list.pop()) - { - RemoveAura(remove_list.front().first, remove_list.front().second, AURA_REMOVE_BY_ENEMY_SPELL); - } -} - void Unit::DealDamageMods(Unit *pVictim, uint32 &damage, uint32* absorb) { if (!pVictim->isAlive() || pVictim->hasUnitState(UNIT_STAT_UNATTACKABLE) || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode()) @@ -605,10 +605,10 @@ void Unit::DealDamageMods(Unit *pVictim, uint32 &damage, uint32* absorb) //You don't lose health from damage taken from another player while in a sanctuary //You still see it in the combat log though - if(pVictim != this && GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) + if(pVictim != this && IsControlledByPlayer() && pVictim->IsControlledByPlayer()) { const AreaTableEntry *area = GetAreaEntryByAreaID(pVictim->GetAreaId()); - if(area && area->flags & AREA_FLAG_SANCTUARY) //sanctuary + if(area && area->IsSanctuary()) //sanctuary { if(absorb) absorb += damage; @@ -639,14 +639,13 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa { // interrupting auras with AURA_INTERRUPT_FLAG_DAMAGE before checking !damage (absorbed damage breaks that type of auras) pVictim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TAKE_DAMAGE, spellProto ? spellProto->Id : 0); - pVictim->RemoveSpellbyDamageTaken(damage, spellProto ? spellProto->Id : 0); } if(!damage) { - // Rage from physical damage received . - if(cleanDamage && cleanDamage->damage && (damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL) && pVictim->GetTypeId() == TYPEID_PLAYER && (pVictim->getPowerType() == POWER_RAGE)) - ((Player*)pVictim)->RewardRage(cleanDamage->damage, 0, false); + // Rage from absorbed damage + if(cleanDamage && cleanDamage->absorbed_damage && pVictim->getPowerType() == POWER_RAGE) + pVictim->RewardRage(cleanDamage->absorbed_damage, 0, false); return 0; } @@ -666,7 +665,7 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa // some critters required for quests if(GetTypeId() == TYPEID_PLAYER) - ((Player*)this)->KilledMonster(pVictim->GetEntry(),pVictim->GetGUID()); + ((Player*)this)->KilledMonster(cInfo ,pVictim->GetGUID()); } else pVictim->ModifyHealth(- (int32)damage); @@ -691,9 +690,10 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa } // Rage from Damage made (only from direct weapon damage) - if( cleanDamage && damagetype==DIRECT_DAMAGE && this != pVictim && GetTypeId() == TYPEID_PLAYER && (getPowerType() == POWER_RAGE)) + if(cleanDamage && damagetype==DIRECT_DAMAGE && this != pVictim && getPowerType() == POWER_RAGE) { uint32 weaponSpeedHitFactor; + uint32 rage_damage = damage + cleanDamage->absorbed_damage; switch(cleanDamage->attackType) { @@ -704,7 +704,7 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa else weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 3.5f); - ((Player*)this)->RewardRage(damage, weaponSpeedHitFactor, true); + RewardRage(rage_damage, weaponSpeedHitFactor, true); break; } @@ -715,7 +715,7 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa else weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 1.75f); - ((Player*)this)->RewardRage(damage, weaponSpeedHitFactor, true); + RewardRage(rage_damage, weaponSpeedHitFactor, true); break; } @@ -865,7 +865,11 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa if(!cVictim->isPet()) { cVictim->DeleteThreatList(); - cVictim->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + // only lootable if it has loot or can drop gold + if(cVictim->GetCreatureInfo()->lootid || cVictim->GetCreatureInfo()->maxgold > 0) + cVictim->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + else + cVictim->lootForBody = true; // needed for skinning } // Call creature just died function if (cVictim->AI()) @@ -949,13 +953,6 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa } else // victim is a player { - // Rage from damage received - if(this != pVictim && pVictim->getPowerType() == POWER_RAGE) - { - uint32 rage_damage = damage + (cleanDamage ? cleanDamage->damage : 0); - ((Player*)pVictim)->RewardRage(rage_damage, 0, false); - } - // random durability for items (HIT TAKEN) if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_DAMAGE))) { @@ -964,6 +961,13 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa } } + // Rage from damage received + if(this != pVictim && pVictim->getPowerType() == POWER_RAGE) + { + uint32 rage_damage = damage + (cleanDamage ? cleanDamage->absorbed_damage : 0); + pVictim->RewardRage(rage_damage, 0, false); + } + if(GetTypeId()==TYPEID_PLAYER) { // random durability for items (HIT DONE) @@ -1200,7 +1204,7 @@ void Unit::CastCustomSpell(uint32 spellId, CustomSpellValues const &value, Unit* } // used for scripting -void Unit::CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem, AuraEffect* triggeredByAura, uint64 originalCaster) +void Unit::CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem, AuraEffect* triggeredByAura, uint64 originalCaster, Unit* OriginalVictim) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId ); @@ -1220,6 +1224,8 @@ void Unit::CastSpell(float x, float y, float z, uint32 spellId, bool triggered, SpellCastTargets targets; targets.setDestination(x, y, z); + if(OriginalVictim) + targets.setUnitTarget(OriginalVictim); spell->m_CastItem = castItem; spell->prepare(&targets, triggeredByAura); } @@ -1276,6 +1282,9 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage *damageInfo, int32 dama if (damage < 0) return; + if(spellInfo->AttributesEx4 & SPELL_ATTR_EX4_FIXED_DAMAGE) + return; + Unit *pVictim = damageInfo->target; if(!pVictim || !pVictim->isAlive()) return; @@ -1380,7 +1389,7 @@ void Unit::DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss) Unit *pVictim = damageInfo->target; - if(!this || !pVictim) + if(!pVictim) return; if (!pVictim->isAlive() || pVictim->hasUnitState(UNIT_STAT_UNATTACKABLE) || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode()) @@ -1395,15 +1404,15 @@ void Unit::DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss) //You don't lose health from damage taken from another player while in a sanctuary //You still see it in the combat log though - if(pVictim != this && GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) + if(pVictim != this && IsControlledByPlayer() && pVictim->IsControlledByPlayer()) { const AreaTableEntry *area = GetAreaEntryByAreaID(pVictim->GetAreaId()); - if(area && area->flags & AREA_FLAG_SANCTUARY) // sanctuary + if(area && area->IsSanctuary()) // sanctuary return; } // Call default DealDamage - CleanDamage cleanDamage(damageInfo->cleanDamage, BASE_ATTACK, MELEE_HIT_NORMAL); + CleanDamage cleanDamage(damageInfo->cleanDamage, damageInfo->absorb, BASE_ATTACK, MELEE_HIT_NORMAL); DealDamage(pVictim, damageInfo->damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SpellSchoolMask(damageInfo->schoolMask), spellProto, durabilityLoss); } @@ -1427,7 +1436,7 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da damageInfo->procEx = PROC_EX_NONE; damageInfo->hitOutCome = MELEE_HIT_EVADE; - if(!this || !pVictim) + if(!pVictim) return; if(!this->isAlive() || !pVictim->isAlive()) return; @@ -1436,12 +1445,12 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da switch (attackType) { case BASE_ATTACK: - damageInfo->procAttacker = PROC_FLAG_SUCCESSFUL_MILEE_HIT; + damageInfo->procAttacker = PROC_FLAG_SUCCESSFUL_MELEE_HIT; damageInfo->procVictim = PROC_FLAG_TAKEN_MELEE_HIT; damageInfo->HitInfo = HITINFO_NORMALSWING2; break; case OFF_ATTACK: - damageInfo->procAttacker = PROC_FLAG_SUCCESSFUL_MILEE_HIT | PROC_FLAG_SUCCESSFUL_OFFHAND_HIT; + damageInfo->procAttacker = PROC_FLAG_SUCCESSFUL_MELEE_HIT | PROC_FLAG_SUCCESSFUL_OFFHAND_HIT; damageInfo->procVictim = PROC_FLAG_TAKEN_MELEE_HIT;//|PROC_FLAG_TAKEN_OFFHAND_HIT // not used damageInfo->HitInfo = HITINFO_LEFTSWING; break; @@ -1623,7 +1632,7 @@ void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss) if (damageInfo==0) return; Unit *pVictim = damageInfo->target; - if(!this || !pVictim) + if(!pVictim) return; if (!pVictim->isAlive() || pVictim->hasUnitState(UNIT_STAT_UNATTACKABLE) || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode()) @@ -1631,10 +1640,10 @@ void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss) //You don't lose health from damage taken from another player while in a sanctuary //You still see it in the combat log though - if(pVictim != this && GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) + if(pVictim != this && IsControlledByPlayer() && pVictim->IsControlledByPlayer()) { const AreaTableEntry *area = GetAreaEntryByAreaID(pVictim->GetAreaId()); - if(area && area->flags & AREA_FLAG_SANCTUARY) // sanctuary + if(area && area->IsSanctuary()) // sanctuary return; } @@ -1681,7 +1690,7 @@ void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss) } // Call default DealDamage - CleanDamage cleanDamage(damageInfo->cleanDamage,damageInfo->attackType,damageInfo->hitOutCome); + CleanDamage cleanDamage(damageInfo->cleanDamage,damageInfo->absorb,damageInfo->attackType,damageInfo->hitOutCome); DealDamage(pVictim, damageInfo->damage, &cleanDamage, DIRECT_DAMAGE, SpellSchoolMask(damageInfo->damageSchoolMask), NULL, durabilityLoss); // If this is a creature and it attacks from behind it has a probability to daze it's victim @@ -1709,34 +1718,8 @@ void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss) CastSpell(pVictim, 1604, true); } - if(GetTypeId() == TYPEID_PLAYER && pVictim->isAlive()) - { - for(uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++) - { - // If usable, try to cast item spell - if (Item * item = ((Player*)this)->GetUseableItemByPos(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 (damageInfo->attackType) - { - 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; - } - ((Player*)this)->CastItemCombatSpell(item, damageInfo, proto); - } - } - } + if(GetTypeId() == TYPEID_PLAYER) + ((Player *)this)->CastItemCombatSpell(pVictim, damageInfo->attackType, damageInfo->procVictim, damageInfo->procEx); // Do effect if any damage done to target if (damageInfo->damage) @@ -1903,6 +1886,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe // Reflect damage spells (not cast any damage spell in aura lookup) uint32 reflectSpell = 0; int32 reflectDamage = 0; + AuraEffect* reflectTriggeredBy = NULL; // expected as not expired at reflect as in current cases uint32 healSpell = 0; int32 healAmount = 0; Unit * healCaster = NULL; @@ -1968,6 +1952,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe else reflectDamage = currentAbsorb / 2; reflectSpell = 33619; + reflectTriggeredBy = *i; break; } if (spellProto->Id == 39228 || // Argussian Compass @@ -2037,13 +2022,13 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe { case 5065: // Rank 1 case 5064: // Rank 2 - case 5063: // Rank 3 { if(RemainingDamage >= currentAbsorb) reflectDamage = (*k)->GetAmount() * currentAbsorb/100; else reflectDamage = (*k)->GetAmount() * RemainingDamage/100; reflectSpell = 33619; + reflectTriggeredBy = *i; } break; } } @@ -2064,9 +2049,20 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe } case SPELLFAMILY_DEATHKNIGHT: { - // Shadow of Death - if (spellProto->Id == 49157) + // Unbreakable Armor + if (spellProto->Id == 51271) { + Unit* caster = (*i)->GetCaster(); + if (!caster) + continue; + + uint32 absorbed = uint32( currentAbsorb * caster->GetArmor() * 0.01f ); + + // Glyph of Unbreakable Armor + if (AuraEffect *aurEff = caster->GetAuraEffect(58635, 0)) + absorbed += uint32( absorbed * aurEff->GetAmount() / 100 ); + + RemainingDamage -= absorbed; continue; } // Anti-Magic Shell (on self) @@ -2146,7 +2142,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe // Cast back reflect damage spell if (reflectSpell) - pVictim->CastCustomSpell(this, reflectSpell, &reflectDamage, NULL, NULL, true); + pVictim->CastCustomSpell(this, reflectSpell, &reflectDamage, NULL, NULL, true, NULL, reflectTriggeredBy); // absorb by mana cost AuraEffectList const& vManaShield = pVictim->GetAurasByType(SPELL_AURA_MANA_SHIELD); @@ -2217,7 +2213,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe SendSpellNonMeleeDamageLog(caster, (*i)->GetSpellProto()->Id, splitted, schoolMask, splitted_absorb, 0, false, 0, false); - CleanDamage cleanDamage = CleanDamage(splitted, BASE_ATTACK, MELEE_HIT_NORMAL); + CleanDamage cleanDamage = CleanDamage(splitted, 0, BASE_ATTACK, MELEE_HIT_NORMAL); DealDamage(caster, splitted, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*i)->GetSpellProto(), false); } @@ -2244,7 +2240,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe SendSpellNonMeleeDamageLog(caster, (*i)->GetSpellProto()->Id, splitted, schoolMask, split_absorb, 0, false, 0, false); - CleanDamage cleanDamage = CleanDamage(splitted, BASE_ATTACK, MELEE_HIT_NORMAL); + CleanDamage cleanDamage = CleanDamage(splitted, 0, BASE_ATTACK, MELEE_HIT_NORMAL); DealDamage(caster, splitted, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*i)->GetSpellProto(), false); } } @@ -2364,6 +2360,7 @@ void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool ex // Send log damage message to client DealDamageMods(pVictim,damageInfo.damage,&damageInfo.absorb); SendAttackStateUpdate(&damageInfo); + ProcDamageAndSpell(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, damageInfo.procEx, damageInfo.damage, damageInfo.attackType); DealMeleeDamage(&damageInfo,true); @@ -2795,6 +2792,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell) bool canDodge = true; bool canParry = true; + bool canBlock = spell->AttributesEx3 & SPELL_ATTR_EX3_BLOCKABLE_SPELL; // Same spells cannot be parry/dodge if (spell->Attributes & SPELL_ATTR_IMPOSSIBLE_DODGE_PARRY_BLOCK) @@ -2805,11 +2803,9 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell) tmp += resist_chance; if (roll < tmp) return SPELL_MISS_RESIST; - - // Ranged attack cannot be parry/dodge only deflect - // Check damage class instead of attack type to correctly handle judgements - // - they are meele, but can't be dodged/parried/deflected because of ranged dmg class - if (spell->DmgClass == SPELL_DAMAGE_CLASS_RANGED) + + // Ranged attacks can only miss, resist and deflect + if (attType == RANGED_ATTACK) { // only if in front if (pVictim->HasInArc(M_PI,this)) @@ -2826,10 +2822,11 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell) if (!pVictim->HasInArc(M_PI,this)) { // Can`t dodge from behind in PvP (but its possible in PvE) - if (GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) + if (pVictim->GetTypeId() == TYPEID_PLAYER) canDodge = false; - // Can`t parry + // Can`t parry or block canParry = false; + canBlock = false; } // Check creatures flags_extra for disable parry if(pVictim->GetTypeId()==TYPEID_UNIT) @@ -2837,6 +2834,9 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell) uint32 flagEx = ((Creature*)pVictim)->GetCreatureInfo()->flags_extra; if( flagEx & CREATURE_FLAG_EXTRA_NO_PARRY ) canParry = false; + // Check creatures flags_extra for disable block + if( flagEx & CREATURE_FLAG_EXTRA_NO_BLOCK ) + canBlock = false; } // Ignore combat result aura AuraEffectList const& ignore = GetAurasByType(SPELL_AURA_IGNORE_COMBAT_RESULT); @@ -2847,7 +2847,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell) switch((*i)->GetMiscValue()) { case MELEE_HIT_DODGE: canDodge = false; break; - case MELEE_HIT_BLOCK: break; // Block check in hit step + case MELEE_HIT_BLOCK: canBlock = false; break; case MELEE_HIT_PARRY: canParry = false; break; default: DEBUG_LOG("Spell %u SPELL_AURA_IGNORE_COMBAT_RESULT have unhandled state %d", (*i)->GetId(), (*i)->GetMiscValue()); @@ -2888,6 +2888,17 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell) return SPELL_MISS_PARRY; } + if (canBlock) + { + int32 blockChance = int32(pVictim->GetUnitBlockChance()*100.0f) - skillDiff * 4; + if ( blockChance < 0 ) + blockChance = 0; + tmp += blockChance; + + if (roll < tmp) + return SPELL_MISS_BLOCK; + } + return SPELL_MISS_NONE; } @@ -3002,7 +3013,7 @@ SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool if (reflectchance > 0 && roll_chance_i(reflectchance)) { // Start triggers for remove charges if need (trigger only for victim, and mark as active spell) - ProcDamageAndSpell(pVictim, PROC_FLAG_NONE, PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT, PROC_EX_REFLECT, 0, BASE_ATTACK, spell); + ProcDamageAndSpell(pVictim, PROC_FLAG_NONE, PROC_FLAG_TAKEN_NEGATIVE_MAGIC_SPELL, PROC_EX_REFLECT , 1, BASE_ATTACK, spell); return SPELL_MISS_REFLECT; } } @@ -3491,8 +3502,8 @@ void Unit::InterruptSpell(uint32 spellType, bool withDelayed, bool withInstant) // send autorepeat cancel message for autorepeat spells if (spellType == CURRENT_AUTOREPEAT_SPELL) { - if(GetTypeId()==TYPEID_PLAYER) - ((Player*)this)->SendAutoRepeatCancel(); + if(GetTypeId() == TYPEID_PLAYER) + ((Player*)this)->SendAutoRepeatCancel(this); } if (spell->getState() != SPELL_STATE_FINISHED) @@ -3510,15 +3521,17 @@ bool Unit::IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled, bool skip if ( m_currentSpells[CURRENT_GENERIC_SPELL] && (m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_FINISHED) && (withDelayed || m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_DELAYED) ) + { if (!isAutoshoot || !(m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_NOT_RESET_AUTOSHOT)) return(true); - + } // channeled spells may be delayed, but they are still considered casted else if ( !skipChanneled && m_currentSpells[CURRENT_CHANNELED_SPELL] && (m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED) ) + { if (!isAutoshoot || !(m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_NOT_RESET_AUTOSHOT)) return(true); - + } // autorepeat spells may be finished or delayed, but they are still considered casted else if ( !skipAutorepeat && m_currentSpells[CURRENT_AUTOREPEAT_SPELL] ) return(true); @@ -3549,6 +3562,13 @@ Spell* Unit::FindCurrentSpellBySpellId(uint32 spell_id) const return NULL; } +int32 Unit::GetCurrentSpellCastTime(uint32 spell_id) const +{ + if (Spell const * spell = FindCurrentSpellBySpellId(spell_id)) + return spell->GetCastTime(); + return 0; +} + bool Unit::isInFrontInMap(Unit const* target, float distance, float arc) const { return IsWithinDistInMap(target, distance) && HasInArc( arc, target ); @@ -3779,8 +3799,11 @@ bool Unit::AddAura(Aura *Aur, bool handleEffects) return false; } + SpellEntry const* aurSpellInfo = Aur->GetSpellProto(); + // ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load) - if( !isAlive() && Aur->GetId() != 20584 && Aur->GetId() != 8326 && Aur->GetId() != 2584 && + if( !isAlive() && !IsDeathPersistentSpell(aurSpellInfo) && + //Aur->GetId() != 2584 && // Waiting to Resurrect (not have death persistence flag) (GetTypeId()!=TYPEID_PLAYER || !((Player*)this)->GetSession()->PlayerLoading()) ) { delete Aur; @@ -3796,7 +3819,6 @@ bool Unit::AddAura(Aura *Aur, bool handleEffects) return false; } - SpellEntry const* aurSpellInfo = Aur->GetSpellProto(); uint32 aurId = aurSpellInfo->Id; // passive and persistent and Incanter's Absorption auras can stack with themselves any number of times @@ -3890,6 +3912,16 @@ bool Unit::AddAura(Aura *Aur, bool handleEffects) // add aura, register in lists and arrays Aur->_AddAura(); + + //***************************************************** + // Update target aura state flag + //***************************************************** + if(AuraState aState = GetSpellAuraState(Aur->GetSpellProto())) + { + m_auraStateAuras.insert(AuraStateAurasMap::value_type(aState, Aur)); + ModifyAuraState(aState, true); + } + m_Auras.insert(AuraMap::value_type(aurId, Aur)); if(aurSpellInfo->AuraInterruptFlags) @@ -3897,12 +3929,6 @@ bool Unit::AddAura(Aura *Aur, bool handleEffects) m_interruptableAuras.push_back(Aur); AddInterruptMask(aurSpellInfo->AuraInterruptFlags); } - if((aurSpellInfo->Attributes & SPELL_ATTR_BREAKABLE_BY_DAMAGE - && !Aur->IsAuraType(SPELL_AURA_MOD_POSSESS)) //only dummy aura is breakable - || ((GetAllSpellMechanicMask(aurSpellInfo) & 1<<MECHANIC_KNOCKOUT) && Aur->IsAuraType(SPELL_AURA_MOD_STUN))) - { - m_ccAuras.push_back(Aur); - } if (handleEffects) Aur->HandleEffects(true); @@ -4173,13 +4199,24 @@ void Unit::RemoveAurasByTypeWithDispel(AuraType auraType, Spell * spell) } } -void Unit::RemoveNotOwnSingleTargetAuras() +void Unit::RemoveNotOwnSingleTargetAuras(uint32 newPhase) { // single target auras from other casters for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); ) { if (iter->second->GetCasterGUID()!=GetGUID() && IsSingleTargetSpell(iter->second->GetSpellProto())) - RemoveAura(iter); + { + if(!newPhase) + RemoveAura(iter); + else + { + Unit* caster = iter->second->GetCaster(); + if(!caster || !caster->InSamePhase(newPhase)) + RemoveAura(iter); + else + ++iter; + } + } else ++iter; } @@ -4188,12 +4225,12 @@ void Unit::RemoveNotOwnSingleTargetAuras() AuraList& scAuras = GetSingleCastAuras(); for (AuraList::iterator iter = scAuras.begin(); iter != scAuras.end();) { - Aura * aur=*iter; + Aura * aura=*iter; ++iter; - if (aur->GetTarget()!=this) + if (aura->GetTarget() != this && !aura->GetTarget()->InSamePhase(newPhase)) { uint32 removedAuras = m_removedAurasCount; - aur->GetTarget()->RemoveAura(aur->GetId(),aur->GetCasterGUID()); + aura->GetTarget()->RemoveAura(aura->GetId(),aura->GetCasterGUID()); if (removedAuras+1<m_removedAurasCount) iter=scAuras.begin(); } @@ -4217,13 +4254,6 @@ void Unit::RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode) UpdateInterruptMask(); } - if((Aur->GetSpellProto()->Attributes & SPELL_ATTR_BREAKABLE_BY_DAMAGE - && !Aur->IsAuraType(SPELL_AURA_MOD_POSSESS)) //only dummy aura is breakable - || ((GetAllSpellMechanicMask(Aur->GetSpellProto()) & 1<<MECHANIC_KNOCKOUT) && Aur->IsAuraType(SPELL_AURA_MOD_STUN))) - { - m_ccAuras.remove(Aur); - } - Aur->SetRemoveMode(mode); sLog.outDebug("Aura %u now is remove mode %d", Aur->GetId(), mode); @@ -4234,8 +4264,30 @@ void Unit::RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode) Aur->_RemoveAura(); + bool auraStateFound = false; + if (AuraState auraState = GetSpellAuraState(Aur->GetSpellProto())) + { + bool canBreak = false; + // Get mask of all aurastates from remaining auras + for(AuraStateAurasMap::iterator itr = m_auraStateAuras.lower_bound(auraState); itr != m_auraStateAuras.upper_bound(auraState) && !(auraStateFound && canBreak);) + { + if (itr->second == Aur) + { + m_auraStateAuras.erase(itr); + itr = m_auraStateAuras.lower_bound(auraState); + canBreak = true; + continue; + } + auraStateFound = true; + ++itr; + } + // Remove only aurastates which were not found + if (!auraStateFound) + ModifyAuraState(auraState, false); + } + // Remove totem at next update if totem looses its aura - if (GetTypeId()==TYPEID_UNIT && ((Creature*)this)->isTotem()&& ((TempSummon*)this)->GetSummonerGUID()==Aur->GetCasterGUID()) + if (Aur->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE && GetTypeId()==TYPEID_UNIT && ((Creature*)this)->isTotem()&& ((TempSummon*)this)->GetSummonerGUID()==Aur->GetCasterGUID()) { if (((Totem*)this)->GetSpell()==Aur->GetId() && ((Totem*)this)->GetTotemType()==TOTEM_PASSIVE) ((Totem*)this)->setDeathState(JUST_DIED); @@ -4401,6 +4453,18 @@ AuraEffect* Unit::GetAura(AuraType type, uint32 family, uint32 familyFlag1, uint return NULL; } +AuraEffect * Unit::IsScriptOverriden(SpellEntry const * spell, int32 script) const +{ + AuraEffectList const& auras = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for(AuraEffectList::const_iterator i = auras.begin();i != auras.end(); ++i) + { + if ((*i)->GetMiscValue() == script) + if ((*i)->isAffectedOnSpell(spell)) + return (*i); + } + return NULL; +} + uint32 Unit::GetDiseasesByCaster(uint64 casterGUID, bool remove) { static const AuraType diseaseAuraTypes[] = @@ -4685,13 +4749,13 @@ void Unit::SendPeriodicAuraLog(SpellPeriodicAuraLogInfo *pInfo) data << uint32(GetSpellSchoolMask(aura->GetSpellProto())); data << uint32(pInfo->absorb); // absorb data << uint32(pInfo->resist); // resist - data << uint8(0); // new 3.1.2 + data << uint8(pInfo->critical); // new 3.1.2 critical tick break; case SPELL_AURA_PERIODIC_HEAL: case SPELL_AURA_OBS_MOD_HEALTH: data << uint32(pInfo->damage); // damage data << uint32(pInfo->overDamage); // overheal? - data << uint8(0); // new 3.1.2 + data << uint8(pInfo->critical); // new 3.1.2 critical tick break; case SPELL_AURA_OBS_MOD_ENERGY: case SPELL_AURA_PERIODIC_ENERGIZE: @@ -4827,7 +4891,7 @@ bool Unit::HandleHasteAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger case 33735: { target = SelectNearbyTarget(); - if(!target) + if(!target || target == pVictim) return false; basepoints0 = damage; triggered_spell_id = 22482; @@ -4851,7 +4915,7 @@ bool Unit::HandleHasteAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger } // default case - if(!target || target!=this && !target->isAlive()) + if((!target && !spellmgr.IsSrcTargetSpell(triggerEntry)) || (target && target!=this && !target->isAlive())) return false; if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id)) @@ -4880,6 +4944,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger uint32 triggered_spell_id = 0; Unit* target = pVictim; int32 basepoints0 = 0; + uint64 originalCaster = 0; // Master of subtlety (checked here because ranks have different spellfamilynames) if (dummySpell->Id == 31223 || dummySpell->Id == 31221 || dummySpell->Id == 31222) @@ -4898,6 +4963,18 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger { switch (dummySpell->Id) { + // BloodWorms Health Leech + case 50453: + { + if (Unit *owner = this->GetOwner()) + { + basepoints0 = int32(damage*1.50); + target = owner; + triggered_spell_id = 50454; + break; + } + return false; + } // Improved Divine Spirit case 33174: case 33182: @@ -4939,14 +5016,9 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger break; } // Sweeping Strikes - case 12328: case 18765: case 35429: { - // prevent chain of triggered spell from same triggered spell - if(procSpell && procSpell->Id==26654) - return false; - target = SelectNearbyTarget(); if(!target) return false; @@ -5411,11 +5483,20 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger } switch(dummySpell->Id) { + // Glyph of Polymorph + case 56375: + { + target->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE); + target->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT); + return true; + } // Glyph of Icy Veins case 56374: + { RemoveAurasByType(SPELL_AURA_MOD_HASTE, 0, 0, true, false); RemoveAurasByType(SPELL_AURA_MOD_DECREASE_SPEED); - return true; + return true; + } // Ignite case 11119: case 11120: @@ -5474,6 +5555,28 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger } case SPELLFAMILY_WARRIOR: { + switch(dummySpell->Id) + { + // Sweeping Strikes + case 12328: + { + target = SelectNearbyTarget(); + if(!target) + return false; + + triggered_spell_id = 26654; + break; + } + // Improved Spell Reflection + case 59088: + case 59089: + { + triggered_spell_id = 59725; + target = this; + break; + } + } + // Retaliation if(dummySpell->SpellFamilyFlags.IsEqual(0, 0x8, 0)) { @@ -5484,14 +5587,6 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger triggered_spell_id = 22858; break; } - // Improved Spell Reflection - if(dummySpell->Id==59088 - || dummySpell->Id==59089) - { - triggered_spell_id = 59725; - target = this; - break; - } // Second Wind if (dummySpell->SpellIconID == 1697) { @@ -5613,6 +5708,8 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger { if ((*i)->GetId()==54117 || (*i)->GetId()==54118) { + if ((*i)->GetEffIndex() != 0) + continue; basepoints0 = int32((*i)->GetAmount()); if (target = GetGuardianPet()) { @@ -5621,6 +5718,15 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger } // regen mana for caster CastCustomSpell(this,59117,&basepoints0,NULL,NULL,true,castItem,triggeredByAura); + // Get second aura of spell for replenishment effect on party + if (AuraEffect const * aurEff = (*i)->GetParentAura()->GetPartAura(1)) + { + // Replenishment - roll chance + if (roll_chance_i(aurEff->GetAmount())) + { + CastSpell(this,57669,true, castItem, triggeredByAura); + } + } break; } } @@ -5806,22 +5912,6 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger } break; } - // Psychic Horror - case 47571: - { - if(!pVictim || !pVictim->isAlive()) - return false; - pVictim->CastSpell(pVictim, 59980,true, castItem, triggeredByAura); - return true; - } - // Psychic Horror (Rank 2) - case 47572: - { - if(!pVictim || !pVictim->isAlive()) - return false; - pVictim->CastSpell(pVictim, 59981,true, castItem, triggeredByAura); - return true; - } // Glyph of Dispel Magic case 55677: { @@ -5884,10 +5974,13 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger basepoints0 = triggerAmount * GetMaxHealth() / 100; target = this; triggered_spell_id = 34299; - if (triggeredByAura->GetCaster() != this) + if (triggeredByAura->GetCasterGUID() != GetGUID()) break; int32 basepoints1 = triggerAmount * 2; - CastCustomSpell(this,60889,&basepoints1,0,0,true,0,triggeredByAura); + // Improved Leader of the Pack + // Check cooldown of heal spell cooldown + if (GetTypeId()==TYPEID_PLAYER && !((Player *)this)->HasSpellCooldown(34299)) + CastCustomSpell(this,60889,&basepoints1,0,0,true,0,triggeredByAura); break; } // Healing Touch (Dreamwalker Raiment set) @@ -5940,7 +6033,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger triggered_spell_id = 40446; chance = 25.0f; } - // Mangle (cat/bear) + // Mangle (Bear) and Mangle (Cat) else if( procSpell->SpellFamilyFlags[1] & 0x00000440) { triggered_spell_id = 40452; @@ -5998,6 +6091,30 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger basepoints0 = triggerAmount * damage / 100; break; } + // King of the Jungle + else if (dummySpell->SpellIconID == 2850) + { + // Effect 0 - mod damage while having Enrage + if (effIndex==0) + { + if (!(procSpell->SpellFamilyFlags[0] & 0x00080000)) + return false; + triggered_spell_id = 51185; + basepoints0 = triggerAmount; + target = this; + break; + } + // Effect 1 - Tiger's Fury restore energy + else if (effIndex==1) + { + if (!(procSpell->SpellFamilyFlags[2] & 0x00000800)) + return false; + triggered_spell_id = 51178; + basepoints0 = triggerAmount; + target = this; + break; + } + } break; } case SPELLFAMILY_ROGUE: @@ -6114,36 +6231,26 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger // Light's Beacon - Beacon of Light if ( dummySpell->Id == 53651 ) { - if (Unit * caster = triggeredByAura->GetCaster()) + if (Unit * source = triggeredByAura->GetSource()) { // do not proc when target of beacon of light is healed - if (caster == pVictim) + if (source == this) return false; - if (Aura const* aur = caster->GetAura(53563)) + if (Unit * caster = triggeredByAura->GetCaster()) { - if (Unit * paladin = aur->GetCaster()) - { - if (paladin != this) - return false; - basepoints0 = damage; - triggered_spell_id = 53654; - target = caster; - break; - } - else - { - pVictim->RemoveAura(triggeredByAura->GetParentAura()); + if (caster != pVictim) return false; - } + basepoints0 = damage; + triggered_spell_id = 53654; + target = source; + break; } } - else return false; + return false; } // Judgements of the Wise if (dummySpell->SpellIconID == 3017) { - // hardcoded amount - basepoints0 = 15 * GetCreatePowers(POWER_MANA)/100; target = this; triggered_spell_id = 31930; // replenishment @@ -6184,17 +6291,19 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger } switch(dummySpell->Id) { + // Heart of the Crusader + case 20335: // rank 1 + triggered_spell_id = 21183; + break; + case 20336: // rank 2 + triggered_spell_id = 54498; + break; + case 20337: // rank 3 + triggered_spell_id = 54499; + break; // Judgement of Light case 20185: { - // Get judgement caster - Unit *caster = triggeredByAura->GetCaster(); - if (!caster) - return false; - float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK); - int32 holy = caster->SpellBaseDamageBonus(SPELL_SCHOOL_MASK_HOLY) + - caster->SpellBaseDamageBonusForVictim(SPELL_SCHOOL_MASK_HOLY, this); - basepoints0 = int32(ap*0.10f + 0.10f*holy); pVictim->CastCustomSpell(pVictim, 20267, &basepoints0, 0, 0, true, 0, triggeredByAura); return true; } @@ -6269,7 +6378,9 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger // heal amount basepoints0 = triggerAmount*damage/100; target = this; - triggered_spell_id = 31786; + + if(basepoints0) + triggered_spell_id = 31786; break; } // Seal of Blood do damage trigger @@ -6320,8 +6431,8 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger triggered_spell_id = 40471; chance = 15.0f; } - // Judgement - else if( procSpell->SpellFamilyFlags[0] & 0x800000 ) + // Judgement (any) + else if (GetSpellSpecific(procSpell->Id)==SPELL_JUDGEMENT) { triggered_spell_id = 40472; chance = 50.0f; @@ -6433,6 +6544,9 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger if( cooldown && ((Player*)this)->HasSpellCooldown(dummySpell->Id)) return false; + if(triggeredByAura->GetParentAura() && castItem->GetGUID() != triggeredByAura->GetParentAura()->GetCastItemGUID()) + return false; + // Now amount of extra power stored in 1 effect of Enchant spell // Get it by item enchant id uint32 spellId; @@ -6463,20 +6577,21 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger int32 extra_attack_power = CalculateSpellDamage(windfurySpellEntry, 1, windfurySpellEntry->EffectBasePoints[1], pVictim); - // Off-Hand case - if ( castItem->GetSlot() == EQUIPMENT_SLOT_OFFHAND && isAttackReady(OFF_ATTACK) ) - { - // Value gained from additional AP - basepoints0 = int32(extra_attack_power/14.0f * GetAttackTime(OFF_ATTACK)/1000/2); - triggered_spell_id = 33750; - } // Main-Hand case - else if ( isAttackReady(BASE_ATTACK) ) + if ( castItem->GetSlot() == EQUIPMENT_SLOT_MAINHAND && isAttackReady(BASE_ATTACK) ) { // Value gained from additional AP basepoints0 = int32(extra_attack_power/14.0f * GetAttackTime(BASE_ATTACK)/1000); triggered_spell_id = 25504; } + // Off-Hand case + else if ( castItem->GetSlot() == EQUIPMENT_SLOT_OFFHAND && isAttackReady(OFF_ATTACK) ) + { + // Value gained from additional AP + basepoints0 = int32(extra_attack_power/14.0f * GetAttackTime(OFF_ATTACK)/1000/2); + triggered_spell_id = 33750; + } + else return false; @@ -6543,6 +6658,13 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger triggered_spell_id = 58879; break; } + // Shaman T8 Elemental 4P Bonus + case 64928: + { + basepoints0 = int32( triggerAmount * damage / 100 ); + triggered_spell_id = 64930; // Electrified + break; + } } // Storm, Earth and Fire if (dummySpell->SpellIconID == 3063) @@ -6569,6 +6691,8 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger // Earth Shield if(dummySpell->SpellFamilyFlags[1] & 0x00000400) { + // 3.0.8: Now correctly uses the Shaman's own spell critical strike chance to determine the chance of a critical heal. + originalCaster = triggeredByAura->GetCasterGUID(); basepoints0 = triggerAmount; target = this; triggered_spell_id = 379; @@ -6816,13 +6940,6 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger triggered_spell_id = 50526; break; } - // Death Strike healing effect - if (dummySpell->Id == 45469) - { - int32 heal=pVictim->GetDiseasesByCaster(GetGUID()) * GetMaxHealth()* 5 /100; - CastCustomSpell(this,45470,&heal,NULL,NULL,true); - return true; - } // Sudden Doom if (dummySpell->SpellIconID == 1939 && GetTypeId() == TYPEID_PLAYER) { @@ -6925,23 +7042,22 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger } // default case - if(!target || target!=this && !target->isAlive()) + if((!target && !spellmgr.IsSrcTargetSpell(triggerEntry)) || (target && target!=this && !target->isAlive())) return false; if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id)) return false; if(basepoints0) - CastCustomSpell(target,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura); + CastCustomSpell(target,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura, originalCaster); else - CastSpell(target,triggered_spell_id,true,castItem,triggeredByAura); + CastSpell(target,triggered_spell_id,true,castItem,triggeredByAura, originalCaster); if( cooldown && GetTypeId()==TYPEID_PLAYER ) ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown); return true; } - bool Unit::HandleObsModEnergyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown) { SpellEntry const *dummySpell = triggeredByAura->GetSpellProto (); @@ -6964,7 +7080,6 @@ bool Unit::HandleObsModEnergyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* { uint32 maxmana = GetMaxPower(POWER_MANA); basepoints0 = maxmana* GetAttackTime(RANGED_ATTACK)/1000.0f/100.0f; - target = this; triggered_spell_id = 34075; break; @@ -6978,6 +7093,7 @@ bool Unit::HandleObsModEnergyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id); + // Try handle unknown trigger spells if(!triggerEntry) { sLog.outError("Unit::HandleObsModEnergyAuraProc: Spell %u have not existed triggered spell %u",dummySpell->Id,triggered_spell_id); @@ -6985,12 +7101,11 @@ bool Unit::HandleObsModEnergyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* } // default case - if(!target || target!=this && !target->isAlive()) + if((!target && !spellmgr.IsSrcTargetSpell(triggerEntry)) || (target && target!=this && !target->isAlive())) return false; if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id)) return false; - if(basepoints0) CastCustomSpell(target,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura); else @@ -6998,10 +7113,8 @@ bool Unit::HandleObsModEnergyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* if( cooldown && GetTypeId()==TYPEID_PLAYER ) ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown); - return true; } - bool Unit::HandleModDamagePctTakenAuraProc(Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown) { SpellEntry const *dummySpell = triggeredByAura->GetSpellProto (); @@ -7025,8 +7138,6 @@ bool Unit::HandleModDamagePctTakenAuraProc(Unit *pVictim, uint32 damage, AuraEff switch (getPowerType()) { case POWER_MANA: triggered_spell_id = 57319; break; - case POWER_RAGE: triggered_spell_id = 57320; break; - case POWER_RUNIC_POWER: triggered_spell_id = 57321; break; default: return false; } @@ -7047,7 +7158,7 @@ bool Unit::HandleModDamagePctTakenAuraProc(Unit *pVictim, uint32 damage, AuraEff } // default case - if(!target || target!=this && !target->isAlive()) + if((!target && !spellmgr.IsSrcTargetSpell(triggerEntry)) || (target && target!=this && !target->isAlive())) return false; if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id)) @@ -7074,12 +7185,66 @@ bool Unit::HandleAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAura, S { case SPELLFAMILY_DEATHKNIGHT: { + // Blood of the North + // Reaping + // Death Rune Mastery + if (dummySpell->SpellIconID == 3041 || dummySpell->SpellIconID == 22 || dummySpell->SpellIconID == 2622) + { + *handled = true; + // Convert recently used Blood Rune to Death Rune + if (GetTypeId() == TYPEID_PLAYER) + { + if(((Player*)this)->getClass() != CLASS_DEATH_KNIGHT) + return false; + RuneType rune = ((Player*)this)->GetLastUsedRune(); + // can't proc from death rune use + if (rune == RUNE_DEATH) + return false; + AuraEffect * aurEff = triggeredByAura->GetPartAura(0); + if (!aurEff) + return false; + // Reset amplitude - set death rune remove timer to 30s + aurEff->ResetPeriodicTimer(); + uint32 runesLeft; + + if (dummySpell->SpellIconID == 2622) + runesLeft = 2; + else + runesLeft = 1; + + for (uint8 i=0;i<MAX_RUNES && runesLeft;++i) + { + if (dummySpell->SpellIconID == 2622) + { + if (((Player*)this)->GetCurrentRune(i) == RUNE_DEATH || + ((Player*)this)->GetBaseRune(i) == RUNE_BLOOD ) + continue; + } + else + { + if (((Player*)this)->GetCurrentRune(i) == RUNE_DEATH || + ((Player*)this)->GetBaseRune(i) != RUNE_BLOOD ) + continue; + } + if (((Player*)this)->GetRuneCooldown(i) != RUNE_COOLDOWN) + continue; + + --runesLeft; + // Mark aura as used + aurEff->SetAmount(aurEff->GetAmount() | (1<<i)); + ((Player*)this)->ConvertRune(i,RUNE_DEATH); + } + return true; + } + return false; + } + switch(dummySpell->Id) { // Hungering Cold aura drop case 51209: *handled = true; - // Drop only in disease case + // Drop only in not disease case if (procSpell && procSpell->Dispel == DISPEL_DISEASE) return false; return true; @@ -7273,38 +7438,8 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig break; case SPELLFAMILY_WARLOCK: { - // Pyroclasm - if (auraSpellInfo->SpellIconID == 1137) - { - if(!pVictim || !pVictim->isAlive() || pVictim == this || procSpell == NULL) - return false; - // Calculate spell tick count for spells - uint32 tick; - - // Hellfire have 15 tick - if (procSpell->SpellFamilyFlags[0]&0x40) - tick = 15; - // Rain of Fire have 4 tick - else if (procSpell->SpellFamilyFlags[0]&0x20) - tick = 4; - else - tick = 1; - - // Calculate chance = baseChance / tick - float chance = 0; - switch (auraSpellInfo->Id) - { - case 18096: chance = 13.0f / tick; break; - case 18073: chance = 26.0f / tick; break; - } - // Roll chance - if (!roll_chance_f(chance)) - return false; - - trigger_spell_id = 18093; - } // Improved Drain Soul - else if (auraSpellInfo->SpellFamilyFlags[0] & 0x4000) + if (auraSpellInfo->SpellFamilyFlags[0] & 0x4000) { Unit::AuraEffectList const& mAddFlatModifier = GetAurasByType(SPELL_AURA_DUMMY); for(Unit::AuraEffectList::const_iterator i = mAddFlatModifier.begin(); i != mAddFlatModifier.end(); ++i) @@ -7387,7 +7522,28 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig break; } case SPELLFAMILY_HUNTER: + { + if (auraSpellInfo->SpellIconID == 3247) // Piercing Shots + { + switch (auraSpellInfo->Id) + { + case 53234: // Rank 1 + case 53237: // Rank 2 + case 53238: // Rank 3 + trigger_spell_id = 63468; + break; + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Piercing Shots",auraSpellInfo->Id); + return false; + } + SpellEntry const *TriggerPS = sSpellStore.LookupEntry(trigger_spell_id); + if(!TriggerPS) + return false; + basepoints0 = int32(damage * triggerAmount / 100 / (GetSpellMaxDuration(TriggerPS) / TriggerPS->EffectAmplitude[0])); + target = pVictim; + } break; + } case SPELLFAMILY_PALADIN: { /* @@ -7462,9 +7618,9 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig // stacking CastSpell(this, 37658, true, NULL, triggeredByAura); - AuraEffect * dummy = GetDummyAura(37658); + Aura * dummy = GetAura(37658); // release at 3 aura in stack (cont contain in basepoint of trigger aura) - if(!dummy || dummy->GetParentAura()->GetStackAmount() < triggerAmount) + if(!dummy || dummy->GetStackAmount() < triggerAmount) return false; RemoveAurasDueToSpell(37658); @@ -7480,9 +7636,9 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig CastSpell(this, 54842, true, NULL, triggeredByAura); // counting - AuraEffect * dummy = GetDummyAura(54842); + Aura * dummy = GetAura(54842); // release at 3 aura in stack (cont contain in basepoint of trigger aura) - if(!dummy || dummy->GetParentAura()->GetStackAmount() < triggerAmount) + if(!dummy || dummy->GetStackAmount() < triggerAmount) return false; RemoveAurasDueToSpell(54842); @@ -7580,8 +7736,8 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig return false; } } - // Blood Presence - else if (auraSpellInfo->Id == 48266) + // Blood Presence (Improved) + else if (auraSpellInfo->Id == 63611) { if (GetTypeId() != TYPEID_PLAYER) return false; @@ -7761,7 +7917,7 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig case 22959: { // Glyph of Improved Scorch - if (AuraEffect * aurEff = GetDummyAura(56371)) + if (AuraEffect * aurEff = GetAuraEffect(56371,0)) { for (int32 count = aurEff->GetAmount();count>0;count--) CastSpell(pVictim, 22959, true); @@ -7795,10 +7951,9 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet()) if (Unit * owner = GetOwner()) { - if (AuraEffect * aurEff = owner->GetDummyAura(SPELLFAMILY_WARLOCK, 3220)) + if (AuraEffect * aurEff = owner->GetDummyAura(SPELLFAMILY_WARLOCK, 3220, 0)) { - if (owner->GetTypeId() == TYPEID_PLAYER) - basepoints0 = (aurEff->GetAmount() * ((Player*)owner)->GetBaseSpellDamageBonus() + 100.0f) / 100; + basepoints0 = int32((aurEff->GetAmount() * owner->SpellBaseDamageBonus(SpellSchoolMask(SPELL_SCHOOL_MASK_MAGIC)) + 100.0f) / 100.0f); CastCustomSpell(this,trigger_spell_id,&basepoints0,&basepoints0,NULL,true,castItem,triggeredByAura); return true; } @@ -7808,14 +7963,20 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig // Sword and Board case 50227: { - // remove cooldown of Shield Slam + // Remove cooldown on Shield Slam if (GetTypeId()==TYPEID_PLAYER) - ((Player*)this)->RemoveCategoryCooldown(1209); + ((Player*)this)->RemoveSpellCategoryCooldown(1209, true); break; } - case 63375: // Improved Stormstrike + // Maelstrom Weapon + case 53817: { - basepoints0 = GetCreateMana() * 0.20f; + // have rank dependent proc chance, ignore too often cases + // PPM = 2.5 * (rank of talent), + uint32 rank = spellmgr.GetSpellRank(auraSpellInfo->Id); + // 5 rank -> 100% 4 rank -> 80% and etc from full rate + if(!roll_chance_i(20*rank)) + return false; break; } // Brain Freeze @@ -7867,7 +8028,15 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig case 56453: { // Proc only from trap activation (from periodic proc another aura of this spell) - if (!(procFlags & PROC_FLAG_ON_TRAP_ACTIVATION)) + if (!(procFlags & PROC_FLAG_ON_TRAP_ACTIVATION) || !roll_chance_i(triggerAmount)) + return false; + break; + } + // Glyph of Death's Embrace + case 58679: + { + // Proc only from healing part of Death Coil. Check is essential as all Death Coil spells have 0x2000 mask in SpellFamilyFlags + if (!procSpell || !(procSpell->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && procSpell->SpellFamilyFlags[0] == 0x80002000)) return false; break; } @@ -7879,6 +8048,12 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig ((Player*)this)->RemoveCategoryCooldown(82); return true; } + // Savage Defense + case 62606: + { + basepoints0 = int32(GetTotalAttackPowerValue(BASE_ATTACK) * triggerAmount / 100.0f); + break; + } } if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(trigger_spell_id)) @@ -7886,16 +8061,14 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig // try detect target manually if not set if ( target == NULL ) - target = !(procFlags & PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL) && IsPositiveSpell(trigger_spell_id) ? this : pVictim; + target = !(procFlags & (PROC_FLAG_SUCCESSFUL_POSITIVE_MAGIC_SPELL | PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL_HIT)) && IsPositiveSpell(trigger_spell_id) ? this : pVictim; // default case - if(!target || target!=this && !target->isAlive()) + if((!target && !spellmgr.IsSrcTargetSpell(triggerEntry)) || (target && target!=this && !target->isAlive())) return false; if(basepoints0) CastCustomSpell(target,trigger_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura); - //else if(spellmgr.GetSpellCustomAttr(trigger_spell_id) & SPELL_ATTR_CU_AURA_SPELL) - // AddAura(trigger_spell_id, target); else CastSpell(target,trigger_spell_id,true,castItem,triggeredByAura); @@ -7982,6 +8155,21 @@ bool Unit::HandleOverrideClassScriptAuraProc(Unit *pVictim, uint32 damage, AuraE CastCustomSpell(this, 47762, &basepoints0, 0, 0, true, 0, triggeredByAura); return true; } + case 7010: // Revitalize - can proc on full hp target + case 7011: + case 7012: + { + if (!roll_chance_i(triggeredByAura->GetAmount())) + return false; + switch(pVictim->getPowerType()) + { + case POWER_MANA: triggered_spell_id = 48542; break; + case POWER_RAGE: triggered_spell_id = 48541; break; + case POWER_ENERGY: triggered_spell_id = 48540; break; + case POWER_RUNIC_POWER: triggered_spell_id = 48543; break; + } + break; + } } // not processed @@ -8329,7 +8517,7 @@ bool Unit::Attack(Unit *victim, bool meleeAttack) return false; // dead units can neither attack nor be attacked - if(!isAlive() || !victim->isAlive()) + if(!isAlive() || !victim->IsInWorld() || !victim->isAlive()) return false; // player cannot attack in mount state @@ -8388,7 +8576,7 @@ bool Unit::Attack(Unit *victim, bool meleeAttack) //if(GetTypeId()==TYPEID_UNIT) // ((Creature*)this)->SetCombatStartPosition(GetPositionX(), GetPositionY(), GetPositionZ()); - if(GetTypeId()==TYPEID_UNIT && !IsControlledByPlayer()) + if(GetTypeId()==TYPEID_UNIT) { // should not let player enter combat by right clicking target SetInCombatWith(victim); @@ -8529,14 +8717,6 @@ void Unit::ModifyAuraState(AuraState flag, bool apply) SpellEntry const* spellProto = (*itr).second->GetSpellProto(); if (spellProto->CasterAuraState == flag) { - // exceptions (applied at state but not removed at state change) - // Rampage - if(spellProto->SpellIconID==2006 && spellProto->SpellFamilyName==SPELLFAMILY_WARRIOR && spellProto->SpellFamilyFlags[0]==0x100000) - { - ++itr; - continue; - } - RemoveAura(itr); } else @@ -8547,15 +8727,42 @@ void Unit::ModifyAuraState(AuraState flag, bool apply) } } +uint32 Unit::BuildAuraStateUpdateForTarget(Unit * target) const +{ + uint32 auraStates = GetUInt32Value(UNIT_FIELD_AURASTATE) &~(PER_CASTER_AURA_STATE_MASK); + for(AuraStateAurasMap::const_iterator itr = m_auraStateAuras.begin(); itr != m_auraStateAuras.end();++itr) + { + if ((1<<(itr->first-1)) & PER_CASTER_AURA_STATE_MASK) + { + if (itr->second->GetCasterGUID() == target->GetGUID()) + auraStates |= (1<<(itr->first-1)); + } + } + return auraStates; +} + bool Unit::HasAuraState(AuraState flag, SpellEntry const *spellProto, Unit * Caster) const { - if (Caster && spellProto) + if (Caster) { - AuraEffectList const& stateAuras = Caster->GetAurasByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE); - for(AuraEffectList::const_iterator j = stateAuras.begin();j != stateAuras.end(); ++j) - if((*j)->isAffectedOnSpell(spellProto)) - return true; + if(spellProto) + { + AuraEffectList const& stateAuras = Caster->GetAurasByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE); + for(AuraEffectList::const_iterator j = stateAuras.begin();j != stateAuras.end(); ++j) + if((*j)->isAffectedOnSpell(spellProto)) + return true; + } + // Check per caster aura state + // If aura with aurastate by caster not found return false + if ((1<<(flag-1)) & PER_CASTER_AURA_STATE_MASK) + { + for(AuraStateAurasMap::const_iterator itr = m_auraStateAuras.lower_bound(flag); itr != m_auraStateAuras.upper_bound(flag);++itr) + if (itr->second->GetCasterGUID() == Caster->GetGUID()) + return true; + return false; + } } + return HasFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)); } @@ -8741,13 +8948,13 @@ void Unit::SetMinion(Minion *minion, bool apply) assert((*itr)->GetOwnerGUID() == GetGUID()); assert((*itr)->GetTypeId() == TYPEID_UNIT); - if(!((Creature*)(*itr))->HasSummonMask(SUMMON_MASK_GUARDIAN)) + if(!((Creature*)(*itr))->HasSummonMask(SUMMON_MASK_CONTROLABLE_GUARDIAN)) continue; if(AddUInt64Value(UNIT_FIELD_SUMMON, (*itr)->GetGUID())) { //show another pet bar if there is no charm bar - if(GetTypeId() == TYPEID_PLAYER && !GetCharmGUID() && ((Creature*)(*itr))->HasSummonMask(SUMMON_MASK_GUARDIAN)) + if(GetTypeId() == TYPEID_PLAYER && !GetCharmGUID() && ((Creature*)(*itr))->HasSummonMask(SUMMON_MASK_CONTROLABLE_GUARDIAN)) { if(((Creature*)(*itr))->isPet()) ((Player*)this)->PetSpellInitialize(); @@ -8835,19 +9042,24 @@ int32 Unit::DealHeal(Unit *pVictim, uint32 addhealth, SpellEntry const *spellPro { int32 gain = pVictim->ModifyHealth(int32(addhealth)); - if (GetTypeId()==TYPEID_PLAYER) + Unit* unit = this; + + if( GetTypeId()==TYPEID_UNIT && ((Creature*)this)->isTotem()) + unit = GetOwner(); + + if (unit->GetTypeId()==TYPEID_PLAYER) { // overheal = addhealth - gain - SendHealSpellLog(pVictim, spellProto->Id, addhealth, addhealth > gain ? addhealth - gain : 0, critical); + unit->SendHealSpellLog(pVictim, spellProto->Id, addhealth, addhealth - gain, critical); - if (BattleGround *bg = ((Player*)this)->GetBattleGround()) - bg->UpdatePlayerScore((Player*)this, SCORE_HEALING_DONE, gain); + if (BattleGround *bg = ((Player*)unit)->GetBattleGround()) + bg->UpdatePlayerScore((Player*)unit, SCORE_HEALING_DONE, gain); // use the actual gain, as the overheal shall not be counted, skip gain 0 (it ignored anyway in to criteria) if (gain) - ((Player*)this)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, gain, 0, pVictim); + ((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, gain, 0, pVictim); - ((Player*)this)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED, addhealth); + ((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED, addhealth); } if (pVictim->GetTypeId()==TYPEID_PLAYER) @@ -8926,9 +9138,7 @@ void Unit::RemoveAllControlled() && target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->HasSummonMask(SUMMON_MASK_SUMMON)) { - - if(!((TempSummon*)target)->isPet()) - ((TempSummon*)target)->UnSummon(); + ((TempSummon*)target)->UnSummon(); } else { @@ -9073,7 +9283,7 @@ void Unit::EnergizeBySpell(Unit *pVictim, uint32 SpellID, uint32 Damage, Powers { SendEnergizeSpellLog(pVictim, SpellID, Damage, powertype); // needs to be called after sending spell log - ModifyPower(powertype, Damage); + pVictim->ModifyPower(powertype, Damage); } uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack) @@ -9202,7 +9412,8 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3 } else // Tundra Stalker { - if (pVictim->GetAura(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DEATHKNIGHT,0, 0x4000000,0)) + // Frost Fever (target debuff) + if (pVictim->GetAura(SPELL_AURA_MOD_HASTE, SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DEATHKNIGHT, 0, 0, 0x2)) DoneTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; break; } @@ -9265,13 +9476,12 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3 } } break; - // Glyph of Shadow Word: Pain case SPELLFAMILY_PRIEST: if (spellProto->SpellFamilyFlags[0] & 0x800000) { // Increase Mind Flay damage - if (AuraEffect * aurEff = GetDummyAura(55687)) + if (AuraEffect * aurEff = GetAuraEffect(55687, 0)) // if Shadow Word: Pain present if (pVictim->GetAura(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_PRIEST, 0x8000, 0,0, GetGUID())) DoneTotalMod *= (aurEff->GetAmount() + 100.0f) / 100.f; @@ -9300,17 +9510,17 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3 // Improved Icy Touch if (spellProto->SpellFamilyFlags[0] & 0x2) { - if (AuraEffect * aurEff = GetDummyAura(SPELLFAMILY_DEATHKNIGHT, 2721)) + if (AuraEffect * aurEff = GetDummyAura(SPELLFAMILY_DEATHKNIGHT, 2721, 0)) DoneTotalMod *= (100.0f + aurEff->GetAmount()) / 100.0f ; } // Glacier Rot if (spellProto->SpellFamilyFlags[0] & 0x2 || spellProto->SpellFamilyFlags[1] & 0x6) { - if (AuraEffect * aurEff = GetDummyAura(SPELLFAMILY_DEATHKNIGHT, 196)) + if (AuraEffect * aurEff = GetDummyAura(SPELLFAMILY_DEATHKNIGHT, 196, 0)) DoneTotalMod *= (100.0f + aurEff->GetAmount()) / 100.0f; } // This is not a typo - Impurity has SPELLFAMILY_DRUID - if (AuraEffect * aurEff = GetDummyAura(SPELLFAMILY_DRUID, 1986)) + if (AuraEffect * aurEff = GetDummyAura(SPELLFAMILY_DRUID, 1986, 0)) ApCoeffMod *= (100.0f + aurEff->GetAmount()) / 100.0f; break; } @@ -9325,7 +9535,7 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3 if (pVictim->GetTypeId() == TYPEID_PLAYER) { //Cheat Death - if (AuraEffect *dummy = pVictim->GetDummyAura(45182)) + if (AuraEffect *dummy = pVictim->GetAuraEffect(45182, 0)) { float mod = -((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_SPELL)*2*4; if (mod < dummy->GetAmount()) @@ -9341,14 +9551,11 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3 TakenTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; // Mod damage from spell mechanic - uint32 mechanicMask = GetAllSpellMechanicMask(spellProto); - if (mechanicMask) + if (uint32 mechanicMask = GetAllSpellMechanicMask(spellProto)) { AuraEffectList const& mDamageDoneMechanic = pVictim->GetAurasByType(SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT); for(AuraEffectList::const_iterator i = mDamageDoneMechanic.begin();i != mDamageDoneMechanic.end(); ++i) - if((mechanicMask & uint32(1<<((*i)->GetMiscValue()))) - // Shred - "Effects which increase Bleed damage also increase Shred damage" - || ((*i)->GetMiscValue() == MECHANIC_BLEED && spellProto->SpellFamilyName == SPELLFAMILY_DRUID && spellProto->SpellFamilyFlags[0] & 0x8000)) + if(mechanicMask & uint32(1<<((*i)->GetMiscValue()))) TakenTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; } @@ -9361,21 +9568,29 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3 DoneAdvertisedBenefit += ((Guardian*)this)->GetBonusDamage(); // Check for table values - float coeff; + float coeff = 0; SpellBonusEntry const* bonus = spellmgr.GetSpellBonusData(spellProto->Id); if (bonus) { if (damagetype == DOT) + { coeff = bonus->dot_damage; + if (bonus->ap_dot_bonus > 0) + DoneTotal+=bonus->ap_dot_bonus * stack * ApCoeffMod * GetTotalAttackPowerValue( + (IsRangedWeaponSpell(spellProto) && spellProto->DmgClass !=SPELL_DAMAGE_CLASS_MELEE) ? RANGED_ATTACK : BASE_ATTACK); + } else + { coeff = bonus->direct_damage; - if (bonus->ap_bonus) - DoneTotal+=bonus->ap_bonus * GetTotalAttackPowerValue(BASE_ATTACK) * stack * ApCoeffMod; + if (bonus->ap_bonus > 0) + DoneTotal+=bonus->ap_bonus * stack * ApCoeffMod * GetTotalAttackPowerValue( + (IsRangedWeaponSpell(spellProto) && spellProto->DmgClass !=SPELL_DAMAGE_CLASS_MELEE)? RANGED_ATTACK : BASE_ATTACK); + } } // Default calculation if (DoneAdvertisedBenefit || TakenAdvertisedBenefit) { - if(!bonus) + if(!bonus || coeff < 0) { // Damage Done from spell damage bonus int32 CastingTime = !IsChanneledSpell(spellProto) ? GetSpellCastTime(spellProto) : GetSpellDuration(spellProto); @@ -9520,8 +9735,9 @@ bool Unit::isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolM float crit_chance = 0.0f; switch(spellProto->DmgClass) { - case SPELL_DAMAGE_CLASS_NONE: - return false; + case SPELL_DAMAGE_CLASS_NONE: // Exception for earth shield + if (spellProto->Id != 379) // We need more spells to find a general way (if there is any) + return false; case SPELL_DAMAGE_CLASS_MAGIC: { if (schoolMask & SPELL_SCHOOL_MASK_NORMAL) @@ -9590,11 +9806,18 @@ bool Unit::isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolM // Sacred Shield if (spellProto->SpellFamilyFlags[0] & 0x40000000) { - AuraEffect const* aura = pVictim->GetDummyAura(58597); + AuraEffect const* aura = pVictim->GetAuraEffect(58597,1); if (aura && aura->GetCasterGUID() == GetGUID()) crit_chance+=aura->GetAmount(); break; } + // Exorcism + else if (spellProto->Category == 19) + { + if (pVictim->GetCreatureTypeMask() & CREATURE_TYPEMASK_DEMON_OR_UNDEAD) + return true; + break; + } break; case SPELLFAMILY_SHAMAN: // Lava Burst @@ -9616,12 +9839,30 @@ bool Unit::isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolM break; } case SPELL_DAMAGE_CLASS_MELEE: + if (pVictim) + { + // Custom crit by class + switch(spellProto->SpellFamilyName) + { + case SPELLFAMILY_DRUID: + // Rend and Tear - bonus crit chance for bleeding targets of Ferocious Bite + if (spellProto->SpellFamilyFlags[0] & 0x00800000 && pVictim->HasAuraState(AURA_STATE_BLEEDING, spellProto, this)) + { + if (AuraEffect const* rendAndTear = GetDummyAura(SPELLFAMILY_DRUID, 2859, 1)) + { + crit_chance += rendAndTear->GetAmount(); + } + break; + } + break; + } + } case SPELL_DAMAGE_CLASS_RANGED: { if (pVictim) { - crit_chance = GetUnitCriticalChance(attackType, pVictim); - crit_chance+= GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask); + crit_chance += GetUnitCriticalChance(attackType, pVictim); + crit_chance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask); } break; } @@ -9801,16 +10042,24 @@ uint32 Unit::SpellHealingBonus(Unit *pVictim, SpellEntry const *spellProto, uint // Check for table values SpellBonusEntry const* bonus = !scripted ? spellmgr.GetSpellBonusData(spellProto->Id) : NULL; - float coeff; + float coeff = 0; float factorMod = 1.0f; if (bonus) { if (damagetype == DOT) + { coeff = bonus->dot_damage; + if (bonus->ap_dot_bonus > 0) + DoneTotal+=bonus->ap_dot_bonus * stack * GetTotalAttackPowerValue( + (IsRangedWeaponSpell(spellProto) && spellProto->DmgClass !=SPELL_DAMAGE_CLASS_MELEE)? RANGED_ATTACK : BASE_ATTACK); + } else + { coeff = bonus->direct_damage; - if (bonus->ap_bonus) - DoneTotal+=bonus->ap_bonus * GetTotalAttackPowerValue(BASE_ATTACK) * stack; + if (bonus->ap_bonus > 0) + DoneTotal+=bonus->ap_bonus * stack * GetTotalAttackPowerValue( + (IsRangedWeaponSpell(spellProto) && spellProto->DmgClass !=SPELL_DAMAGE_CLASS_MELEE)? RANGED_ATTACK : BASE_ATTACK); + } } else // scripted bonus { @@ -9836,6 +10085,7 @@ uint32 Unit::SpellHealingBonus(Unit *pVictim, SpellEntry const *spellProto, uint // No heal coeff for SPELL_DAMAGE_CLASS_NONE class spells by default else if (scripted || spellProto->DmgClass == SPELL_DAMAGE_CLASS_NONE) { + scripted = true; coeff = 0.0f; } } @@ -9843,7 +10093,7 @@ uint32 Unit::SpellHealingBonus(Unit *pVictim, SpellEntry const *spellProto, uint // Default calculation if (DoneAdvertisedBenefit || TakenAdvertisedBenefit) { - if(!bonus && !scripted) + if((!bonus && !scripted) || coeff < 0) { // Damage Done from spell damage bonus int32 CastingTime = !IsChanneledSpell(spellProto) ? GetSpellCastTime(spellProto) : GetSpellDuration(spellProto); @@ -10204,6 +10454,9 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT } } } + // This is not a typo - Impurity has SPELLFAMILY_DRUID + if (AuraEffect const * aurEff = GetDummyAura(SPELLFAMILY_DRUID, 1986, 0)) + APbonus *= (100.0f + aurEff->GetAmount()) / 100.0f; DoneFlatBenefit += int32(APbonus/14.0f * GetAPMultiplier(attType,normalized)); } @@ -10238,6 +10491,22 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT if((*i)->GetMiscValue() & GetMeleeDamageSchoolMask()) TakenTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; + // .. taken pct (special attacks) + if (spellProto) + { + // Mod damage from spell mechanic + uint32 mechanicMask = GetAllSpellMechanicMask(spellProto); + if (mechanicMask) + { + AuraEffectList const& mDamageDoneMechanic = pVictim->GetAurasByType(SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT); + for(AuraEffectList::const_iterator i = mDamageDoneMechanic.begin();i != mDamageDoneMechanic.end(); ++i) + if((mechanicMask & uint32(1<<((*i)->GetMiscValue()))) + // Shred - "Effects which increase Bleed damage also increase Shred damage" + || ((*i)->GetMiscValue() == MECHANIC_BLEED && spellProto->SpellFamilyName == SPELLFAMILY_DRUID && spellProto->SpellFamilyFlags[0] & 0x8000)) + TakenTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; + } + } + // .. taken pct: dummy auras AuraEffectList const& mDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY); for(AuraEffectList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i) @@ -10256,14 +10525,6 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT TakenTotalMod *= (mod+100.0f)/100.0f; } break; - //Mangle - case 2312: - if(spellProto==NULL) - break; - // Should increase Shred (initial Damage of Lacerate and Rake handled in Spell::EffectSchoolDMG) - if(spellProto->SpellFamilyName==SPELLFAMILY_DRUID && spellProto->SpellFamilyFlags.IsEqual (0x00008000,0,0)) - TakenTotalMod *= (100.0f+(*i)->GetAmount())/100.0f; - break; } } @@ -10394,8 +10655,7 @@ float Unit::GetPPMProcChance(uint32 WeaponSpeed, float PPM, const SpellEntry * s if(Player* modOwner = GetSpellModOwner()) modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_PROC_PER_MINUTE,PPM); - uint32 result = uint32((WeaponSpeed * PPM) / 600.0f); // result is chance in percents (probability = Speed_in_sec * (PPM / 60)) - return result; + return uint32((WeaponSpeed * PPM) / 600.0f); // result is chance in percents (probability = Speed_in_sec * (PPM / 60)) } void Unit::Mount(uint32 mount) @@ -10471,20 +10731,22 @@ void Unit::SetInCombatWith(Unit* enemy) SetInCombatState(false,enemy); } -void Unit::CombatStart(Unit* target) +void Unit::CombatStart(Unit* target, bool initialAggro) { - if(!target->IsStandState()/* && !target->hasUnitState(UNIT_STAT_STUNNED)*/) - target->SetStandState(UNIT_STAND_STATE_STAND); - - if(!target->isInCombat() && target->GetTypeId() != TYPEID_PLAYER - && !((Creature*)target)->HasReactState(REACT_PASSIVE) && ((Creature*)target)->IsAIEnabled) + if (initialAggro) { - ((Creature*)target)->AI()->AttackStart(this); - } + if(!target->IsStandState()/* && !target->hasUnitState(UNIT_STAT_STUNNED)*/) + target->SetStandState(UNIT_STAND_STATE_STAND); - SetInCombatWith(target); - target->SetInCombatWith(this); + if(!target->isInCombat() && target->GetTypeId() != TYPEID_PLAYER + && !((Creature*)target)->HasReactState(REACT_PASSIVE) && ((Creature*)target)->IsAIEnabled) + { + ((Creature*)target)->AI()->AttackStart(this); + } + SetInCombatWith(target); + target->SetInCombatWith(this); + } Unit *who = target->GetCharmerOrOwnerOrSelf(); if(who->GetTypeId() == TYPEID_PLAYER) SetContestedPvP((Player*)who); @@ -10515,8 +10777,10 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy) if(GetTypeId() != TYPEID_PLAYER) { - //if(GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_IDLE) != IDLE_MOTION_TYPE) - ((Creature*)this)->SetHomePosition(GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); + // Set home position at place of engaging combat for escorted creatures + if(((Creature*)this)->IsAIEnabled) + if (((Creature *)this)->AI()->IsEscorted()) + ((Creature*)this)->SetHomePosition(GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); if(enemy) { if(((Creature*)this)->IsAIEnabled) @@ -10581,6 +10845,9 @@ bool Unit::canAttack(Unit const* target, bool force) const else if(!IsHostileTo(target)) return false; + //if(m_Vehicle && m_Vehicle == target->m_Vehicle) + // return true; + if(!target->isAttackableByAOE() || target->hasUnitState(UNIT_STAT_DIED)) return false; @@ -10782,6 +11049,7 @@ bool Unit::canDetectStealthOf(Unit const* target, float distance) const //-Stealth Mod(positive like Master of Deception) and Stealth Detection(negative like paranoia) //based on wowwiki every 5 mod we have 1 more level diff in calculation visibleDistance += (float)(GetTotalAuraModifier(SPELL_AURA_MOD_DETECT) - target->GetTotalAuraModifier(SPELL_AURA_MOD_STEALTH_LEVEL)) / 5.0f; + visibleDistance = visibleDistance > MAX_PLAYER_STEALTH_DETECT_RANGE ? MAX_PLAYER_STEALTH_DETECT_RANGE : visibleDistance; return distance < visibleDistance; } @@ -10823,6 +11091,11 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced) switch(mtype) { + // Only apply debuffs + case MOVE_FLIGHT_BACK: + case MOVE_RUN_BACK: + case MOVE_SWIM_BACK: + break; case MOVE_WALK: return; case MOVE_RUN: @@ -10841,15 +11114,11 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced) } break; } - case MOVE_RUN_BACK: - return; case MOVE_SWIM: { main_speed_mod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_SWIM_SPEED); break; } - case MOVE_SWIM_BACK: - return; case MOVE_FLIGHT: { if (GetTypeId()==TYPEID_UNIT && IsControlledByPlayer()) // not sure if good for pet @@ -10881,14 +11150,13 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced) vehicle->UpdateSpeed(MOVE_FLIGHT, true); break; } - case MOVE_FLIGHT_BACK: - return; default: sLog.outError("Unit::UpdateSpeed: Unsupported move type (%d)", mtype); return; } float bonus = non_stack_bonus > stack_bonus ? non_stack_bonus : stack_bonus; + // now we ready for speed calculation float speed = main_speed_mod ? bonus*(100.0f + main_speed_mod)/100.0f : bonus; @@ -10898,6 +11166,10 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced) case MOVE_SWIM: case MOVE_FLIGHT: { + // Set creature speed rate from CreatureInfo + if (GetTypeId() == TYPEID_UNIT) + speed *= ((Creature*)this)->GetCreatureInfo()->speed; + // Normalize speed by 191 aura SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED if need // TODO: possible affect only on MOVE_RUN if(int32 normalization = GetMaxPositiveAuraModifier(SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED)) @@ -11089,6 +11361,8 @@ void Unit::setDeathState(DeathState s) if (m_deathState != ALIVE && s == ALIVE) { //_ApplyAllAuraMods(); + // Reset display id on resurection - needed by corpse explosion to cleanup after display change + SetDisplayId(GetNativeDisplayId()); } m_deathState = s; } @@ -11116,8 +11390,8 @@ bool Unit::CanHaveThreatList() const //if( ((Creature*)this)->isVehicle() ) // return false; - // pets can not have a threat list, unless they are controlled by a creature - if( ((Creature*)this)->isPet() && IS_PLAYER_GUID(((Pet*)this)->GetOwnerGUID()) ) + // summons can not have a threat list, unless they are controlled by a creature + if( ((Creature*)this)->HasSummonMask(SUMMON_MASK_MINION | SUMMON_MASK_GUARDIAN | SUMMON_MASK_CONTROLABLE_GUARDIAN) && IS_PLAYER_GUID(((Pet*)this)->GetOwnerGUID()) ) return false; return true; @@ -11148,6 +11422,8 @@ void Unit::AddThreat(Unit* pVictim, float threat, SpellSchoolMask schoolMask, Sp void Unit::DeleteThreatList() { + if(CanHaveThreatList() && !m_ThreatManager.isThreatListEmpty()) + SendClearThreatListOpcode(); m_ThreatManager.clearReferences(); } @@ -11222,10 +11498,6 @@ Unit* Creature::SelectVictim() //next-victim-selection algorithm and evade mode are called //threat list sorting etc. - //This should not be called by unit who does not have a threatlist - //or who does not have threat (totem/pet/critter) - //otherwise enterevademode every update - Unit* target = NULL; // First checking if we have some taunt on us const AuraEffectList& tauntAuras = GetAurasByType(SPELL_AURA_MOD_TAUNT); @@ -11258,9 +11530,38 @@ Unit* Creature::SelectVictim() target = getVictim(); } - if ( !target && !m_ThreatManager.isThreatListEmpty() ) - // No taunt aura or taunt aura caster is dead standart target selection - target = m_ThreatManager.getHostilTarget(); + if (CanHaveThreatList()) + { + if ( !target && !m_ThreatManager.isThreatListEmpty() ) + // No taunt aura or taunt aura caster is dead standart target selection + target = m_ThreatManager.getHostilTarget(); + } + else if(!HasReactState(REACT_PASSIVE)) + { + // We have player pet probably + target = getAttackerForHelper(); + if (!target && isSummon()) + { + if (Unit * owner = ((TempSummon*)this)->GetOwner()) + { + if (owner->isInCombat()) + target = owner->getAttackerForHelper(); + if (!target) + { + for(ControlList::const_iterator itr = owner->m_Controlled.begin(); itr != owner->m_Controlled.end(); ++itr) + { + if ((*itr)->isInCombat()) + { + target = (*itr)->getAttackerForHelper(); + if (target) break; + } + } + } + } + } + } + else + return NULL; if(target) { @@ -11273,16 +11574,11 @@ Unit* Creature::SelectVictim() // it in combat but attacker not make any damage and not enter to aggro radius to have record in threat list // for example at owner command to pet attack some far away creature // Note: creature not have targeted movement generator but have attacker in this case - if(m_attackers.size()) - return NULL; - /*if( GetMotionMaster()->GetCurrentMovementGeneratorType() != TARGETED_MOTION_TYPE ) + for(AttackerSet::const_iterator itr = m_attackers.begin(); itr != m_attackers.end(); ++itr) { - for(AttackerSet::const_iterator itr = m_attackers.begin(); itr != m_attackers.end(); ++itr) - { - if( (*itr)->IsInMap(this) && canAttack(*itr) && (*itr)->isInAccessiblePlaceFor((Creature*)this) ) - return NULL; - } - }*/ + if( (*itr)->IsInMap(this) && canAttack(*itr) && (*itr)->isInAccessiblePlaceFor((Creature*)this) && ((*itr)->GetTypeId() != TYPEID_PLAYER && (!((Creature*)(*itr))->HasSummonMask(SUMMON_MASK_CONTROLABLE_GUARDIAN)))) + return NULL; + } // search nearby enemy before enter evade mode if(HasReactState(REACT_AGGRESSIVE)) @@ -11368,8 +11664,8 @@ int32 Unit::CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_inde spellProto->EffectApplyAuraName[effect_index] != SPELL_AURA_MOD_INCREASE_SPEED && spellProto->EffectApplyAuraName[effect_index] != SPELL_AURA_MOD_DECREASE_SPEED) //there are many more: slow speed, -healing pct - //value = int32(value*0.25f*exp(getLevel()*(70-spellProto->spellLevel)/1000.0f)); - value = int32(value * (int32)getLevel() / (int32)(spellProto->spellLevel ? spellProto->spellLevel : 1)); + value = int32(value*0.25f*exp(getLevel()*(70-spellProto->spellLevel)/1000.0f)); + //value = int32(value * (int32)getLevel() / (int32)(spellProto->spellLevel ? spellProto->spellLevel : 1)); return value; } @@ -11509,29 +11805,29 @@ void Unit::IncrDiminishing(DiminishingGroup group) m_Diminishing.push_back(DiminishingReturn(group,getMSTime(),DIMINISHING_LEVEL_2)); } -void Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration,Unit* caster,DiminishingLevels Level) +void Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration,Unit* caster,DiminishingLevels Level, int32 limitduration) { if(duration == -1 || group == DIMINISHING_NONE || caster->IsFriendlyTo(this) ) return; - // test pet/charm masters instead pets/charmeds + // test pet/charm masters instead pets/charmeds Unit const* targetOwner = GetCharmerOrOwner(); Unit const* casterOwner = caster->GetCharmerOrOwner(); // Duration of crowd control abilities on pvp target is limited by 10 sec. (2.2.0) - if(duration > 10000 && IsDiminishingReturnsGroupDurationLimited(group)) + if(limitduration > 0 && duration > limitduration) { Unit const* target = targetOwner ? targetOwner : this; Unit const* source = casterOwner ? casterOwner : caster; if(target->GetTypeId() == TYPEID_PLAYER && source->GetTypeId() == TYPEID_PLAYER) - duration = 10000; + duration = limitduration; } float mod = 1.0f; // Some diminishings applies to mobs too (for example, Stun) - if((GetDiminishingReturnsGroupType(group) == DRTYPE_PLAYER && (targetOwner ? targetOwner->GetTypeId():GetTypeId()) == TYPEID_PLAYER) || GetDiminishingReturnsGroupType(group) == DRTYPE_ALL) + if((GetDiminishingReturnsGroupType(group) == DRTYPE_PLAYER && (targetOwner ? (targetOwner->GetTypeId() == TYPEID_PLAYER) : (GetTypeId() == TYPEID_PLAYER))) || GetDiminishingReturnsGroupType(group) == DRTYPE_ALL) { DiminishingLevels diminish = Level; switch(diminish) @@ -11884,6 +12180,8 @@ void Unit::SetHealth(uint32 val) void Unit::SetMaxHealth(uint32 val) { + if(!val) val = 1; + uint32 health = GetHealth(); SetUInt32Value(UNIT_FIELD_MAXHEALTH, val); @@ -12066,7 +12364,7 @@ void Unit::RemoveFromWorld() if(m_NotifyListPos >= 0) { - GetMap()->RemoveUnitFromNotify(m_NotifyListPos); + GetMap()->RemoveUnitFromNotify(this, m_NotifyListPos); m_NotifyListPos = -1; } @@ -12148,11 +12446,9 @@ void Unit::DeleteCharmInfo() CharmInfo::CharmInfo(Unit* unit) : m_unit(unit), m_CommandState(COMMAND_FOLLOW), m_petnumber(0), m_barInit(false) { - for(uint8 i =0; i<MAX_SPELL_CHARM; ++i) - { - m_charmspells[i].spellId = 0; - m_charmspells[i].active = ACT_DISABLED; - } + for(uint8 i = 0; i < MAX_SPELL_CHARM; ++i) + m_charmspells[i].SetActionAndType(0,ACT_DISABLED); + if(m_unit->GetTypeId() == TYPEID_UNIT) { m_oldReactState = ((Creature*)m_unit)->GetReactState(); @@ -12229,18 +12525,21 @@ void CharmInfo::InitCharmCreateSpells() if(spellInfo && spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_DEAD) spellId = 0; - m_charmspells[x].spellId = spellId; - if(!spellId) + { + m_charmspells[x].SetActionAndType(spellId,ACT_DISABLED); continue; + } if (IsPassiveSpell(spellId)) { m_unit->CastSpell(m_unit, spellId, true); - m_charmspells[x].active = ACT_PASSIVE; + m_charmspells[x].SetActionAndType(spellId,ACT_PASSIVE); } else { + m_charmspells[x].SetActionAndType(spellId,ACT_DISABLED); + ActiveStates newstate; if(spellInfo) { @@ -12275,11 +12574,11 @@ bool CharmInfo::AddSpellToActionBar(uint32 spell_id, ActiveStates newstate) // new spell rank can be already listed for(uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i) { - if (PetActionBar[i].SpellOrAction && PetActionBar[i].IsActionBarForSpell()) + if (uint32 action = PetActionBar[i].GetAction()) { - if (spellmgr.GetFirstSpellInChain(PetActionBar[i].SpellOrAction) == first_id) + if (PetActionBar[i].IsActionBarForSpell() && spellmgr.GetFirstSpellInChain(action) == first_id) { - PetActionBar[i].SpellOrAction = spell_id; + PetActionBar[i].SetAction(spell_id); return true; } } @@ -12288,7 +12587,7 @@ bool CharmInfo::AddSpellToActionBar(uint32 spell_id, ActiveStates newstate) // or use empty slot in other case for(uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i) { - if (!PetActionBar[i].SpellOrAction && PetActionBar[i].IsActionBarForSpell()) + if (!PetActionBar[i].GetAction() && PetActionBar[i].IsActionBarForSpell()) { SetActionBar(i,spell_id,newstate == ACT_DECIDE ? IsAutocastableSpell(spell_id) ? ACT_DISABLED : ACT_PASSIVE : newstate); return true; @@ -12303,9 +12602,9 @@ bool CharmInfo::RemoveSpellFromActionBar(uint32 spell_id) for(uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i) { - if (PetActionBar[i].SpellOrAction && PetActionBar[i].IsActionBarForSpell()) + if (uint32 action = PetActionBar[i].GetAction()) { - if (spellmgr.GetFirstSpellInChain(PetActionBar[i].SpellOrAction) == first_id) + if (PetActionBar[i].IsActionBarForSpell() && spellmgr.GetFirstSpellInChain(action) == first_id) { SetActionBar(i,0,ACT_PASSIVE); return true; @@ -12322,12 +12621,8 @@ void CharmInfo::ToggleCreatureAutocast(uint32 spellid, bool apply) return; for(uint32 x = 0; x < MAX_SPELL_CHARM; ++x) - { - if(spellid == m_charmspells[x].spellId) - { - m_charmspells[x].active = apply ? ACT_ENABLED : ACT_DISABLED; - } - } + if(spellid == m_charmspells[x].GetAction()) + m_charmspells[x].SetType(apply ? ACT_ENABLED : ACT_DISABLED); } void CharmInfo::SetPetNumber(uint32 petnumber, bool statwindow) @@ -12353,17 +12648,19 @@ void CharmInfo::LoadPetActionBar(const std::string& data ) for(iter = tokens.begin(), index = ACTION_BAR_INDEX_PET_SPELL_START; index < ACTION_BAR_INDEX_PET_SPELL_END; ++iter, ++index ) { // use unsigned cast to avoid sign negative format use at long-> ActiveStates (int) conversion - PetActionBar[index].Type = atol((*iter).c_str()); + uint8 type = atol((*iter).c_str()); ++iter; - PetActionBar[index].SpellOrAction = atol((*iter).c_str()); + uint32 action = atol((*iter).c_str()); + + PetActionBar[index].SetActionAndType(action,ActiveStates(type)); // check correctness if(PetActionBar[index].IsActionBarForSpell()) { - if(!sSpellStore.LookupEntry(PetActionBar[index].SpellOrAction)) + if(!sSpellStore.LookupEntry(PetActionBar[index].GetAction())) SetActionBar(index,0,ACT_PASSIVE); - else if(!IsAutocastableSpell(PetActionBar[index].SpellOrAction)) - SetActionBar(index,PetActionBar[index].SpellOrAction,ACT_PASSIVE); + else if(!IsAutocastableSpell(PetActionBar[index].GetAction())) + SetActionBar(index,PetActionBar[index].GetAction(),ACT_PASSIVE); } } } @@ -12371,19 +12668,16 @@ void CharmInfo::LoadPetActionBar(const std::string& data ) void CharmInfo::BuildActionBar( WorldPacket* data ) { for(uint32 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i) - { - *data << uint16(PetActionBar[i].SpellOrAction); - *data << uint16(PetActionBar[i].Type); - } + *data << uint32(PetActionBar[i].packedData); } void CharmInfo::SetSpellAutocast( uint32 spell_id, bool state ) { for(uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i) { - if(spell_id == PetActionBar[i].SpellOrAction && PetActionBar[i].IsActionBarForSpell()) + if(spell_id == PetActionBar[i].GetAction() && PetActionBar[i].IsActionBarForSpell()) { - PetActionBar[i].Type = state ? ACT_ENABLED : ACT_DISABLED; + PetActionBar[i].SetType(state ? ACT_ENABLED : ACT_DISABLED); break; } } @@ -12427,8 +12721,10 @@ bool InitTriggerAuraData() isTriggerAura[SPELL_AURA_MOD_DAMAGE_DONE] = true; isTriggerAura[SPELL_AURA_MOD_DAMAGE_TAKEN] = true; isTriggerAura[SPELL_AURA_MOD_RESISTANCE] = true; - isTriggerAura[SPELL_AURA_MOD_STEALTH] = true; // Aura not have charges but need remove him on trigger + isTriggerAura[SPELL_AURA_MOD_STEALTH] = true; + isTriggerAura[SPELL_AURA_MOD_FEAR] = true; // Aura not have charges but need remove him on trigger isTriggerAura[SPELL_AURA_MOD_ROOT] = true; + isTriggerAura[SPELL_AURA_TRANSFORM] = true; isTriggerAura[SPELL_AURA_REFLECT_SPELLS] = true; isTriggerAura[SPELL_AURA_DAMAGE_IMMUNITY] = true; isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL] = true; @@ -12499,8 +12795,11 @@ uint32 createProcExtendMask(SpellNonMeleeDamage *damageInfo, SpellMissInfo missC void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage , SpellEntry const * procAura) { - // For melee/ranged based attack need update skills and set some Aura states - if (procFlag & MELEE_BASED_TRIGGER_MASK) + // Player is loaded now - do not allow passive spell casts to proc + if (GetTypeId() == TYPEID_PLAYER && ((Player*)this)->GetSession()->PlayerLoading()) + return; + // For melee/ranged based attack need update skills and set some Aura states if victim present + if (procFlag & MELEE_BASED_TRIGGER_MASK && pTarget) { // Update skills here for players if (GetTypeId() == TYPEID_PLAYER) @@ -12616,6 +12915,9 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag if (GetTypeId() == TYPEID_PLAYER && i->spellProcEvent && i->spellProcEvent->cooldown) cooldown = i->spellProcEvent->cooldown; + if (spellInfo->AttributesEx3 & SPELL_ATTR_EX3_DISABLE_PROC) + SetCantProc(true); + // This bool is needed till separate aura effect procs are still here bool handled = false; if (HandleAuraProc(pTarget, damage, i->aura, procSpell, procFlag, procExtra, cooldown, &handled)) @@ -12640,8 +12942,8 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag { sLog.outDebug("ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); // Don`t drop charge or add cooldown for not started trigger - if (!HandleProcTriggerSpell(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) - continue; + if (HandleProcTriggerSpell(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + takeCharges=true; break; } case SPELL_AURA_PROC_TRIGGER_DAMAGE: @@ -12653,53 +12955,64 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag DealDamageMods(damageInfo.target,damageInfo.damage,&damageInfo.absorb); SendSpellNonMeleeDamageLog(&damageInfo); DealSpellDamage(&damageInfo, true); + takeCharges=true; break; } case SPELL_AURA_MANA_SHIELD: case SPELL_AURA_DUMMY: { sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - if (!HandleDummyAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) - continue; - if (procDebug & 1) - sLog.outError("Dummy aura of spell %d procs twice from one effect!",spellInfo->Id); - procDebug |= 1; + if (HandleDummyAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + { + takeCharges=true; + if (procDebug & 1) + sLog.outError("Dummy aura of spell %d procs twice from one effect!",spellInfo->Id); + procDebug |= 1; + } break; } case SPELL_AURA_OBS_MOD_ENERGY: sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - if (!HandleObsModEnergyAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) - continue; - if (procDebug & 2) - sLog.outError("ObsModEnergy aura of spell %d procs twice from one effect!",spellInfo->Id); - procDebug |= 2; + if (HandleObsModEnergyAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + { + takeCharges=true; + if (procDebug & 2) + sLog.outError("ObsModEnergy aura of spell %d procs twice from one effect!",spellInfo->Id); + procDebug |= 2; + } break; case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN: sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - if (!HandleModDamagePctTakenAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) - continue; - if (procDebug & 16) - sLog.outError("ModDamagePctTaken aura of spell %d procs twice from one effect!",spellInfo->Id); - procDebug |= 16; + if (HandleModDamagePctTakenAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + { + takeCharges=true; + if (procDebug & 16) + sLog.outError("ModDamagePctTaken aura of spell %d procs twice from one effect!",spellInfo->Id); + procDebug |= 16; + } break; case SPELL_AURA_MOD_HASTE: { sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s haste aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - if (!HandleHasteAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) - continue; - if (procDebug & 4) - sLog.outError("Haste aura of spell %d procs twice from one effect!",spellInfo->Id); - procDebug |= 4; + if (HandleHasteAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + { + takeCharges=true; + if (procDebug & 4) + sLog.outError("Haste aura of spell %d procs twice from one effect!",spellInfo->Id); + procDebug |= 4; + } break; } case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS: { sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - if (!HandleOverrideClassScriptAuraProc(pTarget, damage, triggeredByAura, procSpell, cooldown)) - continue; - if (procDebug & 8) - sLog.outError("OverrideClassScripts aura of spell %d procs twice from one effect!",spellInfo->Id); - procDebug |= 8; + if (HandleOverrideClassScriptAuraProc(pTarget, damage, triggeredByAura, procSpell, cooldown)) + { + takeCharges=true; + if (procDebug & 8) + sLog.outError("OverrideClassScripts aura of spell %d procs twice from one effect!",spellInfo->Id); + procDebug |= 8; + } break; } case SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE: @@ -12708,6 +13021,7 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag (isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId()); HandleAuraRaidProcFromChargeWithValue(triggeredByAura); + takeCharges=true; break; } case SPELL_AURA_RAID_PROC_FROM_CHARGE: @@ -12716,68 +13030,92 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag (isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId()); HandleAuraRaidProcFromCharge(triggeredByAura); + takeCharges=true; break; } case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE: { sLog.outDebug("ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - if (!HandleProcTriggerSpell(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) - continue; + if (HandleProcTriggerSpell(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + takeCharges=true; break; } case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK: // Skip melee hits or instant cast spells - if (procSpell == NULL || GetSpellCastTime(procSpell) == 0) - continue; + if (procSpell && GetSpellCastTime(procSpell) != 0) + takeCharges=true; break; case SPELL_AURA_REFLECT_SPELLS_SCHOOL: // Skip Melee hits and spells ws wrong school - if (procSpell == NULL || (triggeredByAura->GetMiscValue() & procSpell->SchoolMask) == 0) - continue; + if (procSpell && (triggeredByAura->GetMiscValue() & procSpell->SchoolMask)) // School check + takeCharges=true; break; case SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT: case SPELL_AURA_MOD_POWER_COST_SCHOOL: // Skip melee hits and spells ws wrong school or zero cost - if (procSpell == NULL || - (procSpell->manaCost == 0 && procSpell->ManaCostPercentage == 0) || // Cost check + if (procSpell && + (procSpell->manaCost != 0 || procSpell->ManaCostPercentage != 0) && // Cost check (triggeredByAura->GetMiscValue() & procSpell->SchoolMask) == 0) // School check - continue; + takeCharges=true; break; case SPELL_AURA_MECHANIC_IMMUNITY: // Compare mechanic - if (procSpell==NULL || procSpell->Mechanic != triggeredByAura->GetMiscValue()) - continue; + if (procSpell && procSpell->Mechanic == triggeredByAura->GetMiscValue()) + takeCharges=true; break; case SPELL_AURA_MOD_MECHANIC_RESISTANCE: // Compare mechanic - if (procSpell==NULL || procSpell->Mechanic != triggeredByAura->GetMiscValue()) - continue; + if (procSpell && procSpell->Mechanic == triggeredByAura->GetMiscValue()) + takeCharges=true; break; case SPELL_AURA_MOD_DAMAGE_FROM_CASTER: // Compare casters - if (triggeredByAura->GetCasterGUID() != pTarget->GetGUID()) - continue; + if (triggeredByAura->GetCasterGUID() == pTarget->GetGUID()) + takeCharges=true; break; case SPELL_AURA_MOD_SPELL_CRIT_CHANCE: - if (!procSpell) - continue; + if (procSpell) + takeCharges=true; break; - /*case SPELL_AURA_ADD_FLAT_MODIFIER: - case SPELL_AURA_ADD_PCT_MODIFIER: + // CC Auras which use their amount amount to drop + // Are there any more auras which need this? + case SPELL_AURA_MOD_CONFUSE: + case SPELL_AURA_MOD_FEAR: + case SPELL_AURA_MOD_STUN: + case SPELL_AURA_MOD_ROOT: + case SPELL_AURA_TRANSFORM: + if (isVictim && damage) + { + // Damage is dealt after proc system - lets ignore auras which wasn't updated yet + // to make spell not remove its own aura + if (i->aura->GetAuraDuration() == i->aura->GetAuraMaxDuration()) + break; + int32 damageLeft = triggeredByAura->GetAmount(); + // No damage left + if (damageLeft < damage ) + RemoveAura(i->aura); + else + triggeredByAura->SetAmount(damageLeft-damage); + } + break; + //case SPELL_AURA_ADD_FLAT_MODIFIER: + //case SPELL_AURA_ADD_PCT_MODIFIER: // HandleSpellModAuraProc - break;*/ + //break; default: // nothing do, just charges counter + takeCharges=true; break; } - takeCharges=true; } // Remove charge (aura can be removed by triggers) if(useCharges && takeCharges) { i->aura->DropAuraCharge(); } + if (spellInfo->AttributesEx3 & SPELL_ATTR_EX3_DISABLE_PROC) + SetCantProc(false); } // Cleanup proc requirements @@ -12845,33 +13183,6 @@ void Unit::SendPetTalk (uint32 pettalk) ((Player*)owner)->GetSession()->SendPacket(&data); } -void Unit::SendPetSpellCooldown (uint32 spellid, time_t cooltime) -{ - Unit* owner = GetOwner(); - if(!owner || owner->GetTypeId() != TYPEID_PLAYER) - return; - - WorldPacket data(SMSG_SPELL_COOLDOWN, 8+1+4+4); - data << uint64(GetGUID()); - data << uint8(0x0); // flags (0x1, 0x2) - data << uint32(spellid); - data << uint32(cooltime); - - ((Player*)owner)->GetSession()->SendPacket(&data); -} - -void Unit::SendPetClearCooldown (uint32 spellid) -{ - Unit* owner = GetOwner(); - if(!owner || owner->GetTypeId() != TYPEID_PLAYER) - return; - - WorldPacket data(SMSG_CLEAR_COOLDOWN, 4+8); - data << uint32(spellid); - data << uint64(GetGUID()); - ((Player*)owner)->GetSession()->SendPacket(&data); -} - void Unit::SendPetAIReaction(uint64 guid) { Unit* owner = GetOwner(); @@ -13312,21 +13623,13 @@ float Unit::GetAPMultiplier(WeaponAttackType attType, bool normalized) } } -AuraEffect* Unit::GetDummyAura( uint32 spell_id ) const -{ - Unit::AuraEffectList const& mDummy = GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraEffectList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr) - if ((*itr)->GetId() == spell_id) - return *itr; - - return NULL; -} - -AuraEffect* Unit::GetDummyAura(SpellFamilyNames name, uint32 iconId) const +AuraEffect* Unit::GetAuraEffect(AuraType type, SpellFamilyNames name, uint32 iconId, uint8 effIndex) const { - Unit::AuraEffectList const& mDummy = GetAurasByType(SPELL_AURA_DUMMY); + Unit::AuraEffectList const& mDummy = GetAurasByType(type); for(Unit::AuraEffectList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr) { + if (effIndex != (*itr)->GetEffIndex()) + continue; SpellEntry const * spell = (*itr)->GetSpellProto(); if (spell->SpellIconID == iconId && spell->SpellFamilyName == name && !spell->SpellFamilyFlags) @@ -13368,12 +13671,6 @@ void Unit::AddPetAura(PetAura const* petSpell) if(GetTypeId() != TYPEID_PLAYER) return; - // Aura already added - not need to add it twice - // This check is to prevent existing pet having aura applied twice (passive auras can stack) - // if aura has more than 1 dummy effect - if (m_petAuras.find(petSpell)!= m_petAuras.end()) - return; - m_petAuras.insert(petSpell); if(Pet* pet = ((Player*)this)->GetPet()) pet->CastPetAura(petSpell); @@ -13445,15 +13742,15 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit *pVictim, Aura * aura, SpellEntry co if (!EventProcFlag) return false; - // Additional checks for triggered spells - if (procExtra & PROC_EX_INTERNAL_TRIGGERED) + // Additional checks for triggered spells (ignore trap casts) + if (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_ON_TRAP_ACTIVATION)) { if (!(spellProto->AttributesEx3 & SPELL_ATTR_EX3_CAN_PROC_TRIGGERED)) return false; } // Check spellProcEvent data requirements - if(!spellmgr.IsSpellProcEventCanTriggeredBy(spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra)) + if(!spellmgr.IsSpellProcEventCanTriggeredBy(spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active)) return false; // In most cases req get honor or XP from kill if (EventProcFlag & PROC_FLAG_KILL && GetTypeId() == TYPEID_PLAYER) @@ -13465,7 +13762,7 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit *pVictim, Aura * aura, SpellEntry co if (!allow) return false; } - // Aura added by spell can`t trogger from self (prevent drop charges/do triggers) + // Aura added by spell can`t trigger from self (prevent drop charges/do triggers) // But except periodic and kill triggers (can triggered from self) if(procSpell && procSpell->Id == spellProto->Id && !(spellProto->procFlags&(PROC_FLAG_ON_TAKE_PERIODIC | PROC_FLAG_KILL))) @@ -13504,10 +13801,18 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit *pVictim, Aura * aura, SpellEntry co if(spellProcEvent && spellProcEvent->customChance) chance = spellProcEvent->customChance; // If PPM exist calculate chance from PPM - if(!isVictim && spellProcEvent && spellProcEvent->ppmRate != 0) + if(spellProcEvent && spellProcEvent->ppmRate != 0) { - uint32 WeaponSpeed = GetAttackTime(attType); - chance = GetPPMProcChance(WeaponSpeed, spellProcEvent->ppmRate, spellProto); + if(!isVictim) + { + uint32 WeaponSpeed = GetAttackTime(attType); + chance = GetPPMProcChance(WeaponSpeed, spellProcEvent->ppmRate, spellProto); + } + else + { + uint32 WeaponSpeed = pVictim->GetAttackTime(attType); + chance = pVictim->GetPPMProcChance(WeaponSpeed, spellProcEvent->ppmRate, spellProto); + } } // Apply chance modifer aura if(Player* modOwner = GetSpellModOwner()) @@ -13583,7 +13888,7 @@ bool Unit::HandleAuraRaidProcFromCharge( AuraEffect* triggeredByAura ) damageSpellId=43594; break; default: - sLog.outDebug("Unit::HandleAuraRaidProcFromCharge, received not handled spell: %u", spellProto->Id); + sLog.outError("Unit::HandleAuraRaidProcFromCharge, received not handled spell: %u", spellProto->Id); return false; } @@ -13664,6 +13969,9 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss) player->ProcDamageAndSpell(pVictim, PROC_FLAG_NONE, PROC_FLAG_KILLED,PROC_EX_NONE, 0); } + // Proc auras on death - must be before aura/combat remove + pVictim->ProcDamageAndSpell(NULL, PROC_FLAG_DEATH, PROC_FLAG_NONE, PROC_EX_NONE, 0, BASE_ATTACK, 0); + // if talent known but not triggered (check priest class for speedup check) bool SpiritOfRedemption = false; if(pVictim->GetTypeId()==TYPEID_PLAYER && pVictim->getClass()==CLASS_PRIEST ) @@ -13705,10 +14013,10 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss) ((Player*)pVictim)->SetPvPDeath(player!=NULL); // only if not player and not controlled by player pet. And not at BG - if (durabilityLoss && !player && !((Player*)pVictim)->InBattleGround()) + if ( (durabilityLoss && !player && !((Player*)pVictim)->InBattleGround()) || ( player && sWorld.getConfig(CONFIG_DURABILITY_LOSS_IN_PVP) ) ) { - DEBUG_LOG("We are dead, loosing 10 percents durability"); - ((Player*)pVictim)->DurabilityLossAll(0.10f,false); + DEBUG_LOG("We are dead, losing %u percent durability", sWorld.getRate(RATE_DURABILITY_LOSS_ON_DEATH)); + ((Player*)pVictim)->DurabilityLossAll(sWorld.getRate(RATE_DURABILITY_LOSS_ON_DEATH),false); // durability lost message WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 0); ((Player*)pVictim)->GetSession()->SendPacket(&data); @@ -13912,27 +14220,22 @@ void Unit::SetRooted(bool apply) { AddUnitMovementFlag(MOVEMENTFLAG_ROOT); - if(GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_FORCE_MOVE_ROOT, 10); - data.append(GetPackGUID()); - data << (uint32)2; - SendMessageToSet(&data,true); - } - else + WorldPacket data(SMSG_FORCE_MOVE_ROOT, 10); + data.append(GetPackGUID()); + data << (uint32)2; + SendMessageToSet(&data,true); + + if(GetTypeId() != TYPEID_PLAYER) ((Creature *)this)->StopMoving(); } else { if(!hasUnitState(UNIT_STAT_STUNNED)) // prevent allow move if have also stun effect { - if(GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_FORCE_MOVE_UNROOT, 10); - data.append(GetPackGUID()); - data << (uint32)2; - SendMessageToSet(&data,true); - } + WorldPacket data(SMSG_FORCE_MOVE_UNROOT, 10); + data.append(GetPackGUID()); + data << (uint32)2; + SendMessageToSet(&data,true); RemoveUnitMovementFlag(MOVEMENTFLAG_ROOT); } @@ -13990,22 +14293,28 @@ void Unit::SetConfused(bool apply) ((Player*)this)->SetClientControl(this, !apply); } -void Unit::SetCharmedBy(Unit* charmer, CharmType type) +bool Unit::SetCharmedBy(Unit* charmer, CharmType type) { if(!charmer) - return; + return false; assert(type != CHARM_TYPE_POSSESS || charmer->GetTypeId() == TYPEID_PLAYER); assert(type != CHARM_TYPE_VEHICLE || GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isVehicle()); + sLog.outDebug("SetCharmedBy: charmer %u, charmed %u, type %u.", charmer->GetEntry(), GetEntry(), (uint32)type); + if(this == charmer) - return; + return false; - if(hasUnitState(UNIT_STAT_UNATTACKABLE)) - return; + //if(hasUnitState(UNIT_STAT_UNATTACKABLE)) + // return false; if(GetTypeId() == TYPEID_PLAYER && ((Player*)this)->GetTransport()) - return; + return false; + + // Already charmed + if(GetCharmerGUID()) + return false; CastStop(); CombatStop(); //TODO: CombatStop(true) may cause crash (interrupt spells) @@ -14027,7 +14336,7 @@ void Unit::SetCharmedBy(Unit* charmer, CharmType type) // StopCastingCharm may remove a possessed pet? if(!IsInWorld()) - return; + return false; // Set charmed setFaction(charmer->getFaction()); @@ -14096,7 +14405,8 @@ void Unit::SetCharmedBy(Unit* charmer, CharmType type) case CHARM_TYPE_CONVERT: break; } - } + } + return true; } void Unit::RemoveCharmedBy(Unit *charmer) @@ -14106,8 +14416,13 @@ void Unit::RemoveCharmedBy(Unit *charmer) if(!charmer) charmer = GetCharmer(); - else if(charmer != GetCharmer()) // one aura overrides another? + if(charmer != GetCharmer()) // one aura overrides another? + { +// sLog.outCrash("Unit::RemoveCharmedBy: this: " UI64FMTD " true charmer: " UI64FMTD " false charmer: " UI64FMTD, +// GetGUID(), GetCharmerGUID(), charmer->GetGUID()); +// assert(false); return; + } CharmType type; if(hasUnitState(UNIT_STAT_POSSESSED)) @@ -14525,6 +14840,12 @@ float Unit::MeleeSpellMissChance(const Unit *pVictim, WeaponAttackType attType, void Unit::SetPhaseMask(uint32 newPhaseMask, bool update) { + if(newPhaseMask==GetPhaseMask()) + return; + + if(IsInWorld()) + RemoveNotOwnSingleTargetAuras(newPhaseMask); // we can lost access to caster or target + WorldObject::SetPhaseMask(newPhaseMask,update); if(!IsInWorld()) @@ -14626,7 +14947,6 @@ void Unit::EnterVehicle(Vehicle *vehicle, int8 seatId) return; } - addUnitState(UNIT_STAT_ONVEHICLE); SetControlled(true, UNIT_STAT_ROOT); //movementInfo is set in AddPassenger //packets are sent in AddPassenger @@ -14671,7 +14991,6 @@ void Unit::ExitVehicle() Vehicle *vehicle = m_Vehicle; m_Vehicle = NULL; - clearUnitState(UNIT_STAT_ONVEHICLE); SetControlled(false, UNIT_STAT_ROOT); RemoveUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); @@ -14682,6 +15001,8 @@ void Unit::ExitVehicle() m_movementInfo.t_time = 0; m_movementInfo.t_seat = 0; + Relocate(vehicle->GetPositionX(), vehicle->GetPositionY(), vehicle->GetPositionZ(), GetOrientation()); + //Send leave vehicle, not correct if(GetTypeId() == TYPEID_PLAYER) { @@ -14830,3 +15151,89 @@ void Unit::NearTeleportTo( float x, float y, float z, float orientation, bool ca //SendMessageToSet(&data, false); } } + +void Unit::SendThreatListUpdate() +{ + if (uint32 count = getThreatManager().getThreatList().size()) + { + sLog.outDebug( "WORLD: Send SMSG_THREAT_UPDATE Message" ); + WorldPacket data(SMSG_THREAT_UPDATE, 8 + count * 8); + data.append(GetPackGUID()); + data << uint32(count); + std::list<HostilReference*>& tlist = getThreatManager().getThreatList(); + for (std::list<HostilReference*>::const_iterator itr = tlist.begin(); itr != tlist.end(); ++itr) + { + data.appendPackGUID((*itr)->getUnitGuid()); + data << uint32((*itr)->getThreat()); + } + SendMessageToSet(&data, false); + } +} + + +void Unit::SendChangeCurrentVictimOpcode(HostilReference* pHostilReference) +{ + if (uint32 count = getThreatManager().getThreatList().size()) + { + sLog.outDebug( "WORLD: Send SMSG_HIGHEST_THREAT_UPDATE Message" ); + WorldPacket data(SMSG_HIGHEST_THREAT_UPDATE, 8 + 8 + count * 8); + data.append(GetPackGUID()); + data.appendPackGUID(pHostilReference->getUnitGuid()); + data << uint32(count); + std::list<HostilReference*>& tlist = getThreatManager().getThreatList(); + for (std::list<HostilReference*>::const_iterator itr = tlist.begin(); itr != tlist.end(); ++itr) + { + data.appendPackGUID((*itr)->getUnitGuid()); + data << uint32((*itr)->getThreat()); + } + SendMessageToSet(&data, false); + } +} + +void Unit::SendClearThreatListOpcode() +{ + sLog.outDebug( "WORLD: Send SMSG_THREAT_CLEAR Message" ); + WorldPacket data(SMSG_THREAT_CLEAR, 8); + data.append(GetPackGUID()); + SendMessageToSet(&data, false); +} + +void Unit::SendRemoveFromThreatListOpcode(HostilReference* pHostilReference) +{ + sLog.outDebug( "WORLD: Send SMSG_THREAT_REMOVE Message" ); + WorldPacket data(SMSG_THREAT_REMOVE, 8 + 8); + data.append(GetPackGUID()); + data.appendPackGUID(pHostilReference->getUnitGuid()); + SendMessageToSet(&data, false); +} + +void Unit::RewardRage( uint32 damage, uint32 weaponSpeedHitFactor, bool attacker ) +{ + float addRage; + + float rageconversion = ((0.0091107836 * getLevel()*getLevel())+3.225598133*getLevel())+4.2652911; + + // Unknown if correct, but lineary adjust rage conversion above level 70 + if (getLevel() > 70) + rageconversion += 13.27f*(getLevel()-70); + + 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 *= 2.0; + } + + addRage *= sWorld.getRate(RATE_POWER_RAGE_INCOME); + + ModifyPower(POWER_RAGE, uint32(addRage*10)); +} diff --git a/src/game/Unit.h b/src/game/Unit.h index 03a02f0c512..0c96f6a17e6 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -56,6 +56,7 @@ enum SpellChannelInterruptFlags CHANNEL_FLAG_MOVEMENT = 0x0008, CHANNEL_FLAG_TURNING = 0x0010, CHANNEL_FLAG_DAMAGE2 = 0x0080, + CHANNEL_FLAG_ONLY_IN_WATER = 0x0100, CHANNEL_FLAG_DELAY = 0x4000 }; @@ -196,6 +197,7 @@ enum ShapeshiftForm FORM_AMBIENT = 0x06, FORM_GHOUL = 0x07, FORM_DIREBEAR = 0x08, + FORM_SHADOW_DANCE = 0x0D, FORM_CREATUREBEAR = 0x0E, FORM_CREATURECAT = 0x0F, FORM_GHOSTWOLF = 0x10, @@ -205,6 +207,7 @@ enum ShapeshiftForm FORM_TEST = 0x14, FORM_ZOMBIE = 0x15, FORM_METAMORPHOSIS = 0x16, + FORM_UNDEAD = 0x19, FORM_FLIGHT_EPIC = 0x1B, FORM_SHADOW = 0x1C, FORM_FLIGHT = 0x1D, @@ -580,6 +583,7 @@ enum UnitFlags2 UNIT_FLAG2_FEIGN_DEATH = 0x00000001, UNIT_FLAG2_UNK1 = 0x00000002, // Hide unit model (show only player equip) UNIT_FLAG2_COMPREHEND_LANG = 0x00000008, + UNIT_FLAG2_MIRROR_IMAGE = 0x00000010, UNIT_FLAG2_FORCE_MOVE = 0x00000040, UNIT_FLAG2_DISARM_OFFHAND = 0x00000080, UNIT_FLAG2_DISARM_RANGED = 0x00000400, //this does not disable ranged weapon display (maybe additional flag needed?) @@ -768,10 +772,12 @@ enum MeleeHitOutcome struct CleanDamage { - CleanDamage(uint32 _damage, WeaponAttackType _attackType, MeleeHitOutcome _hitOutCome) : - damage(_damage), attackType(_attackType), hitOutCome(_hitOutCome) {} + CleanDamage(uint32 mitigated, uint32 absorbed, WeaponAttackType _attackType, MeleeHitOutcome _hitOutCome) : + mitigated_damage(mitigated), absorbed_damage(absorbed), attackType(_attackType), hitOutCome(_hitOutCome) {} + + uint32 absorbed_damage; + uint32 mitigated_damage; - uint32 damage; WeaponAttackType attackType; MeleeHitOutcome hitOutCome; }; @@ -823,8 +829,8 @@ struct SpellNonMeleeDamage{ struct SpellPeriodicAuraLogInfo { - SpellPeriodicAuraLogInfo(AuraEffect *_auraEff, uint32 _damage, uint32 _overDamage, uint32 _absorb, uint32 _resist, float _multiplier) - : auraEff(_auraEff), damage(_damage), overDamage(_overDamage), absorb(_absorb), resist(_resist), multiplier(_multiplier) {} + SpellPeriodicAuraLogInfo(AuraEffect *_auraEff, uint32 _damage, uint32 _overDamage, uint32 _absorb, uint32 _resist, float _multiplier, bool _critical) + : auraEff(_auraEff), damage(_damage), overDamage(_overDamage), absorb(_absorb), resist(_resist), multiplier(_multiplier), critical(_critical){} AuraEffect *auraEff; uint32 damage; @@ -832,6 +838,7 @@ struct SpellPeriodicAuraLogInfo uint32 resist; uint32 overDamage; // overkill/overheal float multiplier; + bool critical; }; uint32 createProcExtendMask(SpellNonMeleeDamage *damageInfo, SpellMissInfo missCondition); @@ -855,12 +862,12 @@ enum CurrentSpellTypes enum ActiveStates { - ACT_PASSIVE = 0x0100, // 0x0100 - passive - ACT_DISABLED = 0x8100, // 0x8000 - castable - ACT_ENABLED = 0xC100, // 0x4000 | 0x8000 - auto cast + castable - ACT_COMMAND = 0x0700, // 0x0100 | 0x0200 | 0x0400 - ACT_REACTION = 0x0600, // 0x0200 | 0x0400 - ACT_DECIDE = 0x0001 // what is it? + ACT_PASSIVE = 0x01, // 0x01 - passive + ACT_DISABLED = 0x81, // 0x80 - castable + ACT_ENABLED = 0xC1, // 0x40 | 0x80 - auto cast + castable + ACT_COMMAND = 0x07, // 0x01 | 0x02 | 0x04 + ACT_REACTION = 0x06, // 0x02 | 0x04 + ACT_DECIDE = 0x00 // custom }; enum ReactStates @@ -878,24 +885,40 @@ enum CommandStates COMMAND_ABANDON = 3 }; +#define UNIT_ACTION_BUTTON_ACTION(X) (uint32(X) & 0x00FFFFFF) +#define UNIT_ACTION_BUTTON_TYPE(X) ((uint32(X) & 0xFF000000) >> 24) +#define MAX_UNIT_ACTION_BUTTON_ACTION_VALUE (0x00FFFFFF+1) +#define MAKE_UNIT_ACTION_BUTTON(A,T) (uint32(A) | (uint32(T) << 24)) + struct UnitActionBarEntry { - UnitActionBarEntry() : SpellOrAction(0), Type(ACT_DISABLED) {} + UnitActionBarEntry() : packedData(uint32(ACT_DISABLED) << 24) {} - uint16 SpellOrAction; - uint16 Type; + uint32 packedData; // helper + ActiveStates GetType() const { return ActiveStates(UNIT_ACTION_BUTTON_TYPE(packedData)); } + uint32 GetAction() const { return UNIT_ACTION_BUTTON_ACTION(packedData); } bool IsActionBarForSpell() const { + ActiveStates Type = GetType(); return Type == ACT_DISABLED || Type == ACT_ENABLED || Type == ACT_PASSIVE; } -}; -struct CharmSpellEntry -{ - uint16 spellId; - uint16 active; + void SetActionAndType(uint32 action, ActiveStates type) + { + packedData = MAKE_UNIT_ACTION_BUTTON(action,type); + } + + void SetType(ActiveStates type) + { + packedData = MAKE_UNIT_ACTION_BUTTON(UNIT_ACTION_BUTTON_ACTION(packedData),type); + } + + void SetAction(uint32 action) + { + packedData = (packedData & 0xFF000000) | UNIT_ACTION_BUTTON_ACTION(action); + } }; typedef std::list<Player*> SharedVisionList; @@ -908,6 +931,8 @@ enum CharmType CHARM_TYPE_CONVERT, }; +typedef UnitActionBarEntry CharmSpellEntry; + enum ActionBarIndex { ACTION_BAR_INDEX_START = 0, @@ -946,8 +971,7 @@ struct CharmInfo void SetSpellAutocast(uint32 spell_id, bool state); void SetActionBar(uint8 index, uint32 spellOrAction,ActiveStates type) { - PetActionBar[index].Type = type; - PetActionBar[index].SpellOrAction = spellOrAction; + PetActionBar[index].SetActionAndType(spellOrAction,type); } UnitActionBarEntry const* GetActionBarEntry(uint8 index) const { return &(PetActionBar[index]); } @@ -988,6 +1012,7 @@ enum ReactiveType // delay time next attack to prevent client attack animation problems #define ATTACK_DISPLAY_DELAY 200 +#define MAX_PLAYER_STEALTH_DETECT_RANGE 45.0f // max distance for detection targets by player struct SpellProcEventEntry; // used only privately @@ -998,6 +1023,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject typedef std::set<Unit*> ControlList; typedef std::pair<uint32, uint8> spellEffectPair; typedef std::multimap<uint32, Aura*> AuraMap; + typedef std::multimap<AuraState, Aura*> AuraStateAurasMap; typedef std::list<AuraEffect *> AuraEffectList; typedef std::list<Aura *> AuraList; typedef std::list<DiminishingReturn> Diminishing; @@ -1014,7 +1040,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject DiminishingLevels GetDiminishing(DiminishingGroup group); void IncrDiminishing(DiminishingGroup group); - void ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration,Unit* caster, DiminishingLevels Level); + void ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration,Unit* caster, DiminishingLevels Level, int32 limitduration); void ApplyDiminishingAura(DiminishingGroup group, bool apply); void ClearDiminishings() { m_Diminishing.clear(); } @@ -1177,7 +1203,6 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void Unmount(); uint16 GetMaxSkillValueForLevel(Unit const* target = NULL) const { return (target ? getLevelForTarget(target) : getLevel()) * 5; } - void RemoveSpellbyDamageTaken(uint32 damage, uint32 spell); void DealDamageMods(Unit *pVictim, uint32 &damage, uint32* absorb); uint32 DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage = NULL, DamageEffectType damagetype = DIRECT_DAMAGE, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellEntry const *spellProto = NULL, bool durabilityLoss = true); void Kill(Unit *pVictim, bool durabilityLoss = true); @@ -1258,7 +1283,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject bool isInFlight() const { return hasUnitState(UNIT_STAT_IN_FLIGHT); } bool isInCombat() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); } - void CombatStart(Unit* target); + void CombatStart(Unit* target, bool initialAggro = true); void SetInCombatState(bool PvP, Unit* enemy = NULL); void SetInCombatWith(Unit* enemy); void ClearInCombat(); @@ -1288,7 +1313,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject uint32 SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage); void CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem = NULL, AuraEffect* triggeredByAura = NULL, uint64 originalCaster = 0); void CastSpell(Unit* Victim, SpellEntry const *spellInfo, bool triggered, Item *castItem= NULL, AuraEffect* triggeredByAura = NULL, uint64 originalCaster = 0); - void CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem = NULL, AuraEffect* triggeredByAura = NULL, uint64 originalCaster = 0); + void CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem = NULL, AuraEffect* triggeredByAura = NULL, uint64 originalCaster = 0, Unit* originalVictim = 0); void CastCustomSpell(Unit* Victim, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem= NULL, AuraEffect* triggeredByAura = NULL, uint64 originalCaster = 0); void CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* Victim = NULL, bool triggered = true, Item *castItem = NULL, AuraEffect* triggeredByAura = NULL, uint64 originalCaster = 0); void CastCustomSpell(uint32 spellId, CustomSpellValues const &value, Unit* Victim = NULL, bool triggered = true, Item *castItem = NULL, AuraEffect* triggeredByAura = NULL, uint64 originalCaster = 0); @@ -1325,6 +1350,11 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void SendMonsterMoveWithSpeedToCurrentDestination(Player* player = NULL); void SendMovementFlagUpdate(); + void SendChangeCurrentVictimOpcode(HostilReference* pHostilReference); + void SendClearThreatListOpcode(); + void SendRemoveFromThreatListOpcode(HostilReference* pHostilReference); + void SendThreatListUpdate(); + void BuildHeartBeatMsg(WorldPacket *data) const; void OutMovementInfo() const; @@ -1374,7 +1404,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void SetMinion(Minion *minion, bool apply); void SetCharm(Unit* target, bool apply); Unit* GetNextRandomRaidMemberOrPet(float radius); - void SetCharmedBy(Unit* charmer, CharmType type); + bool SetCharmedBy(Unit* charmer, CharmType type); void RemoveCharmedBy(Unit* charmer); void RestoreFaction(); @@ -1420,11 +1450,15 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void RemoveAurasDueToItemSpell(Item* castItem,uint32 spellId); void RemoveAurasByType(AuraType auraType, uint64 casterGUID = 0, Aura * except=NULL, bool negative = true, bool positive = true); void RemoveAurasByTypeWithDispel(AuraType auraType, Spell * spell = NULL); - void RemoveNotOwnSingleTargetAuras(); + void RemoveNotOwnSingleTargetAuras(uint32 newPhase = 0x0); + + void RemoveSpellsCausingAura(AuraType auraType); + void RemoveRankAurasDueToSpell(uint32 spellId); bool RemoveNoStackAurasDueToAura(Aura *Aur); void RemoveAurasWithInterruptFlags(uint32 flag, uint32 except = NULL); void RemoveAurasWithFamily(uint32 family, uint32 familyFlag1, uint32 familyFlag2, uint32 familyFlag3, uint64 casterGUID); void RemoveMovementImpairingAuras(); + void RemoveAurasWithMechanic(uint32 mechanic_mask, uint32 except=0); void RemoveAllAuras(); void RemoveArenaAuras(bool onleave = false); void RemoveAllAurasOnDeath(); @@ -1469,6 +1503,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid = 0, bool withInstant = true); Spell* FindCurrentSpellBySpellId(uint32 spell_id) const; + int32 GetCurrentSpellCastTime(uint32 spell_id) const; Spell* m_currentSpells[CURRENT_MAX_SPELL]; @@ -1572,14 +1607,15 @@ class TRINITY_DLL_SPEC Unit : public WorldObject AuraEffect * GetAuraEffect(uint32 spellId, uint8 effIndex, uint64 casterGUID = 0) const; Aura * GetAura(uint32 spellId, uint64 casterGUID = 0) const; - AuraEffect* GetAura(AuraType type, uint32 family, uint32 familyFlag1 , uint32 familyFlag2=0, uint32 familyFlag3=0, uint64 casterGUID=0); + AuraEffect * GetAura(AuraType type, uint32 family, uint32 familyFlag1 , uint32 familyFlag2=0, uint32 familyFlag3=0, uint64 casterGUID=0); + AuraEffect * IsScriptOverriden(SpellEntry const * spell, int32 script) const; bool HasAuraEffect(uint32 spellId, uint8 effIndex, uint64 caster = 0) const; bool HasAura(uint32 spellId, uint64 caster = 0) const; bool HasAura(Aura * aur) const; bool HasAuraType(AuraType auraType) const; bool HasAuraTypeWithMiscvalue(AuraType auratype, uint32 miscvalue) const; - AuraEffect* GetDummyAura(uint32 spell_id) const; - AuraEffect* GetDummyAura(SpellFamilyNames name, uint32 iconId) const; + inline AuraEffect* GetDummyAura(SpellFamilyNames name, uint32 iconId, uint8 effIndex) const { return GetAuraEffect(SPELL_AURA_DUMMY, name, iconId, effIndex);} + AuraEffect* GetAuraEffect(AuraType type, SpellFamilyNames name, uint32 iconId, uint8 effIndex) const; uint32 GetDiseasesByCaster(uint64 casterGUID, bool remove = false); uint32 GetDoTsByCaster(uint64 casterGUID) const; @@ -1625,6 +1661,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject uint32 CalculateDamage(WeaponAttackType attType, bool normalized); float GetAPMultiplier(WeaponAttackType attType, bool normalized); void ModifyAuraState(AuraState flag, bool apply); + uint32 BuildAuraStateUpdateForTarget(Unit * target) const; bool HasAuraState(AuraState flag, SpellEntry const *spellProto = NULL, Unit * Caster = NULL) const ; void UnsummonAllTotems(); Unit* SelectMagnetTarget(Unit *victim, SpellEntry const *spellInfo = NULL); @@ -1705,8 +1742,6 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void SendPetCastFail(uint32 spellid, SpellCastResult msg); void SendPetActionFeedback (uint8 msg); void SendPetTalk (uint32 pettalk); - void SendPetSpellCooldown (uint32 spellid, time_t cooltime); - void SendPetClearCooldown (uint32 spellid); void SendPetAIReaction(uint64 guid); ///----------End of Pet responses methods---------- @@ -1780,6 +1815,10 @@ class TRINITY_DLL_SPEC Unit : public WorldObject bool canFly() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FLY_MODE); } bool IsFlying() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FLYING); } void SetFlying(bool apply); + + void RewardRage( uint32 damage, uint32 weaponSpeedHitFactor, bool attacker ); + + virtual float GetFollowAngle() const { return M_PI/2; } protected: explicit Unit (); @@ -1819,14 +1858,13 @@ class TRINITY_DLL_SPEC Unit : public WorldObject AuraEffectList m_modAuras[TOTAL_AURAS]; AuraList m_scAuras; // casted singlecast auras AuraList m_interruptableAuras; - AuraList m_ccAuras; AuraList m_removedAuras; + AuraStateAurasMap m_auraStateAuras; // Used for improve performance of aura state checks on aura apply/remove uint32 m_interruptMask; float m_auraModifiersGroup[UNIT_MOD_END][MODIFIER_TYPE_END]; float m_weaponDamage[MAX_ATTACK][2]; bool m_canModifyStats; - //std::list< spellEffectPair > AuraSpells[TOTAL_AURAS]; // TODO: use this if ok for mem VisibleAuraMap m_visibleAuras; float m_speed_rate[MAX_MOVE_TYPE]; diff --git a/src/game/UnitAI.cpp b/src/game/UnitAI.cpp index c159861697c..a41ad894097 100644 --- a/src/game/UnitAI.cpp +++ b/src/game/UnitAI.cpp @@ -325,6 +325,10 @@ void UnitAI::FillAISpellInfo() } } } + AIInfo->realCooldown = spellInfo->RecoveryTime + spellInfo->StartRecoveryTime; + SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); + if (srange) + AIInfo->maxRange = srange->maxRangeHostile * 3 / 4; } } @@ -348,7 +352,7 @@ void SimpleCharmedAI::UpdateAI(const uint32 /*diff*/) } if(!charmer->isInCombat()) - me->GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + me->GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, me->GetFollowAngle()); Unit *target = me->getVictim(); if(!target || !charmer->canAttack(target)) diff --git a/src/game/UnitAI.h b/src/game/UnitAI.h index 9530405ed4d..3e6ce4f4a6f 100644 --- a/src/game/UnitAI.h +++ b/src/game/UnitAI.h @@ -23,6 +23,7 @@ #include "Platform/Define.h" #include <list> +#include "Unit.h" class Unit; class Player; @@ -47,7 +48,7 @@ class TRINITY_DLL_SPEC UnitAI virtual void AttackStart(Unit *); virtual void UpdateAI(const uint32 diff) = 0; - virtual void InitializeAI() { Reset(); } + virtual void InitializeAI() { if(!me->isDead()) Reset(); } virtual void Reset() {}; diff --git a/src/game/UpdateData.cpp b/src/game/UpdateData.cpp index a3192b74b0e..e461d63e248 100644 --- a/src/game/UpdateData.cpp +++ b/src/game/UpdateData.cpp @@ -128,7 +128,7 @@ bool UpdateData::BuildPacket(WorldPacket *packet) if (pSize > 100 ) // compress large packets { - uint32 destsize = pSize; + uint32 destsize = compressBound(pSize); packet->resize( destsize + sizeof(uint32) ); packet->put<uint32>(0, pSize); diff --git a/src/game/Vehicle.cpp b/src/game/Vehicle.cpp index 0aa0d48a24c..3cad7963d0b 100644 --- a/src/game/Vehicle.cpp +++ b/src/game/Vehicle.cpp @@ -65,9 +65,9 @@ void Vehicle::AddToWorld() } } + Unit::AddToWorld(); InstallAllAccessories(); - Unit::AddToWorld(); AIM_Initialize(); } } @@ -90,9 +90,11 @@ void Vehicle::InstallAllAccessories() InstallAccessory(33139,7); break; case 33114: - InstallAccessory(33143,1); - //InstallAccessory(33142,0); - InstallAccessory(33142,2); + InstallAccessory(33142,0); + //InstallAccessory(33143,1); + //InstallAccessory(33142,2); + InstallAccessory(33143,2); + InstallAccessory(33142,1); break; } } @@ -266,8 +268,7 @@ void Vehicle::InstallAccessory(uint32 entry, int8 seatId) if(!accessory) return; - accessory->m_Vehicle = this; - AddPassenger(accessory, seatId); + accessory->EnterVehicle(this, seatId); // This is not good, we have to send update twice accessory->SendMovementFlagUpdate(); } @@ -310,6 +311,9 @@ bool Vehicle::AddPassenger(Unit *unit, int8 seatId) RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); } + if(!(seat->second.seatInfo->m_flags & 0x4000)) + unit->addUnitState(UNIT_STAT_ONVEHICLE); + //SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_24); unit->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); @@ -321,11 +325,15 @@ bool Vehicle::AddPassenger(Unit *unit, int8 seatId) unit->m_movementInfo.t_time = 0; // 1 for player unit->m_movementInfo.t_seat = seat->first; - if(unit->GetTypeId() == TYPEID_PLAYER && seat->first == 0 && seat->second.seatInfo->IsUsable()) // not right - SetCharmedBy(unit, CHARM_TYPE_VEHICLE); + if(unit->GetTypeId() == TYPEID_PLAYER && seat->first == 0 && seat->second.seatInfo->m_flags & 0x800) // not right + if (!SetCharmedBy(unit, CHARM_TYPE_VEHICLE)) + assert(false); if(IsInWorld()) + { unit->SendMonsterMoveTransport(this); + GetMap()->CreatureRelocation(this, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); + } //if(unit->GetTypeId() == TYPEID_PLAYER) // ((Player*)unit)->SendTeleportAckMsg(); @@ -341,27 +349,27 @@ void Vehicle::RemovePassenger(Unit *unit) SeatMap::iterator seat; for(seat = m_Seats.begin(); seat != m_Seats.end(); ++seat) - { if(seat->second.passenger == unit) - { - seat->second.passenger = NULL; - if(seat->second.seatInfo->IsUsable()) - { - if(!m_usableSeatNum) - SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); - ++m_usableSeatNum; - } break; - } - } assert(seat != m_Seats.end()); - sLog.outDebug("Unit %s exit vehicle entry %u id %u dbguid %u", unit->GetName(), GetEntry(), m_vehicleInfo->m_ID, GetDBTableGUIDLow()); + sLog.outDebug("Unit %s exit vehicle entry %u id %u dbguid %u seat %d", unit->GetName(), GetEntry(), m_vehicleInfo->m_ID, GetDBTableGUIDLow(), (int32)seat->first); + + seat->second.passenger = NULL; + if(seat->second.seatInfo->IsUsable()) + { + if(!m_usableSeatNum) + SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); + ++m_usableSeatNum; + } + + if(!(seat->second.seatInfo->m_flags & 0x4000)) + unit->clearUnitState(UNIT_STAT_ONVEHICLE); //SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); - if(unit->GetTypeId() == TYPEID_PLAYER && seat->first == 0 && seat->second.seatInfo->IsUsable()) + if(unit->GetTypeId() == TYPEID_PLAYER && seat->first == 0 && seat->second.seatInfo->m_flags & 0x800) RemoveCharmedBy(unit); // only for flyable vehicles? @@ -382,7 +390,6 @@ void Vehicle::Dismiss() RemoveAllPassengers(); SendObjectDeSpawnAnim(GetGUID()); CombatStop(); - CleanupsBeforeDelete(); AddObjectToRemoveList(); } diff --git a/src/game/WaypointMovementGenerator.cpp b/src/game/WaypointMovementGenerator.cpp index 34be9d3179b..92fd9bca2d7 100644 --- a/src/game/WaypointMovementGenerator.cpp +++ b/src/game/WaypointMovementGenerator.cpp @@ -198,7 +198,7 @@ WaypointMovementGenerator<Creature>::Update(Creature &unit, const uint32 &diff) //note: disable "start" for mtmap if(node->event_id && rand()%100 < node->event_chance) - sWorld.ScriptsStart(sWaypointScripts, node->event_id, &unit, NULL, false); + unit.GetMap()->ScriptsStart(sWaypointScripts, node->event_id, &unit, NULL/*, false*/); MovementInform(unit); unit.UpdateWaypointID(i_currentNode); diff --git a/src/game/World.cpp b/src/game/World.cpp index 608aaec01d5..f7cc552342a 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -63,7 +63,6 @@ #include "GridNotifiersImpl.h" #include "CellImpl.h" #include "InstanceSaveMgr.h" -#include "WaypointManager.h" #include "Util.h" #include "Language.h" #include "CreatureGroups.h" @@ -83,14 +82,6 @@ float World::m_MaxVisibleDistanceInFlight = DEFAULT_VISIBILITY_DISTANCE; float World::m_VisibleUnitGreyDistance = 0; float World::m_VisibleObjectGreyDistance = 0; -struct ScriptAction -{ - uint64 sourceGUID; - uint64 targetGUID; - uint64 ownerGUID; // owner of source if source is item - ScriptInfo const* script; // pointer to static script data -}; - /// World constructor World::World() { @@ -102,8 +93,11 @@ World::World() m_startTime=m_gameTime; m_maxActiveSessionCount = 0; m_maxQueuedSessionCount = 0; + m_PlayerCount = 0; + m_MaxPlayerCount = 0; m_resultQueue = NULL; m_NextDailyQuestReset = 0; + m_scheduledScripts = 0; m_defaultDbcLocale = LOCALE_enUS; m_availableDbcLocaleMask = 0; @@ -266,7 +260,7 @@ World::AddSession_ (WorldSession* s) float popu = GetActiveSessionCount (); //updated number of users on the server popu /= pLimit; popu *= 2; - LoginDatabase.PExecute ("UPDATE realmlist SET population = '%f' WHERE id = '%d'", popu, realmID); + loginDatabase.PExecute ("UPDATE realmlist SET population = '%f' WHERE id = '%d'", popu, realmID); sLog.outDetail ("Server Population (%f).", popu); } } @@ -475,6 +469,12 @@ void World::LoadConfigSettings(bool reload) rate_values[RATE_XP_KILL] = sConfig.GetFloatDefault("Rate.XP.Kill", 1.0f); rate_values[RATE_XP_QUEST] = sConfig.GetFloatDefault("Rate.XP.Quest", 1.0f); rate_values[RATE_XP_EXPLORE] = sConfig.GetFloatDefault("Rate.XP.Explore", 1.0f); + rate_values[RATE_REPAIRCOST] = sConfig.GetFloatDefault("Rate.RepairCost", 1.0f); + if(rate_values[RATE_REPAIRCOST] < 0.0f) + { + sLog.outError("Rate.RepairCost (%f) must be >=0. Using 0.0 instead.",rate_values[RATE_REPAIRCOST]); + rate_values[RATE_REPAIRCOST] = 0.0f; + } rate_values[RATE_REPUTATION_GAIN] = sConfig.GetFloatDefault("Rate.Reputation.Gain", 1.0f); rate_values[RATE_REPUTATION_LOWLEVEL_KILL] = sConfig.GetFloatDefault("Rate.Reputation.LowLevel.Kill", 1.0f); rate_values[RATE_REPUTATION_LOWLEVEL_QUEST] = sConfig.GetFloatDefault("Rate.Reputation.LowLevel.Quest", 1.0f); @@ -526,6 +526,19 @@ void World::LoadConfigSettings(bool reload) rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] = NOMINAL_MELEE_RANGE; } + rate_values[RATE_DURABILITY_LOSS_ON_DEATH] = sConfig.GetFloatDefault("DurabilityLoss.OnDeath", 10.0f); + if(rate_values[RATE_DURABILITY_LOSS_ON_DEATH] < 0.0f) + { + sLog.outError("DurabilityLoss.OnDeath (%f) must be >=0. Using 0.0 instead.",rate_values[RATE_DURABILITY_LOSS_ON_DEATH]); + rate_values[RATE_DURABILITY_LOSS_ON_DEATH] = 0.0f; + } + if(rate_values[RATE_DURABILITY_LOSS_ON_DEATH] > 100.0f) + { + sLog.outError("DurabilityLoss.OnDeath (%f) must be <=100. Using 100.0 instead.",rate_values[RATE_DURABILITY_LOSS_ON_DEATH]); + rate_values[RATE_DURABILITY_LOSS_ON_DEATH] = 0.0f; + } + rate_values[RATE_DURABILITY_LOSS_ON_DEATH] = rate_values[RATE_DURABILITY_LOSS_ON_DEATH] / 100.0f; + rate_values[RATE_DURABILITY_LOSS_DAMAGE] = sConfig.GetFloatDefault("DurabilityLossChance.Damage",0.5f); if(rate_values[RATE_DURABILITY_LOSS_DAMAGE] < 0.0f) { @@ -553,6 +566,8 @@ void World::LoadConfigSettings(bool reload) ///- Read other configuration items from the config file + m_configs[CONFIG_DURABILITY_LOSS_IN_PVP] = sConfig.GetBoolDefault("DurabilityLoss.InPvP", false); + m_configs[CONFIG_COMPRESSION] = sConfig.GetIntDefault("Compression", 1); if(m_configs[CONFIG_COMPRESSION] < 1 || m_configs[CONFIG_COMPRESSION] > 9) { @@ -639,6 +654,27 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_STRICT_CHARTER_NAMES] = sConfig.GetIntDefault ("StrictCharterNames", 0); m_configs[CONFIG_STRICT_PET_NAMES] = sConfig.GetIntDefault ("StrictPetNames", 0); + m_configs[CONFIG_MIN_PLAYER_NAME] = sConfig.GetIntDefault ("MinPlayerName", 2); + if(m_configs[CONFIG_MIN_PLAYER_NAME] < 1 || m_configs[CONFIG_MIN_PLAYER_NAME] > MAX_PLAYER_NAME) + { + sLog.outError("MinPlayerName (%i) must be in range 1..%u. Set to 2.",m_configs[CONFIG_MIN_PLAYER_NAME],MAX_PLAYER_NAME); + m_configs[CONFIG_MIN_PLAYER_NAME] = 2; + } + + m_configs[CONFIG_MIN_CHARTER_NAME] = sConfig.GetIntDefault ("MinCharterName", 2); + if(m_configs[CONFIG_MIN_CHARTER_NAME] < 1 || m_configs[CONFIG_MIN_CHARTER_NAME] > MAX_CHARTER_NAME) + { + sLog.outError("MinCharterName (%i) must be in range 1..%u. Set to 2.",m_configs[CONFIG_MIN_CHARTER_NAME],MAX_CHARTER_NAME); + m_configs[CONFIG_MIN_CHARTER_NAME] = 2; + } + + m_configs[CONFIG_MIN_PET_NAME] = sConfig.GetIntDefault ("MinPetName", 2); + if(m_configs[CONFIG_MIN_PET_NAME] < 1 || m_configs[CONFIG_MIN_PET_NAME] > MAX_PET_NAME) + { + sLog.outError("MinPetName (%i) must be in range 1..%u. Set to 2.",m_configs[CONFIG_MIN_PET_NAME],MAX_PET_NAME); + m_configs[CONFIG_MIN_PET_NAME] = 2; + } + m_configs[CONFIG_CHARACTERS_CREATING_DISABLED] = sConfig.GetIntDefault ("CharactersCreatingDisabled", 0); m_configs[CONFIG_CHARACTERS_PER_REALM] = sConfig.GetIntDefault("CharactersPerRealm", 10); @@ -790,8 +826,9 @@ void World::LoadConfigSettings(bool reload) //m_configs[CONFIG_GM_ACCEPT_TICKETS] = sConfig.GetIntDefault("GM.AcceptTickets", 2); m_configs[CONFIG_GM_CHAT] = sConfig.GetIntDefault("GM.Chat", 2); m_configs[CONFIG_GM_WISPERING_TO] = sConfig.GetIntDefault("GM.WhisperingTo", 2); - m_configs[CONFIG_GM_IN_GM_LIST] = sConfig.GetBoolDefault("GM.InGMList", false); - m_configs[CONFIG_GM_IN_WHO_LIST] = sConfig.GetBoolDefault("GM.InWhoList", false); + + m_configs[CONFIG_GM_LEVEL_IN_GM_LIST] = sConfig.GetIntDefault("GM.InGMList.Level", SEC_ADMINISTRATOR); + m_configs[CONFIG_GM_LEVEL_IN_WHO_LIST] = sConfig.GetIntDefault("GM.InWhoList.Level", SEC_ADMINISTRATOR); m_configs[CONFIG_GM_LOG_TRADE] = sConfig.GetBoolDefault("GM.LogTrade", false); m_configs[CONFIG_START_GM_LEVEL] = sConfig.GetIntDefault("GM.StartLevel", 1); m_configs[CONFIG_ALLOW_GM_GROUP] = sConfig.GetBoolDefault("GM.AllowInvite", false); @@ -1156,7 +1193,7 @@ void World::SetInitialWorldSettings() // not send custom type REALM_FFA_PVP to realm list uint32 server_type = IsFFAPvPRealm() ? REALM_TYPE_PVP : getConfig(CONFIG_GAME_TYPE); uint32 realm_zone = getConfig(CONFIG_REALM_ZONE); - LoginDatabase.PExecute("UPDATE realmlist SET icon = %u, timezone = %u WHERE id = '%d'", server_type, realm_zone, realmID); + loginDatabase.PExecute("UPDATE realmlist SET icon = %u, timezone = %u WHERE id = '%d'", server_type, realm_zone, realmID); ///- Remove the bones after a restart CharacterDatabase.PExecute("DELETE FROM corpse WHERE corpse_type = '0'"); @@ -1340,9 +1377,6 @@ void World::SetInitialWorldSettings() sLog.outString( "Loading Spell target coordinates..." ); spellmgr.LoadSpellTargetPositions(); - sLog.outString( "Loading SpellAffect definitions..." ); - spellmgr.LoadSpellAffects(); - sLog.outString( "Loading spell pet auras..." ); spellmgr.LoadSpellPetAuras(); @@ -1514,9 +1548,11 @@ void World::SetInitialWorldSettings() sprintf( isoDate, "%04d-%02d-%02d %02d:%02d:%02d", local.tm_year+1900, local.tm_mon+1, local.tm_mday, local.tm_hour, local.tm_min, local.tm_sec); - LoginDatabase.PExecute("INSERT INTO uptime (realmid, starttime, startstring, uptime) VALUES('%u', " UI64FMTD ", '%s', 0)", + loginDatabase.PExecute("INSERT INTO uptime (realmid, starttime, startstring, uptime) VALUES('%u', " UI64FMTD ", '%s', 0)", realmID, uint64(m_startTime), isoDate); + static uint32 abtimer = 0; + abtimer = sConfig.GetIntDefault("AutoBroadcast.Timer", 60000); m_timers[WUPDATE_OBJECTS].SetInterval(IN_MILISECONDS/2); m_timers[WUPDATE_SESSIONS].SetInterval(0); m_timers[WUPDATE_WEATHERS].SetInterval(1*IN_MILISECONDS); @@ -1527,6 +1563,7 @@ void World::SetInitialWorldSettings() //erase corpses every 20 minutes m_timers[WUPDATE_CLEANDB].SetInterval(m_configs[CONFIG_LOGDB_CLEARINTERVAL]*MINUTE*IN_MILISECONDS); // clean logs table every 14 days by default + m_timers[WUPDATE_AUTOBROADCAST].SetInterval(abtimer); //to set mailtimer to return mails every day between 4 and 5 am //mailtimer is increased when updating auctions @@ -1565,7 +1602,7 @@ void World::SetInitialWorldSettings() objmgr.LoadTransportEvents(); sLog.outString("Deleting expired bans..." ); - LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); + loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); sLog.outString("Calculate next daily quest reset time..." ); InitDailyQuestResetTime(); @@ -1574,7 +1611,7 @@ void World::SetInitialWorldSettings() poolhandler.Initialize(); sLog.outString("Initialize AuctionHouseBot..."); - AuctionHouseBotInit(); + auctionbot.Initialize(); // possibly enable db logging; avoid massive startup spam by doing it here. if (sLog.GetLogDBLater()) @@ -1700,7 +1737,7 @@ void World::Update(uint32 diff) /// <ul><li> Handle auctions when the timer has passed if (m_timers[WUPDATE_AUCTIONS].Passed()) { - AuctionHouseBot(); + auctionbot.Update(); m_timers[WUPDATE_AUCTIONS].Reset(); ///- Update mails (return old mails with item, or delete them) @@ -1748,7 +1785,7 @@ void World::Update(uint32 diff) uint32 maxClientsNum = GetMaxActiveSessionCount(); m_timers[WUPDATE_UPTIME].Reset(); - LoginDatabase.PExecute("UPDATE uptime SET uptime = %u, maxplayers = %u WHERE realmid = %u AND starttime = " UI64FMTD, tmpDiff, maxClientsNum, realmID, uint64(m_startTime)); + loginDatabase.PExecute("UPDATE uptime SET uptime = %u, maxplayers = %u WHERE realmid = %u AND starttime = " UI64FMTD, tmpDiff, maxClientsNum, realmID, uint64(m_startTime)); } /// <li> Clean logs table @@ -1760,7 +1797,7 @@ void World::Update(uint32 diff) uint32 maxClientsNum = sWorld.GetMaxActiveSessionCount(); m_timers[WUPDATE_CLEANDB].Reset(); - LoginDatabase.PExecute("DELETE FROM logs WHERE (time + %u) < "UI64FMTD";", + loginDatabase.PExecute("DELETE FROM logs WHERE (time + %u) < "UI64FMTD";", sWorld.getConfig(CONFIG_LOGDB_CLEARTIME), uint64(time(0))); } } @@ -1775,12 +1812,15 @@ void World::Update(uint32 diff) MapManager::Instance().DoDelayedMovesAndRemoves(); }*/ - ///- Process necessary scripts - if (!m_scriptSchedule.empty()) + static uint32 autobroadcaston = 0; + autobroadcaston = sConfig.GetIntDefault("AutoBroadcast.On", 0); + if(autobroadcaston == 1) { - RecordTimeDiff(NULL); - ScriptsProcess(); - RecordTimeDiff("UpdateScriptsProcess"); + if (m_timers[WUPDATE_AUTOBROADCAST].Passed()) + { + m_timers[WUPDATE_AUTOBROADCAST].Reset(); + SendRNDBroadcast(); + } } sBattleGroundMgr.Update(diff); @@ -1827,831 +1867,6 @@ void World::ForceGameEventUpdate() m_timers[WUPDATE_EVENTS].Reset(); } -/// Put scripts in the execution queue -void World::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, Object* target, bool start) -{ - ///- Find the script map - ScriptMapMap::const_iterator s = scripts.find(id); - if (s == scripts.end()) - return; - - // prepare static data - uint64 sourceGUID = source ? source->GetGUID() : (uint64)0; //some script commands doesn't have source - uint64 targetGUID = target ? target->GetGUID() : (uint64)0; - uint64 ownerGUID = (source->GetTypeId()==TYPEID_ITEM) ? ((Item*)source)->GetOwnerGUID() : (uint64)0; - - ///- Schedule script execution for all scripts in the script map - ScriptMap const *s2 = &(s->second); - bool immedScript = false; - for (ScriptMap::const_iterator iter = s2->begin(); iter != s2->end(); ++iter) - { - ScriptAction sa; - sa.sourceGUID = sourceGUID; - sa.targetGUID = targetGUID; - sa.ownerGUID = ownerGUID; - - sa.script = &iter->second; - m_scriptSchedule.insert(std::pair<time_t, ScriptAction>(m_gameTime + iter->first, sa)); - if (iter->first == 0) - immedScript = true; - } - ///- If one of the effects should be immediate, launch the script execution - if (start && immedScript) - ScriptsProcess(); -} - -void World::ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target) -{ - // NOTE: script record _must_ exist until command executed - - // prepare static data - uint64 sourceGUID = source ? source->GetGUID() : (uint64)0; - uint64 targetGUID = target ? target->GetGUID() : (uint64)0; - uint64 ownerGUID = (source->GetTypeId()==TYPEID_ITEM) ? ((Item*)source)->GetOwnerGUID() : (uint64)0; - - ScriptAction sa; - sa.sourceGUID = sourceGUID; - sa.targetGUID = targetGUID; - sa.ownerGUID = ownerGUID; - - sa.script = &script; - m_scriptSchedule.insert(std::pair<time_t, ScriptAction>(m_gameTime + delay, sa)); - - ///- If effects should be immediate, launch the script execution - if(delay == 0) - ScriptsProcess(); -} - -/// Process queued scripts -void World::ScriptsProcess() -{ - if (m_scriptSchedule.empty()) - return; - - ///- Process overdue queued scripts - std::multimap<time_t, ScriptAction>::iterator iter = m_scriptSchedule.begin(); - // ok as multimap is a *sorted* associative container - while (!m_scriptSchedule.empty() && (iter->first <= m_gameTime)) - { - ScriptAction const& step = iter->second; - - Object* source = NULL; - - if(step.sourceGUID) - { - switch(GUID_HIPART(step.sourceGUID)) - { - case HIGHGUID_ITEM: - // case HIGHGUID_CONTAINER: ==HIGHGUID_ITEM - { - Player* player = HashMapHolder<Player>::Find(step.ownerGUID); - if(player) - source = player->GetItemByGuid(step.sourceGUID); - break; - } - case HIGHGUID_UNIT: - source = HashMapHolder<Creature>::Find(step.sourceGUID); - break; - case HIGHGUID_PET: - source = HashMapHolder<Pet>::Find(step.sourceGUID); - break; - case HIGHGUID_VEHICLE: - source = HashMapHolder<Vehicle>::Find(step.sourceGUID); - break; - case HIGHGUID_PLAYER: - source = HashMapHolder<Player>::Find(step.sourceGUID); - break; - case HIGHGUID_GAMEOBJECT: - source = HashMapHolder<GameObject>::Find(step.sourceGUID); - break; - case HIGHGUID_CORPSE: - source = HashMapHolder<Corpse>::Find(step.sourceGUID); - break; - case HIGHGUID_MO_TRANSPORT: - for (MapManager::TransportSet::iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().m_Transports.end(); ++iter) - { - if((*iter)->GetGUID() == step.sourceGUID) - { - source = reinterpret_cast<Object*>(*iter); - break; - } - } - break; - default: - sLog.outError("*_script source with unsupported high guid value %u",GUID_HIPART(step.sourceGUID)); - break; - } - } - - //if(source && !source->IsInWorld()) source = NULL; - - Object* target = NULL; - - if(step.targetGUID) - { - switch(GUID_HIPART(step.targetGUID)) - { - case HIGHGUID_UNIT: - target = HashMapHolder<Creature>::Find(step.targetGUID); - break; - case HIGHGUID_PET: - target = HashMapHolder<Pet>::Find(step.targetGUID); - break; - case HIGHGUID_VEHICLE: - target = HashMapHolder<Vehicle>::Find(step.targetGUID); - break; - case HIGHGUID_PLAYER: // empty GUID case also - target = HashMapHolder<Player>::Find(step.targetGUID); - break; - case HIGHGUID_GAMEOBJECT: - target = HashMapHolder<GameObject>::Find(step.targetGUID); - break; - case HIGHGUID_CORPSE: - target = HashMapHolder<Corpse>::Find(step.targetGUID); - break; - default: - sLog.outError("*_script source with unsupported high guid value %u",GUID_HIPART(step.targetGUID)); - break; - } - } - - //if(target && !target->IsInWorld()) target = NULL; - - switch (step.script->command) - { - case SCRIPT_COMMAND_TALK: - { - if(!source) - { - sLog.outError("SCRIPT_COMMAND_TALK call for NULL creature."); - break; - } - - if(source->GetTypeId()!=TYPEID_UNIT) - { - sLog.outError("SCRIPT_COMMAND_TALK call for non-creature (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - if(step.script->datalong > 3) - { - sLog.outError("SCRIPT_COMMAND_TALK invalid chat type (%u), skipping.",step.script->datalong); - break; - } - - uint64 unit_target = target ? target->GetGUID() : 0; - - //datalong 0=normal say, 1=whisper, 2=yell, 3=emote text - switch(step.script->datalong) - { - case 0: // Say - ((Creature *)source)->Say(step.script->dataint, LANG_UNIVERSAL, unit_target); - break; - case 1: // Whisper - if(!unit_target) - { - sLog.outError("SCRIPT_COMMAND_TALK attempt to whisper (%u) NULL, skipping.",step.script->datalong); - break; - } - ((Creature *)source)->Whisper(step.script->dataint,unit_target); - break; - case 2: // Yell - ((Creature *)source)->Yell(step.script->dataint, LANG_UNIVERSAL, unit_target); - break; - case 3: // Emote text - ((Creature *)source)->TextEmote(step.script->dataint, unit_target); - break; - default: - break; // must be already checked at load - } - break; - } - - case SCRIPT_COMMAND_EMOTE: - if(!source) - { - sLog.outError("SCRIPT_COMMAND_EMOTE call for NULL creature."); - break; - } - - if(source->GetTypeId()!=TYPEID_UNIT) - { - sLog.outError("SCRIPT_COMMAND_EMOTE call for non-creature (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - ((Creature *)source)->HandleEmoteCommand(step.script->datalong); - break; - case SCRIPT_COMMAND_FIELD_SET: - if(!source) - { - sLog.outError("SCRIPT_COMMAND_FIELD_SET call for NULL object."); - break; - } - if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount()) - { - sLog.outError("SCRIPT_COMMAND_FIELD_SET call for wrong field %u (max count: %u) in object (TypeId: %u).", - step.script->datalong,source->GetValuesCount(),source->GetTypeId()); - break; - } - - source->SetUInt32Value(step.script->datalong, step.script->datalong2); - break; - case SCRIPT_COMMAND_MOVE_TO: - if(!source) - { - sLog.outError("SCRIPT_COMMAND_MOVE_TO call for NULL creature."); - break; - } - - if(source->GetTypeId()!=TYPEID_UNIT) - { - sLog.outError("SCRIPT_COMMAND_MOVE_TO call for non-creature (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - ((Unit *)source)->SendMonsterMoveWithSpeed(step.script->x, step.script->y, step.script->z, step.script->datalong2 ); - ((Unit *)source)->GetMap()->CreatureRelocation(((Creature *)source), step.script->x, step.script->y, step.script->z, 0); - break; - case SCRIPT_COMMAND_FLAG_SET: - if(!source) - { - sLog.outError("SCRIPT_COMMAND_FLAG_SET call for NULL object."); - break; - } - if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount()) - { - sLog.outError("SCRIPT_COMMAND_FLAG_SET call for wrong field %u (max count: %u) in object (TypeId: %u).", - step.script->datalong,source->GetValuesCount(),source->GetTypeId()); - break; - } - - source->SetFlag(step.script->datalong, step.script->datalong2); - break; - case SCRIPT_COMMAND_FLAG_REMOVE: - if(!source) - { - sLog.outError("SCRIPT_COMMAND_FLAG_REMOVE call for NULL object."); - break; - } - if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount()) - { - sLog.outError("SCRIPT_COMMAND_FLAG_REMOVE call for wrong field %u (max count: %u) in object (TypeId: %u).", - step.script->datalong,source->GetValuesCount(),source->GetTypeId()); - break; - } - - source->RemoveFlag(step.script->datalong, step.script->datalong2); - break; - - case SCRIPT_COMMAND_TELEPORT_TO: - { - // accept player in any one from target/source arg - if (!target && !source) - { - sLog.outError("SCRIPT_COMMAND_TELEPORT_TO call for NULL object."); - break; - } - - // must be only Player - if((!target || target->GetTypeId() != TYPEID_PLAYER) && (!source || source->GetTypeId() != TYPEID_PLAYER)) - { - sLog.outError("SCRIPT_COMMAND_TELEPORT_TO call for non-player (TypeIdSource: %u)(TypeIdTarget: %u), skipping.", source ? source->GetTypeId() : 0, target ? target->GetTypeId() : 0); - break; - } - - Player* pSource = target && target->GetTypeId() == TYPEID_PLAYER ? (Player*)target : (Player*)source; - - pSource->TeleportTo(step.script->datalong, step.script->x, step.script->y, step.script->z, step.script->o); - break; - } - - case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: - { - if(!step.script->datalong) // creature not specified - { - sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for NULL creature."); - break; - } - - if(!source) - { - sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for NULL world object."); - break; - } - - WorldObject* summoner = dynamic_cast<WorldObject*>(source); - - if(!summoner) - { - sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for non-WorldObject (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - float x = step.script->x; - float y = step.script->y; - float z = step.script->z; - float o = step.script->o; - - Creature* pCreature = summoner->SummonCreature(step.script->datalong, x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,step.script->datalong2); - if (!pCreature) - { - sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON failed for creature (entry: %u).",step.script->datalong); - break; - } - - break; - } - - case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: - { - if(!step.script->datalong) // gameobject not specified - { - sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for NULL gameobject."); - break; - } - - if(!source) - { - sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for NULL world object."); - break; - } - - WorldObject* summoner = dynamic_cast<WorldObject*>(source); - - if(!summoner) - { - sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for non-WorldObject (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - GameObject *go = NULL; - int32 time_to_despawn = step.script->datalong2<5 ? 5 : (int32)step.script->datalong2; - - CellPair p(Trinity::ComputeCellPair(summoner->GetPositionX(), summoner->GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - - MaNGOS::GameObjectWithDbGUIDCheck go_check(*summoner,step.script->datalong); - MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(summoner, go,go_check); - - TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(summoner->GetMapId(), summoner)); - - if ( !go ) - { - sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT failed for gameobject(guid: %u).", step.script->datalong); - break; - } - - if( go->GetGoType()==GAMEOBJECT_TYPE_FISHINGNODE || - go->GetGoType()==GAMEOBJECT_TYPE_DOOR || - go->GetGoType()==GAMEOBJECT_TYPE_BUTTON || - go->GetGoType()==GAMEOBJECT_TYPE_TRAP ) - { - sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT can not be used with gameobject of type %u (guid: %u).", uint32(go->GetGoType()), step.script->datalong); - break; - } - - if( go->isSpawned() ) - break; //gameobject already spawned - - go->SetLootState(GO_READY); - go->SetRespawnTime(time_to_despawn); //despawn object in ? seconds - - go->GetMap()->Add(go); - break; - } - case SCRIPT_COMMAND_OPEN_DOOR: - { - if(!step.script->datalong) // door not specified - { - sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for NULL door."); - break; - } - - if(!source) - { - sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for NULL unit."); - break; - } - - if(!source->isType(TYPEMASK_UNIT)) // must be any Unit (creature or player) - { - sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for non-unit (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - Unit* caster = (Unit*)source; - - GameObject *door = NULL; - int32 time_to_close = step.script->datalong2 < 15 ? 15 : (int32)step.script->datalong2; - - CellPair p(Trinity::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - - MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong); - MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(caster,door,go_check); - - TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(caster->GetMapId(), (Unit*)source)); - - if (!door) - { - sLog.outError("SCRIPT_COMMAND_OPEN_DOOR failed for gameobject(guid: %u).", step.script->datalong); - break; - } - if (door->GetGoType() != GAMEOBJECT_TYPE_DOOR) - { - sLog.outError("SCRIPT_COMMAND_OPEN_DOOR failed for non-door(GoType: %u).", door->GetGoType()); - break; - } - - if (door->GetGoState() != GO_STATE_READY) - break; //door already open - - door->UseDoorOrButton(time_to_close); - - if(target && target->isType(TYPEMASK_GAMEOBJECT) && ((GameObject*)target)->GetGoType()==GAMEOBJECT_TYPE_BUTTON) - ((GameObject*)target)->UseDoorOrButton(time_to_close); - break; - } - case SCRIPT_COMMAND_CLOSE_DOOR: - { - if(!step.script->datalong) // guid for door not specified - { - sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for NULL door."); - break; - } - - if(!source) - { - sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for NULL unit."); - break; - } - - if(!source->isType(TYPEMASK_UNIT)) // must be any Unit (creature or player) - { - sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for non-unit (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - Unit* caster = (Unit*)source; - - GameObject *door = NULL; - int32 time_to_open = step.script->datalong2 < 15 ? 15 : (int32)step.script->datalong2; - - CellPair p(Trinity::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - - MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong); - MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(caster,door,go_check); - - TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(caster->GetMapId(), (Unit*)source)); - - if ( !door ) - { - sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR failed for gameobject(guid: %u).", step.script->datalong); - break; - } - if ( door->GetGoType() != GAMEOBJECT_TYPE_DOOR ) - { - sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR failed for non-door(GoType: %u).", door->GetGoType()); - break; - } - - if( door->GetGoState() == GO_STATE_READY ) - break; //door already closed - - door->UseDoorOrButton(time_to_open); - - if(target && target->isType(TYPEMASK_GAMEOBJECT) && ((GameObject*)target)->GetGoType()==GAMEOBJECT_TYPE_BUTTON) - ((GameObject*)target)->UseDoorOrButton(time_to_open); - - break; - } - case SCRIPT_COMMAND_QUEST_EXPLORED: - { - if(!source) - { - sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for NULL source."); - break; - } - - if(!target) - { - sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for NULL target."); - break; - } - - // when script called for item spell casting then target == (unit or GO) and source is player - WorldObject* worldObject; - Player* player; - - if(target->GetTypeId()==TYPEID_PLAYER) - { - if(source->GetTypeId()!=TYPEID_UNIT && source->GetTypeId()!=TYPEID_GAMEOBJECT) - { - sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-creature and non-gameobject (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - worldObject = (WorldObject*)source; - player = (Player*)target; - } - else - { - if(target->GetTypeId()!=TYPEID_UNIT && target->GetTypeId()!=TYPEID_GAMEOBJECT) - { - sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-creature and non-gameobject (TypeId: %u), skipping.",target->GetTypeId()); - break; - } - - if(source->GetTypeId()!=TYPEID_PLAYER) - { - sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-player(TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - worldObject = (WorldObject*)target; - player = (Player*)source; - } - - // quest id and flags checked at script loading - if( (worldObject->GetTypeId()!=TYPEID_UNIT || ((Unit*)worldObject)->isAlive()) && - (step.script->datalong2==0 || worldObject->IsWithinDistInMap(player,float(step.script->datalong2))) ) - player->AreaExploredOrEventHappens(step.script->datalong); - else - player->FailQuest(step.script->datalong); - - break; - } - - case SCRIPT_COMMAND_ACTIVATE_OBJECT: - { - if(!source) - { - sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT must have source caster."); - break; - } - - if(!source->isType(TYPEMASK_UNIT)) - { - sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT source caster isn't unit (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - if(!target) - { - sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT call for NULL gameobject."); - break; - } - - if(target->GetTypeId()!=TYPEID_GAMEOBJECT) - { - sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT call for non-gameobject (TypeId: %u), skipping.",target->GetTypeId()); - break; - } - - Unit* caster = (Unit*)source; - - GameObject *go = (GameObject*)target; - - go->Use(caster); - break; - } - - case SCRIPT_COMMAND_REMOVE_AURA: - { - Object* cmdTarget = step.script->datalong2 ? source : target; - - if(!cmdTarget) - { - sLog.outError("SCRIPT_COMMAND_REMOVE_AURA call for NULL %s.",step.script->datalong2 ? "source" : "target"); - break; - } - - if(!cmdTarget->isType(TYPEMASK_UNIT)) - { - sLog.outError("SCRIPT_COMMAND_REMOVE_AURA %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 ? "source" : "target",cmdTarget->GetTypeId()); - break; - } - - ((Unit*)cmdTarget)->RemoveAurasDueToSpell(step.script->datalong); - break; - } - - case SCRIPT_COMMAND_CAST_SPELL: - { - if(!source) - { - sLog.outError("SCRIPT_COMMAND_CAST_SPELL must have source caster."); - break; - } - - Object* cmdTarget = step.script->datalong2 & 0x01 ? source : target; - - if(!cmdTarget) - { - sLog.outError("SCRIPT_COMMAND_CAST_SPELL call for NULL %s.",step.script->datalong2 & 0x01 ? "source" : "target"); - break; - } - - if(!cmdTarget->isType(TYPEMASK_UNIT)) - { - sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 & 0x01 ? "source" : "target",cmdTarget->GetTypeId()); - break; - } - - Unit* spellTarget = (Unit*)cmdTarget; - - Object* cmdSource = step.script->datalong2 & 0x02 ? target : source; - - if(!cmdSource) - { - sLog.outError("SCRIPT_COMMAND_CAST_SPELL call for NULL %s.",step.script->datalong2 & 0x02 ? "target" : "source"); - break; - } - - if(!cmdSource->isType(TYPEMASK_UNIT)) - { - sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 & 0x02 ? "target" : "source", cmdSource->GetTypeId()); - break; - } - - Unit* spellSource = (Unit*)cmdSource; - - //TODO: when GO cast implemented, code below must be updated accordingly to also allow GO spell cast - spellSource->CastSpell(spellTarget,step.script->datalong,false); - - break; - } - - case SCRIPT_COMMAND_LOAD_PATH: - { - if(!source) - { - sLog.outError("SCRIPT_COMMAND_START_MOVE is tried to apply to NON-existing unit."); - break; - } - - if(!source->isType(TYPEMASK_UNIT)) - { - sLog.outError("SCRIPT_COMMAND_START_MOVE source mover isn't unit (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - if(!WaypointMgr.GetPath(step.script->datalong)) - { - sLog.outError("SCRIPT_COMMAND_START_MOVE source mover has an invallid path, skipping.", step.script->datalong2); - break; - } - - dynamic_cast<Unit*>(source)->GetMotionMaster()->MovePath(step.script->datalong, step.script->datalong2); - break; - } - - case SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT: - { - if(!step.script->datalong || !step.script->datalong2) - { - sLog.outError("SCRIPT_COMMAND_CALLSCRIPT calls invallid db_script_id or lowguid not present: skipping."); - break; - } - //our target - Creature* target = NULL; - - if(source) //using grid searcher - { - CellPair p(Trinity::ComputeCellPair(((Unit*)source)->GetPositionX(), ((Unit*)source)->GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - - //sLog.outDebug("Attempting to find Creature: Db GUID: %i", step.script->datalong); - Trinity::CreatureWithDbGUIDCheck target_check(((Unit*)source), step.script->datalong); - Trinity::CreatureSearcher<Trinity::CreatureWithDbGUIDCheck> checker(((Unit*)source), target, target_check); - - TypeContainerVisitor<Trinity::CreatureSearcher <Trinity::CreatureWithDbGUIDCheck>, GridTypeMapContainer > unit_checker(checker); - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, unit_checker, *(((Unit*)source)->GetMap())); - } - else //check hashmap holders - { - if(CreatureData const* data = objmgr.GetCreatureData(step.script->datalong)) - target = ObjectAccessor::GetObjectInWorld<Creature>(data->mapid, data->posX, data->posY, MAKE_NEW_GUID(step.script->datalong, data->id, HIGHGUID_UNIT), target); - } - //sLog.outDebug("attempting to pass target..."); - if(!target) - break; - //sLog.outDebug("target passed"); - //Lets choose our ScriptMap map - ScriptMapMap *datamap = NULL; - switch(step.script->dataint) - { - case 1://QUEST END SCRIPTMAP - datamap = &sQuestEndScripts; - break; - case 2://QUEST START SCRIPTMAP - datamap = &sQuestStartScripts; - break; - case 3://SPELLS SCRIPTMAP - datamap = &sSpellScripts; - break; - case 4://GAMEOBJECTS SCRIPTMAP - datamap = &sGameObjectScripts; - break; - case 5://EVENTS SCRIPTMAP - datamap = &sEventScripts; - break; - case 6://WAYPOINTS SCRIPTMAP - datamap = &sWaypointScripts; - break; - default: - sLog.outError("SCRIPT_COMMAND_CALLSCRIPT ERROR: no scriptmap present... ignoring"); - break; - } - //if no scriptmap present... - if(!datamap) - break; - - uint32 script_id = step.script->datalong2; - //insert script into schedule but do not start it - ScriptsStart(*datamap, script_id, target, NULL, false); - break; - } - - case SCRIPT_COMMAND_KILL: - { - if(!source || ((Creature*)source)->isDead()) - break; - - ((Creature*)source)->DealDamage(((Creature*)source), ((Creature*)source)->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - - switch(step.script->dataint) - { - case 0: break; //return false not remove corpse - case 1: ((Creature*)source)->RemoveCorpse(); break; - } - break; - } - - case SCRIPT_COMMAND_PLAY_SOUND: - { - if(!source) - { - sLog.outError("SCRIPT_COMMAND_PLAY_SOUND call for NULL creature."); - break; - } - - WorldObject* pSource = dynamic_cast<WorldObject*>(source); - if(!pSource) - { - sLog.outError("SCRIPT_COMMAND_PLAY_SOUND call for non-world object (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - // bitmask: 0/1=anyone/target, 0/2=with distance dependent - Player* pTarget = NULL; - if(step.script->datalong2 & 1) - { - if(!target) - { - sLog.outError("SCRIPT_COMMAND_PLAY_SOUND in targeted mode call for NULL target."); - break; - } - - if(target->GetTypeId()!=TYPEID_PLAYER) - { - sLog.outError("SCRIPT_COMMAND_PLAY_SOUND in targeted mode call for non-player (TypeId: %u), skipping.",target->GetTypeId()); - break; - } - - pTarget = (Player*)target; - } - - // bitmask: 0/1=anyone/target, 0/2=with distance dependent - if(step.script->datalong2 & 2) - pSource->PlayDistanceSound(step.script->datalong,pTarget); - else - pSource->PlayDirectSound(step.script->datalong,pTarget); - break; - } - default: - sLog.outError("Unknown script command %u called.",step.script->command); - break; - } - - m_scriptSchedule.erase(iter); - - iter = m_scriptSchedule.begin(); - } - return; -} - /// Send a packet to all players (except self if mentioned) void World::SendGlobalMessage(WorldPacket *packet, WorldSession *self, uint32 team) { @@ -2851,10 +2066,10 @@ void World::KickAllLess(AccountTypes sec) /// Ban an account or ban an IP address, duration will be parsed using TimeStringToSecs if it is positive, otherwise permban BanReturn World::BanAccount(BanMode mode, std::string nameOrIP, std::string duration, std::string reason, std::string author) { - LoginDatabase.escape_string(nameOrIP); - LoginDatabase.escape_string(reason); + loginDatabase.escape_string(nameOrIP); + loginDatabase.escape_string(reason); std::string safe_author=author; - LoginDatabase.escape_string(safe_author); + loginDatabase.escape_string(safe_author); uint32 duration_secs = TimeStringToSecs(duration); QueryResult *resultAccounts = NULL; //used for kicking @@ -2864,12 +2079,12 @@ BanReturn World::BanAccount(BanMode mode, std::string nameOrIP, std::string dura { case BAN_IP: //No SQL injection as strings are escaped - resultAccounts = LoginDatabase.PQuery("SELECT id FROM account WHERE last_ip = '%s'",nameOrIP.c_str()); - LoginDatabase.PExecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+%u,'%s','%s')",nameOrIP.c_str(),duration_secs,safe_author.c_str(),reason.c_str()); + resultAccounts = loginDatabase.PQuery("SELECT id FROM account WHERE last_ip = '%s'",nameOrIP.c_str()); + loginDatabase.PExecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+%u,'%s','%s')",nameOrIP.c_str(),duration_secs,safe_author.c_str(),reason.c_str()); break; case BAN_ACCOUNT: //No SQL injection as string is escaped - resultAccounts = LoginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'",nameOrIP.c_str()); + resultAccounts = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'",nameOrIP.c_str()); break; case BAN_CHARACTER: //No SQL injection as string is escaped @@ -2896,7 +2111,7 @@ BanReturn World::BanAccount(BanMode mode, std::string nameOrIP, std::string dura if(mode!=BAN_IP) { //No SQL injection as strings are escaped - LoginDatabase.PExecute("INSERT INTO account_banned VALUES ('%u', UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+%u, '%s', '%s', '1')", + loginDatabase.PExecute("INSERT INTO account_banned VALUES ('%u', UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+%u, '%s', '%s', '1')", account,duration_secs,safe_author.c_str(),reason.c_str()); } @@ -2915,8 +2130,8 @@ bool World::RemoveBanAccount(BanMode mode, std::string nameOrIP) { if (mode == BAN_IP) { - LoginDatabase.escape_string(nameOrIP); - LoginDatabase.PExecute("DELETE FROM ip_banned WHERE ip = '%s'",nameOrIP.c_str()); + loginDatabase.escape_string(nameOrIP); + loginDatabase.PExecute("DELETE FROM ip_banned WHERE ip = '%s'",nameOrIP.c_str()); } else { @@ -2930,7 +2145,7 @@ bool World::RemoveBanAccount(BanMode mode, std::string nameOrIP) return false; //NO SQL injection as account is uint32 - LoginDatabase.PExecute("UPDATE account_banned SET active = '0' WHERE id = '%u'",account); + loginDatabase.PExecute("UPDATE account_banned SET active = '0' WHERE id = '%u'",account); } return true; } @@ -3104,6 +2319,45 @@ void World::ProcessCliCommands() zprint("TC> "); } +void World::SendRNDBroadcast() +{ + std::string msg; + QueryResult *result = WorldDatabase.PQuery("SELECT `text` FROM autobroadcast AS r1 JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) FROM autobroadcast)) AS id) AS r2 WHERE r1.id >= r2.id ORDER BY r1.id ASC LIMIT 1"); // ORDER BY RAND() is bad.. look it up to see why. + + if(!result) + return; + + msg = result->Fetch()[0].GetString(); + delete result; + + static uint32 abcenter = 0; + abcenter = sConfig.GetIntDefault("AutoBroadcast.Center", 0); + if(abcenter == 0) + { + sWorld.SendWorldText(LANG_AUTO_BROADCAST, msg.c_str()); + + sLog.outString("AutoBroadcast: '%s'",msg.c_str()); + } + if(abcenter == 1) + { + WorldPacket data(SMSG_NOTIFICATION, (msg.size()+1)); + data << msg; + sWorld.SendGlobalMessage(&data); + + sLog.outString("AutoBroadcast: '%s'",msg.c_str()); + } + if(abcenter == 2) + { + sWorld.SendWorldText(LANG_AUTO_BROADCAST, msg.c_str()); + + WorldPacket data(SMSG_NOTIFICATION, (msg.size()+1)); + data << msg; + sWorld.SendGlobalMessage(&data); + + sLog.outString("AutoBroadcast: '%s'",msg.c_str()); + } +} + void World::InitResultQueue() { m_resultQueue = new SqlResultQueue; @@ -3128,8 +2382,8 @@ void World::_UpdateRealmCharCount(QueryResult *resultCharCount, uint32 accountId Field *fields = resultCharCount->Fetch(); uint32 charCount = fields[0].GetUInt32(); delete resultCharCount; - LoginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", accountId, realmID); - LoginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charCount, accountId, realmID); + loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", accountId, realmID); + loginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charCount, accountId, realmID); } } @@ -3192,7 +2446,7 @@ void World::SetPlayerLimit( int32 limit, bool needUpdate ) m_playerLimit = limit; if(db_update_need) - LoginDatabase.PExecute("UPDATE realmlist SET allowedSecurityLevel = '%u' WHERE id = '%d'",uint8(GetPlayerSecurityLimit()),realmID); + loginDatabase.PExecute("UPDATE realmlist SET allowedSecurityLevel = '%u' WHERE id = '%d'",uint8(GetPlayerSecurityLimit()),realmID); } void World::UpdateMaxSessionCounters() @@ -3203,14 +2457,14 @@ void World::UpdateMaxSessionCounters() void World::LoadDBVersion() { - QueryResult* result = WorldDatabase.Query("SELECT db_version FROM version LIMIT 1"); + QueryResult* result = WorldDatabase.Query("SELECT db_version, script_version FROM version LIMIT 1"); //QueryResult* result = WorldDatabase.Query("SELECT version, creature_ai_version FROM db_version LIMIT 1"); if(result) { Field* fields = result->Fetch(); m_DBVersion = fields[0].GetCppString(); - //m_CreatureEventAIVersion = fields[1].GetCppString(); + m_CreatureEventAIVersion = fields[1].GetCppString(); delete result; } diff --git a/src/game/World.h b/src/game/World.h index 4fbde8ccb71..0bd2abb4c9b 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -29,6 +29,7 @@ #include "Timer.h" #include "Policies/Singleton.h" #include "SharedDefines.h" +#include "ace/Atomic_Op.h" #include <map> #include <set> @@ -79,7 +80,8 @@ enum WorldTimers WUPDATE_CORPSES = 5, WUPDATE_EVENTS = 6, WUPDATE_CLEANDB = 7, - WUPDATE_COUNT = 8 + WUPDATE_AUTOBROADCAST = 8, + WUPDATE_COUNT = 9 }; /// Configuration elements @@ -112,6 +114,9 @@ enum WorldConfigs CONFIG_STRICT_PLAYER_NAMES, CONFIG_STRICT_CHARTER_NAMES, CONFIG_STRICT_PET_NAMES, + CONFIG_MIN_PLAYER_NAME, + CONFIG_MIN_CHARTER_NAME, + CONFIG_MIN_PET_NAME, CONFIG_CHARACTERS_CREATING_DISABLED, CONFIG_CHARACTERS_PER_ACCOUNT, CONFIG_CHARACTERS_PER_REALM, @@ -138,8 +143,8 @@ enum WorldConfigs CONFIG_GM_ACCEPT_TICKETS, CONFIG_GM_CHAT, CONFIG_GM_WISPERING_TO, - CONFIG_GM_IN_GM_LIST, - CONFIG_GM_IN_WHO_LIST, + CONFIG_GM_LEVEL_IN_GM_LIST, + CONFIG_GM_LEVEL_IN_WHO_LIST, CONFIG_GM_LOG_TRADE, CONFIG_START_GM_LEVEL, CONFIG_ALLOW_GM_GROUP, @@ -160,6 +165,7 @@ enum WorldConfigs CONFIG_SKILL_GAIN_DEFENSE, CONFIG_SKILL_GAIN_GATHERING, CONFIG_SKILL_GAIN_WEAPON, + CONFIG_DURABILITY_LOSS_IN_PVP, CONFIG_MAX_OVERSPEED_PINGS, CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY, CONFIG_ALWAYS_MAX_SKILL_FOR_LEVEL, @@ -275,6 +281,7 @@ enum Rates RATE_XP_KILL, RATE_XP_QUEST, RATE_XP_EXPLORE, + RATE_REPAIRCOST, RATE_REPUTATION_GAIN, RATE_REPUTATION_LOWLEVEL_KILL, RATE_REPUTATION_LOWLEVEL_QUEST, @@ -308,6 +315,7 @@ enum Rates RATE_CORPSE_DECAY_LOOTED, RATE_INSTANCE_RESET_TIME, RATE_TARGET_POS_RECALCULATION_RANGE, + RATE_DURABILITY_LOSS_ON_DEATH, RATE_DURABILITY_LOSS_DAMAGE, RATE_DURABILITY_LOSS_PARRY, RATE_DURABILITY_LOSS_ABSORB, @@ -412,6 +420,7 @@ class World WorldSession* FindSession(uint32 id) const; void AddSession(WorldSession *s); + void SendRNDBroadcast(); bool RemoveSession(uint32 id); /// Get the number of current active sessions void UpdateMaxSessionCounters(); @@ -421,8 +430,18 @@ class World /// Get the maximum number of parallel sessions on the server since last reboot uint32 GetMaxQueuedSessionCount() const { return m_maxQueuedSessionCount; } uint32 GetMaxActiveSessionCount() const { return m_maxActiveSessionCount; } - Player* FindPlayerInZone(uint32 zone); + /// Get number of players + inline uint32 GetPlayerCount() const { return m_PlayerCount; } + inline uint32 GetMaxPlayerCount() const { return m_MaxPlayerCount; } + /// Increase/Decrease number of players + inline void IncreasePlayerCount() + { + m_PlayerCount++; + m_MaxPlayerCount = std::max(m_MaxPlayerCount, m_PlayerCount); + } + inline void DecreasePlayerCount() { m_PlayerCount--; } + Player* FindPlayerInZone(uint32 zone); Weather* FindWeather(uint32 id) const; Weather* AddWeather(uint32 zone_id); void RemoveWeather(uint32 zone_id); @@ -540,9 +559,10 @@ class World BanReturn BanAccount(BanMode mode, std::string nameOrIP, std::string duration, std::string reason, std::string author); bool RemoveBanAccount(BanMode mode, std::string nameOrIP); - void ScriptsStart(std::map<uint32, std::multimap<uint32, ScriptInfo> > const& scripts, uint32 id, Object* source, Object* target, bool start = true); - void ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target); - bool IsScriptScheduled() const { return !m_scriptSchedule.empty(); } + uint32 IncreaseScheduledScriptsCount() { return (uint32)++m_scheduledScripts; } + uint32 DecreaseScheduledScriptCount() { return (uint32)--m_scheduledScripts; } + uint32 DecreaseScheduledScriptCount(size_t count) { return (uint32)(m_scheduledScripts -= count); } + bool IsScriptScheduled() const { return m_scheduledScripts > 0; } bool IsAllowedMap(uint32 mapid) { return m_forbiddenMapIds.count(mapid) == 0 ;} @@ -579,7 +599,6 @@ class World void RecordTimeDiff(const char * text, ...); protected: void _UpdateGameTime(); - void ScriptsProcess(); // callback for UpdateRealmCharacters void _UpdateRealmCharCount(QueryResult *resultCharCount, uint32 accountId); @@ -593,6 +612,9 @@ class World bool m_isClosed; + //atomic op counter for active scripts amount + ACE_Atomic_Op<ACE_Thread_Mutex, long> m_scheduledScripts; + time_t m_startTime; time_t m_gameTime; IntervalTimer m_timers[WUPDATE_COUNT]; @@ -610,8 +632,8 @@ class World DisconnectMap m_disconnects; uint32 m_maxActiveSessionCount; uint32 m_maxQueuedSessionCount; - - std::multimap<time_t, ScriptAction> m_scriptSchedule; + uint32 m_PlayerCount; + uint32 m_MaxPlayerCount; std::string m_newCharString; diff --git a/src/game/WorldSession.cpp b/src/game/WorldSession.cpp index 44bab655886..3b32eefcda7 100644 --- a/src/game/WorldSession.cpp +++ b/src/game/WorldSession.cpp @@ -53,6 +53,7 @@ m_latency(0), m_TutorialsChanged(false) { m_Address = sock->GetRemoteAddress (); sock->AddReference (); + loginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId()); } } @@ -77,11 +78,13 @@ WorldSession::~WorldSession() WorldPacket *packet = _recvQueue.next (); delete packet; } + loginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = %u;", GetAccountId()); + CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = %u;", GetAccountId()); } void WorldSession::SizeError(WorldPacket const& packet, uint32 size) const { - sLog.outError("Client (account %u) send packet %s (%u) with size %u but expected %u (attempt crash server?), skipped", + sLog.outError("Client (account %u) send packet %s (%u) with size " SIZEFMTD " but expected %u (attempt crash server?), skipped", GetAccountId(),LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size); } @@ -323,10 +326,10 @@ void WorldSession::LogoutPlayer(bool Save) } } - ///- Reset the online field in the account table - // no point resetting online in character table here as Player::SaveToDB() will set it to 1 since player has not been removed from world at this stage - //No SQL injection as AccountID is uint32 - LoginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = '%u'", GetAccountId()); + // Repop at GraveYard or other player far teleport will prevent saving player because of not present map + // Teleport player immediately for correct player save + while(_player->IsBeingTeleportedFar()) + HandleMoveWorldportAckOpcode(); ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members Guild *guild = objmgr.GetGuildById(_player->GetGuildId()); @@ -380,19 +383,13 @@ void WorldSession::LogoutPlayer(bool Save) sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), true); sSocialMgr.RemovePlayerSocial (_player->GetGUIDLow ()); - ///- Delete the player object - _player->CleanupsBeforeDelete(); // do some cleanup before deleting to prevent crash at crossreferences to already deleted data - ///- Remove the player from the world // the player may not be in the world when logging out // e.g if he got disconnected during a transfer to another map // calls to GetMap in this case may cause crashes - if(_player->IsInWorld()) _player->GetMap()->Remove(_player, false); - // RemoveFromWorld does cleanup that requires the player to be in the accessor - ObjectAccessor::Instance().RemoveObject(_player); - - delete _player; - _player = NULL; + Map* _map = _player->GetMap(); + _map->Remove(_player, true); + _player = NULL; // deleted in Remove call ///- Send the 'logout complete' packet to the client WorldPacket data( SMSG_LOGOUT_COMPLETE, 0 ); diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h index f4842c0f0bc..22ab100197c 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -711,6 +711,7 @@ class TRINITY_DLL_SPEC WorldSession void HandleCalendarGetNumPending(WorldPacket& recv_data); void HandleSpellClick(WorldPacket& recv_data); + void HandleMirrrorImageDataRequest( WorldPacket & recv_data ); void HandleAlterAppearance(WorldPacket& recv_data); void HandleRemoveGlyph(WorldPacket& recv_data); void HandleCharCustomize(WorldPacket& recv_data); diff --git a/src/game/WorldSocket.cpp b/src/game/WorldSocket.cpp index 440eeb31790..0efb0cd3d4e 100644 --- a/src/game/WorldSocket.cpp +++ b/src/game/WorldSocket.cpp @@ -678,7 +678,7 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) BigNumber K; - if (recvPacket.size () < (4 + 4 + 1 + 4 + 20)) + if (recvPacket.size () < (4 + 4 + 1 + 4 + 4 + 20)) { sLog.outError ("WorldSocket::HandleAuthSession: wrong packet size"); return -1; @@ -700,7 +700,7 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) recvPacket >> account; recvPacket >> unk3; - if (recvPacket.size () < (4 + 4 + (account.size () + 1) + 4 + 20)) + if (recvPacket.size () < (4 + 4 + (account.size () + 1) + 4 + 4 + 20)) { sLog.outError ("WorldSocket::HandleAuthSession: wrong packet size second check"); return -1; @@ -709,19 +709,20 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) recvPacket >> clientSeed; recvPacket.read (digest, 20); - DEBUG_LOG ("WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, clientseed %u", + DEBUG_LOG ("WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, unk3 %u, clientseed %u", BuiltNumberClient, unk2, account.c_str (), + unk3, clientSeed); // Get the account information from the realmd database std::string safe_account = account; // Duplicate, else will screw the SHA hash verification below - LoginDatabase.escape_string (safe_account); + loginDatabase.escape_string (safe_account); // No SQL injection, username escaped. QueryResult *result = - LoginDatabase.PQuery ("SELECT " + loginDatabase.PQuery ("SELECT " "id, " //0 "gmlevel, " //1 "sessionkey, " //2 @@ -786,7 +787,7 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) vold, vStr); - LoginDatabase.PExecute ("UPDATE account " + loginDatabase.PExecute ("UPDATE account " "SET " "v = '0', " "s = '0' " @@ -843,7 +844,7 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) // Re-check account ban (same check as in realmd) QueryResult *banresult = - LoginDatabase.PQuery ("SELECT " + loginDatabase.PQuery ("SELECT " "bandate, " "unbandate " "FROM account_banned " @@ -909,9 +910,9 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) // Update the last_ip in the database // No SQL injection, username escaped. - LoginDatabase.escape_string (address); + loginDatabase.escape_string (address); - LoginDatabase.PExecute ("UPDATE account " + loginDatabase.PExecute ("UPDATE account " "SET last_ip = '%s' " "WHERE username = '%s'", address.c_str (), diff --git a/src/game/pchdef.h b/src/game/pchdef.h index 4b5b9dfbe3e..7252e980e7d 100644 --- a/src/game/pchdef.h +++ b/src/game/pchdef.h @@ -9,8 +9,4 @@ #include "Database/SQLStorage.h" #include "Opcodes.h" #include "SharedDefines.h" - -#ifdef FASTBUILD -//add additional headers here to speed up compilation in release builds even more #include "ObjectMgr.h" -#endif
\ No newline at end of file |
